I've ignored backtesting in Amibroker for years because I trade on a discretionary basis. But I thought, what the heck, see how far I can get by trying to automate a lot of what I do.
I made up a completely bogus backtest script just to make sure I'm setting the whole thing up correctly for the much more complicated logic to follow when I implement my strategies. I'm running on a 4-core 3.8Ghz Xeon processor, with 32Gb of memory, SSD drive.
Here's what the test script does:
Only trade between 9:30 AM and 15:45 PM (EST) on a 1 min timeframe
Buy at the close of the 9:35 bar
Sell at the close of the 9:55 bar
That's it. I setup up the backtest to run for August 20, 2021, long only and in futures mode as I'm testing this on the NQU1-GLOBEX-FUT.
Here's the code. I run Backtest and it says "100% complete" almost instantaneously and then there's this wait of 35 seconds before a report is generated.
I must be doing something wrong because 35 secs is a relative eternity for looking at 1 day on a 1 min chart with logic like this and a processor this fast.
Thanks for the help.
SetOption("FuturesMode", True);
PositionSize = MarginDeposit = 1;
TickSize = 0.25;
PointValue = 20;
function IsTimeInRange(i)
{
local ret, tn;
ret = 0;
tn = TimeNum();
if (tn[i] >= 93000 && tn[i] <= 154500)
ret = 1;
return ret;
}
function IsTime(i, t)
{
local ret, tn;
ret = 0;
tn = TimeNum();
if (tn[i] == t)
ret = 1;
return ret;
}
position = 0;
Buy = Sell = 0;
for (i= 0; i < BarCount; i++)
{
if (IsTimeInRange(i))
{
if (position == 0)
{
if (IsTime(i, 93500))
{
Buy[i] = 1;
position = 1;
}
}
else if (position == 1)
{
if (IsTime(i, 95500))
{
Sell[i] = 1;
position = 0;
}
}
}
}
Unfortunately your question isn't clear enough and does not provide all necessary details to give you an answer. Please follow this advice: How to ask a good question
You need to read provide timings from "Info" tab as explained here:
Few guesses (uninformed):
Monte Carlo simulation is turned on on your machine and it must be done before report is generated
Your formula is unnecessarily complex for such basic stuff (no loops are needed). It would be enough to just write:
Here's the corrected template script [Monte Carlo is turned off]:
SetOption("FuturesMode", True);
PositionSize = MarginDeposit = 1;
TickSize = 0.25;
PointValue = 20;
tn = TimeNum();
position = 0;
Buy = Sell = 0;
for (i= 0; i < BarCount; i++)
{
if (tn[i] >= 93000 && tn[i] <= 160000)
{
if (position == 0)
{
if (tn[i] == 93500)
{
Buy[i] = 1;
position = 1;
}
}
else if (position == 1)
{
if (tn[i] == 95500)
{
Sell[i] = 1;
position = 0;
}
}
}
}
[As fxshrat pointed out]
The "time waster" was in missing that "tn = TimeNum()" is loop invariant so it should be hoisted up out of the BarCount loop. Now the backtest is virtually instantaneous, as it should be for something like this.
My real auto daytrading attempt is going to try and mimmick what I do in discretionary trading. It's more of a dynamic bar-by-bar finite state machine, completely unrelated to a specific time to actually enter/exit the market. That was just a placeholder trade to make 1 trade within the time boundaries to present the problem. Unfortunately, there will be no super-fast one-liner array formulas for that kind of real-time analysis.
Tomasz,
If you're still reading this, on a change of subject but still relating to general computing performance, have you taken a look at the J/K/Q programming languages? I'm following the shatki (aka K9) forum in Google Groups (basically, a rebranded Usenet service) and learned from there and the publicly available K clones coded in C that one of the techniques Arthur Whitney uses to make it so blazingly fast when working with files and huge amounts of data in memory is to use mmap() for all of his memory and file allocations. Is Amibroker doing something like this for enhanced performance? Just curious. Thanks.
I don't know what you are doing, but on my end this backtest takes less than half of the second.
C is the fastest high-level language out there. It is BARE METAL. Except hand-optimized assembler nothing is faster. That is one of the reasons why embedded systems (microcontrollers) with limited CPU power are programmed in C (previously, in the 80s, they were programmed directly in assembler).
BTW: you still did not show what I asked in first response, the timings from "Info" tab.
I checked and it IS instantenous, 10 day intraday backtest 1-minute data with Monte Carlo simulation with YOUR formula takes 0.46 second:
The first script I posted takes around 35 seconds to complete for 1 day of NQ 1 min data. The second script I posted takes around 0.19 seconds. These numbers come from the Info Window.
I made myself as clear as I can. The change was to hoist the loop invariant statement "tn = TimeNum();" as fxshrat pointed out. Now, if you can run that first script in less than 1 second, I have no idea why it's taking 35 secs for me then vs. the 2nd script.
@fxshrat explained you why. Your code was the culprit. If you are calling array functions (that internally process entire array of barcount bars) inside a loop that goes again thru all bars thousand of times instead of just one time outside the loop, your code executes the code BarCount* BarCount times instead of just one BarCount go. Your formula was the culprit. Array functions should be OUTSIDE of barcount loops.
Should you use Tools->Code Check And Profile in AFL Editor you would find out that TimeNum() function is called thousands of times while it should be called just once.
Sometimes one needs to re-read it a couple of times until "aha" moment comes and everything suddenly makes sense. Changing the way of thinking from sequential (bar by bar) to parallel (vector based) is crucial. It is like going from 2 dimensions to 3 dimensions.