I am running backtests in the Analysis window with filter set to a watchlist. Amibroker should launch each ticker in the watchlist in its own thread. For now, the watchlist only has two members. The goal is for each member to launch in different threads, and for one member to wait for the other to finish before it finishes. Eventually the watchlist will hold many more symbols, but I am starting out with these two as proof-of-concept. I need to process with a pause like this because there is an implicit tree structure among the watchlist members, and higher nodes need to wait for lower nodes to process before finishing their own processing.
I've been looking at this on my own for three nights with no luck fixing it. As a last resort, I'm posting here to see if there's something else about Amibroker I don't know but should be looking at or for any other ideas about how to accomplish this.
The problem is the initial thread is launching and then (correctly) pausing to wait to hear from the other thread, but the second thread doesn't launch. I assume the problem is I have if (Status("stocknum") == 0) somewhere in the code, so Amibroker is trying to finish the first thread before launching others, but for the life of me I can't find it in my code. I've used grepWin to look through all files and am not coming up with anything.
Here's what I've written to accomplish the goal. I've written critical section code using the code in the Amibroker guide, to ensure static variables get set to the appropriate state at the beginning of the first thread. The code is as follows:
function _TryEnterCS( secname, maxTime ) {
// Functions from AB documentation for creating a critical region which allows only
// one thread at a time to enter it.
//
// secname - Name of a static variable to use for the critical region
// maxTime - The maximum number of milliseconds a thread will try to enter the
// critical region before failing.
//
// These functions
global _cursec;
_cursec= "";
// try obtaining semaphore for 1000 ms
for( i = 0; i < maxTime; i++ )
if( StaticVarCompareExchange( secname, 1, 0 ) == 0 ) {
_cursec = secname;
break;
}
else ThreadSleep( 1 ); //sleep one millisecond
return _cursec != "";
} // end _TryEnterCS
// call it ONLY when _TryEnterCS returned TRUE !
function _LeaveCS() {
global _cursec;
if( _cursec != "" )
{
StaticVarSet( _cursec, 0 );
_cursec = "";
}
} // end _LeaveCS()
// Section 1: Clean static variables from memory
// Most of these are used in AllocDownward() inside the DLL,
// which is called by the tree processing section of this file.
//
// This code is run inside
_TRACE("Lauching thread with name " + Name());
if (_TryEnterCS("firstThreadProcessing", 100) ) {
// Begin critical section processing
temp = Nz(StaticVarGet("firstThread"));
_TRACE("Status is " + Status("ActionEx"));
if (AlmostEqual(temp, 0)) {
StaticVarSet("firstThread", ++temp);
_TRACE(Name() + " thread, and firstThread param is " + temp);
StaticVarRemove("*Sizing");
StaticVarRemove("*PositionScore");
StaticVarRemove("*alloc");
StaticVarRemove("*reset");
StaticVarRemove("*replace");
/**/ StaticVarRemove("~Min*");
StaticVarSet("~SPLTYREITSPM" + "updated", False);
StaticVarSet("~CREDIVERSEREITSPM" + "updated", False);
StaticVarSet("~LBBRKOUTREITDIVCOMMEM" + "updated", False);
} // end if this is the first thread
_TRACE("Leaving first thread critical section.");
_LeaveCS();
} // End enter critical section to cleanup/set static vars
I then clean up the thread counter variable after all the threads are finished using,
if (Status("ActionEx") == actionPortfolio)
StaticVarRemove("firstThread");
As the end of each thread's processing, there is a StaticVarSet( name() + "updated", true) call to communicate to other threads that it is done.
Each symbol has a configuration file with the names of other symbols it needs to wait on, and this is used in a pause function as follows,
function PauseForUpdates(symList) {
for (i = 0; (symbol = StrExtract(symList, i)) != ""; ++i) {
_TRACE("Waiting for update on symbol " + symbol);
while (IsCompositeName(symbol) AND NOT StaticVarGet(symbol + "updated")) {
ThreadSleep(10);
} // end while
} // end for
} // End function PauseForUpdates
In general, I've tested this in a baby environment and it works as long as the symbols are getting launched in threads and processing. In context, it's not working because with the live code Amibroker launches the first thread and just sits in the pause, never launching the second thread. Therefore, it never gets a message that the ticker for the second thread is updated and just hangs.
I understand from the Amibroker documentation that this is normal behavior if there is a call to Status("stocknum") == 0 to allow for pre-processing. I've done my best to get these out of my code, though I've done this by commenting them out rather than deleting them.
I'm wondering, is there anything else which would trigger this behavior in Amibroker, which I'm not aware of? Is it enough to hide those "stocknum" calls inside comments or should I delete them? Is there another, better way to accomplish what I'm trying to do?
Thanks for any help.