Multiple Time Frame support in AFL
Release 4.41 brings ability to use multiple time frames (bar intervals) in single formula. The time frame functions can be divided into 3 functional groups:
switching time frame of build-in O, H, L, C, V, OI, Avg arrays: TimeFrameSet, TimeFrameRestore
compressing/expanding single arrays to/from specified interval: TimeFrameCompress, TimeFrameExpand
immediate access to price/volume arrays in different time frame: TimeFrameGetPrice
First group is used when your formula needs to perform some calculations on indicators in different time frame than currently selected one. For example if you need to calculate 13-bar moving average on 5 minute data and 9 bar exponential avarage from hourly data while current interval is 1 minute you would write:
TimeFrameSet( in5Minute ); // switch to 5 minute frame
/* MA now operates on 5 minute data, ma5_13 holds time-compressed 13 bar MA of 5min bars */
ma5_13 = MA( C, 13 );
TimeFrameRestore(); // restore time frame to original
TimeFrameSet( inHourly ); // switch now to hourly
mah_9 = EMA( C, 9 ); // 9 bar moving average from hourly data
TimeFrameRestore(); // restore time frame to original
Plot( Close, "Price", colorWhite, styleCandle );
// plot expanded average
Plot( TimeFrameExpand( ma5_13, in5Minute), "13 bar moving average from 5 min bars", colorRed );
Plot( TimeFrameExpand( mah_9, inHourly), "9 bar moving average from hourly bars", colorRed );
TimeFrameSet( interval ) - replaces current built-in price/volume arrays: open, high, low, close, volume, openint, avg with time-compressed bars of specified interval once you switched to a different time frame all calculations and built-in indicators operate on selected time frame. To get back to original interval call TimeFrameRestore() funciton. If you want to call TimeFrameSet again with different interval you have to restore original time frame first using TimeFrameRestore(). Interval is time frame interval in seconds. For example: 60 is one minute bar. You should use convenient constants for common intervals: in1Minute, in5Minute, in15Minute, inHourly, inDaily, inWeekly, inMonthly.
With version 4.70 you can also specify N-tick intervals. This is done by passing NEGATIVE value as interval. For example -5 will give 5-tick bar compression, and -133 will give 133-tick compression. Please note that using N-tick intervals works only if your database uses Tick base time interval set in File -> Database Settings dialog.
TimeFrameSet( -133 ); // switch to 133-tick interval
TimeFrameRestore() - restores price arrays replaced by SetTimeFrame.Note that only OHLC, V, OI and Avg built-in variables are restored to original time frame when you call TimeFrameRestore(). All other variables created when being in different time frame remain compressed. To de-compress them to original interval you have to use TimeFrameExpand.
Once you switch the time frame using TimeFrameSet, all AFL functions operate on this time frame until you switch back the time frame to original interval using TimeFrameRestore or set to different interval again using TimeFrameSet. It is good idea to ALWAYS call TimeFrameRestore when you are done with processing in other time frames.
When time frame is switched to other than original interval the results of all functions called since TimeFrameSet are time-compressed too. If you want to display them in original time frame you would need to 'expand' them as described later. Variables created and assigned before call to TimeFrameSet() remain in the time frame they were created. This behaviour allows mixing unlimited different time frames in single formula.
Please note that you can only compress data from shorter interval to longer interval. So when working with 1-minute data you can compress to 2, 3, 4, 5, 6, ....N-minute data. But when working with 15 minute data you can not get 1-minute data bars. In a similar way if you have only EOD data you can not access intraday time frames.
Second group: TimeFrameCompress/TimeFrameExpand allow to compress and expand single arrays to / from different time frames. Especially worth mentioning is TimeFrameExpand that is used to decompress array variables that were created in different time frame. Decompressing is required to properly display the array created in different time frame. For example if you want to display weekly moving average it must be 'expanded' so the data of one weekly bar covers five daily bars (Monday-Friday) of corresponding week.
TimeFrameExpand( array, interval, mode = expandLast ) - expands time-compressed array from 'interval' time frame to base time frame ('interval' must match the value used in TimeFrameCompress or TimeFrameSet)
Available modes:
expandLast - the compressed value is expanded starting from last bar within given period (so for example weekly close/high/low is available on Friday's bar)
expandFirst - the compressed value is expanded starting from first bar within given period (so for example weekly open is available from Monday's bar)
expandPoint - the resulting array gets not empty values only for the last bar within given period (all remaining bars are Null (empty)).
Caveat: expandFirst used on price different than open may look into the future. For example if you create weekly HIGH series, expanding it to daily interval using expandFirst will enable you to know on MONDAY what was the high for entire week.
TimeFrameCompress is provided for completeness and it can be used when you want to compress single array without affecting built-in OHLC,V arrays. If you call TimeFrameCompress it does not affect results of other functions.
wc = TimeFrameCompress( Close, inWeekly );
/* now the time frame is still unchanged (say daily) and our MA will operate on daily data */
dailyma = MA( C, 14 );
/* but if we call MA on compressed array, it will give MA from other time frame */
weeklyma = MA( wc, 14 ); // note that argument is time-compressed array
Plot( dailyma, "DailyMA", colorRed );
weeklyma = TimeFrameExpand( weeklyma, inWeekly ); // expand for display
Plot( weeklyma, "WeeklyMA", colorBlue );
During this formula the time frame remained at original setting we only compressed single array.
TimeFrameCompress( array, interval, mode = compressLast )
- compresses single array to given interval using given compression mode available modes:
compressLast - last (close) value of the array within interval
compressOpen - open value of the array within interval
compressHigh - highest value of the array within interval
compressLow - lowest value of the array within interval
compressVolume - sum of values of the array within interval
Graph0 = TimeFrameExpand( TimeFrameCompress( Close, inWeekly, compressLast ), inWeekly, expandLast );
Graph1 = TimeFrameExpand( TimeFrameCompress( Open, inWeekly, compressOpen ), inWeekly, expandFirst );
Third group consist of just one useful function: TimeFrameGetPrice which allows to reference price and volume from other time frames without switching /compressing/expanding time frames. Just one function call to retrieve price from higher time frame. It allows also to reference not only current but past bars from different time frames.
TimeFrameGetPrice( pricefield, interval, shift = 0, mode = expandFirst );
- references OHLCV fields from other time frames. This works immediatelly without need to call TimeFrameSet at all.
Price field is one of the following: "O", "H", "L", "C", "V", "I" (open interest). Interval is bar interval in seconds. shift allows to reference past (negative values) and future (positive values) data in higher time frame. For example -1 gives previous bar's data (like in Ref function but this works in higher time frame).
Examples:
TimeFrameGetPrice( "O", inWeekly, -1 ) // gives you previous week Open price
TimeFrameGetPrice( "C", inWeekly, -3 ) // gives you weekly Close price 3 weeks ago
TimeFrameGetPrice( "H", inWeekly, -2 ) // gives you weekly High price 2 weeks ago
TimeFrameGetPrice( "O", inWeekly, 0 ) // gives you this week Open price.
TimeFrameGetPrice( "H", inDaily, -1 ) // gives previous Day High when working on intraday data
Shift works as in Ref() function but it is applied to compressed time frame.
Note these functions work like these 3 nested functions
TimeFrameExpand( Ref( TimeFrameCompress( array, interval, compress(depending on field used) ), shift ), interval, expandFirst )
therefore if shift = 0 compressed data may look into the future ( weekly high can be known on monday ). If you want to write a trading system using this function please make sure to reference PAST data by using negative shift value.
How does it work internally ?
Time-frame functions do not change the BarCount - they just squeeze the arrays so you have first N-bars filled with NULL values and then - last part of the array contains the actual time-compressed values. This is why it is essential to expand the data back to the original frame with TimeFrameExpand.