July 21, 2007
Real-Time Delays
In real-time trading many situations arise when you want to delay action until a specific criterion is met. In AmiBroker you can base your delays in many different ways, the only requirement being that the delay variable increments or decrements. If the selected variable doesn’t revert towards your timeout value (target), your delay function would never time out. In this case you would have to add code to handle that condition. A few variables you might use are:
- RT TimeNumber (Now(4)).
- Elapsed Seconds (redrawaction).
- Real-time Data time-stamp (TimeNum()).
- Tick-count (New Data).
- Volume (Change in Volume).
- Price change (Change in Price).
- Chart Refresh (any AFL Execution).
- Indicator values.
Which of the above variables you would use for your delay depends on the requirements of your trading system. There may be times when you may need to combine several methods to get the required results. For example, if a delay were based on the data-timestamp, it might not time out during a data dropout or a no-trading period. In this case you need to back up your data-timestamp delay with a real-time (seconds) delay.
Delays play a critical role in real-time system design. For example, in real-time systems, signals may have a short lifetime. The signal is strongest when it triggers and than quickly decays until, perhaps after a few bars, it has lost all significance. Letting the order fill at the time when it has lost significance is pure gambling. To prevent this, you can cancel the order after a delay, or decrease the position size proportional to the perceived decay in signal strength (perhaps based on elapsed bars?)
Since in a real-time system the time-lapse between AFL executions can be significant, you should place your LMT price calculations ahead of the ordering code. Calculating the LMT price after the order has been placed postpones order placement until the next AFL execution occurs, i.e., when the next quote arrives; by then, the price has probably changed. Especially during periods of low volume, this could be significant. When these delays would be insignificant in EOD systems, they could make-or-break your system in fast-trading systems.
To ensure frequent AFL execution in the absence of quotes, you can place a RequestTimedRefresh(1) statement at the top of your code, where the variable ‘1′ refers to a 1-second refresh. This guarantees an AFL execution at least once per second.
If your code is lengthy and takes a significant amount of time to execute, you may have to check order status at several places in your code. If changed status demands immediate action, you can force an immediate AFL execution by calling the following function:
1 2 3 4 5 | function RefreshAll() { oAB = CreateObject("Broker.Application"); oAB.RefreshAll(); } |
The highest rate that you can call this function is once per second.
Due to the lack of chart resolution and the static character of historical data in longer time frames (minutes and higher), you may only be able to optimize delays during live simulation or real trading. This is because only the last bar will show live changes.
If delay functions are used in different programs, they must use unique static variable names in each program. See the post on Keying Static Variables for more information on this.
The code below introduces a DelayManager() that can be used to implement most delay functions. The delay function uses the following variables:
1) DelayName is needed to allow its use for different delays.
2) DelayValue is used to set the DelayTarget, if set to zero the function returns the DelayRemaining.
3) DelayReference is the variable that is sampled when the delay is initialized.
4) DelayTarget is calculated as (DelayReference at initialization) + DelayValue.
5) DelayRemaining is the distance to the DelayTarget.
In your final system formula, you may want to use an in-line equivalent for better performance. The code below lists multiple choices for the DelayReference; you should comment out the ones not used or place the one to use in the last position.
When testing this code you can observe the DelayValue being initialized, and counting down to timeout, in the Title.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function getTickCount() { LastVol = LastValue(Volume); PrevVol = Nz(StaticVarGet("PrevVol")); NewTick = PrevVol != LastVol; if( NewTick ) { TickCount = Nz(StaticVarGet("TickCounter")); StaticVarSet("TickCounter",++TickCount); StaticVarSet("PrevVol",LastVol); } TickCount = StaticVarGet("TickCounter"); return TickCount; } |
1 2 3 4 5 6 | function getRefreshCount() { RefreshCount = Nz(StaticVarGet("RefreshCounter")); StaticVarSet("RefreshCounter",++RefreshCount); return RefreshCount; } |
1 2 3 4 5 6 7 8 9 | function getElapsedSeconds() { if( Status("redrawaction") ) { ElapsedSeconds = Nz(StaticVarGet("ElapsedSeconds")); StaticVarSet("ElapsedSeconds",++ElapsedSeconds); } return Nz(StaticVarGet("ElapsedSeconds")); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | function DelayManager( DelayName, DelayValue, DelayReference ) { if(DelayValue == 0 ) { DelayTarget = Nz(StaticVarGet(DelayName)); DelayRemaining = Max( 0, DelayTarget - DelayReference); } else { DelayTarget = DelayReference + DelayValue; StaticVarSet(DelayName, DelayTarget ); DelayRemaining = DelayValue; } return DelayRemaining; } function GetSysSecondNum() { Time = Now(4); Seconds = int(Time%100); Minutes = int(Time/100%100); Hours = int(Time/10000%100); SecondNum= int(Hours*60*60+Minutes*60+Seconds); return SecondNum; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | RequestTimedRefresh(1); DelayName = ParamStr("DelayName","TestDelayName"); DelayValue = Param("Delay Value", 10,1,200,1); DelayTrigger = ParamTrigger("Initiate Delay","INIT"); ResetHRPC = ParamTrigger("Reset Performance Counter", "RESET HRPC"); if( ResetHRPC ) GetPerformanceCounter( True ); NewDay = DateNum() != Ref(DateNum(),-1); // Some of the many delay references you can use // The last defeinition for DelayReference is used DelayReference = TimeNum(); DelayReference = MA(C,10); DelayReference = C; DelayReference = Sum(V,BarsSince(NewDay)); // DailyVolume DelayReference = getTickCount(); DelayReference = getRefreshCount(); DelayReference = getElapsedSeconds(); DelayReference = GetSysSecondNum(); if( DelayTrigger ) DelayManager(DelayName, DelayValue, DelayReference ); else DelayManager(DelayName, 0, DelayReference ); DelayToGo = DelayManager( DelayName, 0, DelayReference ); Title = "\nDelay:"+NumToStr(DelayToGo,1.0); |
Edited by Al Venosa
Filed by Herman at 5:12 pm under Real-Time AFL Programming



(2 votes, average: 3.5 out of 5)
It may also be possible to rely on order attributes within TWS to leave an order open only till a certain date/time. I know its possible to do this manually with order tickets in TWS, but not sure if it would be possible programmatically via AB/IBc.
JD
Starting with v1.0.8 the TimeInForce field in PlaceOrder/ModifyOrder accepts GTD and GAT specifications:
http://www.amibroker.com/at/
Best regards,
Herman