TRADERS’ TIPS
For this month’s Traders’ Tips, the focus is Markos Katsanos’ article in this issue, “Detecting Flags In Intraday Charts.” Here we present the December 2014 Traders’ Tips code with possible implementations in various software.
Code for TradeStation was already provided in Katsanos’ article by the author. S&C subscribers will find that code at the Subscriber Area of our website, www.traders.com. Presented here is an overview of some possible implementations for other software as well.
Traders’ Tips code is provided to help the reader implement a selected technique from an article in this issue or another recent issue. The entries here are contributed by various software developers or programmers for software that is capable of customization.
In “Detecting Flags In Intraday Charts” in this issue, author Markos Katsanos describes a method for identifying chart patterns known as flags on intraday charts. The author provides some TradeStation strategy code for a trading system based on his rules. Here, we are providing some additional EasyLanguage code for TradeStation for an indicator based on the same rules. The indicator can be used with a chart as well as with the TradeStation Scanner to search your symbol list of stocks.
To download this EasyLanguage code, please visit our TradeStation and EasyLanguage support forum. The code can be found here: https://www.tradestation.com/TASC-2014. The ELD filename is “_TASC_DEC2014_INTRADAYFLAGS.ELD”.
The code is also shown here:
IntradayFlags (Indicator) { TASC December 2014 Detecting Flags in Intraday Charts by Markos Katsanos Indicator } inputs: MAXFLDUR( 15 ), //Max Flag Duration FLAGMIN( 2.5 ), // Max ATR in lowest point in flag PX( 23 ), //Max Pole Duration. UPT1BARS( 70 ), // Bars for Uptrend leading to flag POLEMIN( 5.5 ), //Min ATR Height of the pole ATRmin( 5 ) ;// Min volatility change variables: X1( 0 ), X2( 0 ), LRSX2( 0 ), LRSX1( 0 ), POLE( 0 ), TOP( 0 ), BOTTOM( 0), Y23( 0 ), FLAGBOT( 0 ), UPT1( 0 ), LF( 0 ), TARGETPER( 0 ) ; // FLAG CALCULATION //Flag duration ex pole top X1 = HighestBar( Close, MAXFLDUR )[2] ; //Flag duration including pole top X2 = X1 + 1 ; LF = Lowest( Close, X2 ) ; TOP = Highest( Close, X2 )[2] ; X2=Iff( LinearRegSlope( Close, X1 )[1] < 0 and TOP - LF < flagmin * AvgTrueRange( 40 ), X1 + 1, 100 ) ; if X2 > 2 and X2 <= MAXFLDUR then begin //Limits flag duration Y23=LowestBar( Close, PX + X2 ) ; // Pole bottom bar BOTTOM=Lowest( Close, PX+X2 ) ;// Pole bottom POLE = TOP - BOTTOM; IF TOP - BOTTOM > POLEMIN * AvgTrueRange( 40 ) and Y23 > X2 then begin TOP = Highest( Close, X2 )[2] ; FLAGBOT = Lowest( Close, X2 ) ; //Uptrend leading to flag UPT1 = BOTTOM - Lowest( Low, UPT1BARS ) ; //Slope in flag LRSX1 = LinearRegSlope( Close, X1 ) * 100 ; //Slope in flag before breakout LRSX2 = LinearRegSlope( Close, X1-1 )[2] * 100 ; //Limits flag slope between 0 and -3 ATR Condition1 = TOP-LF < flagmin * AvgTrueRange( 40 ) and ( LRSX1 < 0 or LRSX2 < 0 ) ; //Limits min pole height Condition2 = POLE > POLEMIN * AvgTrueRange( 40 ) ; //Uptrend leading to flag Condition3 = UPT1 > 0 ; //Volatility Condition5 = ( AvgTrueRange( 40 ) / AvgTrueRange( 40 )[Y23] - 1 ) * 100 > ATRmin ; If Condition1 and Condition2 and Condition3 and Condition5 then begin Plot1( High, "Flag" ) ; Alert ; end ; end ; end ; IntradayFlags (Strategy) { TASC December 2014 Detecting Flags in Intraday Charts by Markos Katsanos Strategy } inputs: MAXFLDUR( 15 ), //Max Flag Duration FLAGMIN( 2.5 ), // Max ATR in lowest point in flag PX( 23 ), //Max Pole Duration. UPT1BARS( 70 ), // Bars for Uptrend leading to flag POLEMIN( 5.5 ), //Min ATR Height of the pole LBF( 50 ), // Min distance between flags ATRmin( 5 ),// Min volatility change K( 1.2 ), //Profit Target constant timeexit( 100 ), //Time exit bars ATRLL( 3 ), BSEMIN( 5 ), // Stop loss below flag ATRTRAIL( 3 ), TRAILBARS( 5 ), // Trailing stop parameters BSEINACT( 70 ), ATRINACT( 4 ) ; // Inactivity exit parameter variables: X1( 0 ), X2( 0 ), LRSX2( 0 ), LRSX1( 0 ), POLE( 0 ), ptarget( 0 ), BSE( 0 ), TOP( 0 ), BOTTOM( 0), X3( 0 ), L3( 0 ), Y23( 0 ), FLAGBOT( 0 ), UPT1( 0 ), LF( 0 ), TARGETPER( 0 ), MP( 0 ) ; MP = MarketPosition ; BSE = BarsSinceEntry ; if MP <> 0 and MP[1] <> MP then BSE = 0 else if MP <> 0 then BSE += 1 else if MP = 0 then BSE = 0 ; // FLAG CALCULATION //Flag duration ex pole top X1 = HighestBar( Close, MAXFLDUR )[2] ; //Flag duration including pole top X2 = X1 + 1 ; LF = Lowest( Close, X2 ) ; TOP = Highest( Close, X2 )[2] ; X2=Iff( LinearRegSlope( Close, X1 )[1] < 0 and TOP - LF < flagmin * AvgTrueRange( 40 ), X1 + 1, 100 ) ; if X2 > 2 and X2 <= MAXFLDUR then begin //Limits flag duration Y23=LowestBar( Close, PX + X2 ) ; // Pole bottom bar BOTTOM=Lowest( Close, PX+X2 ) ;// Pole bottom POLE = TOP - BOTTOM; IF TOP - BOTTOM > POLEMIN * AvgTrueRange( 40 ) and Y23 > X2 then begin TOP = Highest( Close, X2 )[2] ; FLAGBOT = Lowest( Close, X2 ) ; //Uptrend leading to flag UPT1 = BOTTOM - Lowest( Low, UPT1BARS ) ; //Slope in flag LRSX1 = LinearRegSlope( Close, X1 ) * 100 ; //Slope in flag before breakout LRSX2 = LinearRegSlope( Close, X1-1 )[2] * 100 ; //Limits flag slope between 0 and -3 ATR Condition1 = TOP-LF < flagmin * AvgTrueRange( 40 ) and ( LRSX1 < 0 or LRSX2 < 0 ) ; //Limits min pole height Condition2 = POLE > POLEMIN * AvgTrueRange( 40 ) ; //Uptrend leading to flag Condition3 = UPT1 > 0 ; //Limits distance between successive flags Condition4 = (BarsSinceExit( 1 ) = 0 or barssinceexit( 1 ) > LBF ) ; //Volatility Condition5 = ( AvgTrueRange( 40 ) / AvgTrueRange( 40 )[Y23] - 1 ) * 100 > ATRmin ; If MP = 0 and Condition1 and Condition2 and Condition3 and Condition4 and Condition5 then begin Buy( "Flag" ) next bar at Highest( Close, X1 ) Stop ; end ; end ; end ; {EXIT CONDITIONS} if MP = 1 then begin X3 = HighestBar( Close, MAXFLDUR )[BSE + 2] - BSE + 1 ; TOP = Highest( Close, X3 )[BSE + 1] ; BOTTOM = Lowest( Close, ( PX + X3 ) )[BSE + 1]; POLE = ( TOP - BOTTOM ) / ( BOTTOM + .0001 ) * 100 ; targetPER = K * POLE ; ptarget = ( 1 + TARGETPER / 100 ) * EntryPrice ; L3 = Lowest( Low, X3 )[BSE] ; //Profit target if Close >= ptarget then Sell ( "pTARGET" ) this bar on Close; //Stop if BSE > BSEMIN then Sell ( "UNDER FLAG" ) next bar at L3 - ATRLL * AvgTrueRange( 40 ) Stop ; //Trailing stop if Close < Highest( Close, TRAILBARS ) - ATRTRAIL * AvgTrueRange( 40 )then Sell ( "TRAIL" ) next bar at Market ; //Inactivity exit if BSE > BSEINACT and Close < EntryPrice + ATRINACT * AvgTrueRange( 40 ) then Sell ( "INACTIVITY" ) next bar at Market ; end ; //Time exit if BSE > timeexit then Sell ( "TIME" ) next bar at Market ;
For more information about EasyLanguage coding in general, please see https://www.tradestation.com/EL-FAQ.
A sample chart is shown in Figure 1.
FIGURE 1: TRADESTATION, SAMPLE SCANNER RESULTS. Here is a list of some sample results from the TradeStation Scanner along with the indicator and strategy based on Katsanos’ article, applied to an intraday chart of Cantel Medical Corp. (CMN).
This article is for informational purposes. No type of trading or investment recommendation, advice, or strategy is being made, given, or in any manner provided by TradeStation Securities or its affiliates.
For this month’s Traders’ Tip, we’ve provided the formula IntradayFlagStrategy.efs based on the formula given in Markos Katsanos’ article in this issue, “Detecting Flags In Intraday Charts.”
The study contains formula parameters that may be configured through the edit chart window (right-click on the chart and select “edit chart”). A sample chart is shown in Figure 2.
FIGURE 2: eSIGNAL. Here is an example of the flag study based on Markos Katsanos’ article in this issue implemented on a chart of Priceline Group (PCLN).
To discuss this study or download a complete copy of the formula code, please visit the EFS Library Discussion Board forum under the forums link from the support menu at www.esignal.com or visit our EFS KnowledgeBase at https://www.esignal.com/support/kb/efs/. The eSignal formula script (EFS) is also available for copying & pasting below, or downloading here.
/********************************* Provided By: Interactive Data Corporation (Copyright © 2014) All rights reserved. This sample eSignal Formula Script (EFS) is for educational purposes only. Interactive Data Corporation reserves the right to modify and overwrite this EFS file with each new release. Description: Detecting Flags In Intraday Charts by Markos Katsanos Version: 1.00 10/07/2014 Notes: The related article is copyrighted material. If you are not a subscriber of Stocks & Commodities, please visit www.traders.com. **********************************/ var fpArray = new Array(); function preMain(){ setStudyTitle("IntradayFlagStrategy"); setPriceStudy(true); var x = 0; fpArray[x] = new FunctionParameter("fpMaxFlDur", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Max Flag Duration"); setDefault(15); }; fpArray[x] = new FunctionParameter("fpFlagMin", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Max ATR in lowest point in flag"); setDefault(2.5); }; fpArray[x] = new FunctionParameter("fpPX", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Max Pole Duration"); setDefault(23); }; fpArray[x] = new FunctionParameter("fpUptBars", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Bars for Uptrend leading to flag"); setDefault(70); }; fpArray[x] = new FunctionParameter("fpPoleMin", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Min ATR Height of the pole"); setDefault(5.5); }; fpArray[x] = new FunctionParameter("fpDistBFlags", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Min distance between flags"); setDefault(50); }; fpArray[x] = new FunctionParameter("fpATRMin", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Min volatility change"); setDefault(5); }; fpArray[x] = new FunctionParameter("fpK", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Profit Target constant (K)"); setDefault(1.2); }; fpArray[x] = new FunctionParameter("fpTimeExit", FunctionParameter.NUMBER); with(fpArray[x++]){ setName("Time exit bars"); setDefault(100); }; fpArray[x] = new FunctionParameter("fpCap1", FunctionParameter.STRING); with(fpArray[x++]){ setName("Stop loss below flag parameters:"); }; fpArray[x] = new FunctionParameter("fpATRLL", FunctionParameter.NUMBER); with(fpArray[x++]){ setName(" ATRLL"); setDefault(3); }; fpArray[x] = new FunctionParameter("fpBSEMIN", FunctionParameter.NUMBER); with(fpArray[x++]){ setName(" BSEMIN"); setDefault(5); }; fpArray[x] = new FunctionParameter("fpCap2", FunctionParameter.STRING); with(fpArray[x++]){ setName("Trailing stop parameters:"); }; fpArray[x] = new FunctionParameter("fpATRTRAIL", FunctionParameter.NUMBER); with(fpArray[x++]){ setName(" ATRTRAIL"); setDefault(3); }; fpArray[x] = new FunctionParameter("fpTRAILBARS", FunctionParameter.NUMBER); with(fpArray[x++]){ setName(" TRAILBARS"); setDefault(5); }; fpArray[x] = new FunctionParameter("fpCap3", FunctionParameter.STRING); with(fpArray[x++]){ setName("Inactivity exit parameter:"); }; fpArray[x] = new FunctionParameter("fpBSEINACT", FunctionParameter.NUMBER); with(fpArray[x++]){ setName(" BSEINACT"); setDefault(70); }; fpArray[x] = new FunctionParameter("fpATRINACT", FunctionParameter.NUMBER); with(fpArray[x++]){ setName(" ATRINACT"); setDefault(4); }; fpArray[x] = new FunctionParameter("fpLongColor", FunctionParameter.COLOR); with(fpArray[x++]){ setName("Enter Position Color"); setDefault(Color.lime); }; fpArray[x] = new FunctionParameter("fpShortColor", FunctionParameter.COLOR); with(fpArray[x++]){ setName("Exit Position Color"); setDefault(Color.red); }; } setComputeOnClose(); var bInit = false; var bVersion = null; var xClose = null; var xLow = null; var xHighestIndexBar = null; var xTrueRange = null; var xATR = null; var xLowestUPT = null; var xHighestTrail = null; var BarSinseEntry = null; var BarSinceExit = 0; var EntryPrice = null; var prLinearRegSlopeX2 = null; function main(fpMaxFlDur, fpFlagMin, fpPX, fpUptBars, fpPoleMin, fpDistBFlags, fpATRMin, fpK, fpTimeExit, fpATRLL, fpBSEMIN, fpATRTRAIL, fpTRAILBARS, fpBSEINACT, fpATRINACT, fpLongColor, fpShortColor){ if (bVersion == null) bVersion = verify(); if (bVersion == false) return; var nX1 = null; var nX2 = null; var nOffset = -2; var nBottom = null; var nPole = null; if (getBarState() == BARSTATE_ALLBARS){ BarSinseEntry = null; BarSinceExit = 0; EntryPrice = null; prLinearRegSlopeX2 = null; } if (!bInit){ xClose = close(); xLow = low(); xOpen = open(); xHighestIndexBar = efsInternal("Calc_HighestBar", xClose, fpMaxFlDur); xTrueRange = efsInternal("Calc_TrueRange"); xATR = sma(40, xTrueRange); xLowestUPT = lowest(fpUptBars, xLow); xHighestTrail = highest(fpTRAILBARS); bInit = true; }; var nClose = xClose.getValue(0); var nNextOpen = xOpen.getValue(1); var nHighestTrail = xHighestTrail.getValue(0); nX1 = xHighestIndexBar.getValue(nOffset); if (nX1 == null) return; nX1 = nX1 + -nOffset nX2 = nX1 + 1; var nLF = lowest(nX2).getValue(0); var nTop = highest(nX2).getValue(-2); var xLinearRegSlopeX1 = efsInternal("Calc_LinearRegression_Slope", xClose, nX1); var nPrLinearRegSlope = xLinearRegSlopeX1.getValue(-1); var nATR = xATR.getValue(0); if (nLF == null || nTop == null || nPrLinearRegSlope == null || nATR == null || nHighestTrail == null) return; if (nPrLinearRegSlope < 0 && (nTop - nLF) < fpFlagMin * nATR) nX2 = nX1 + 1; else nX2 = 100; if (nX2 > 2 && nX2 <= fpMaxFlDur){ var xLowestIndexBar = efsInternal("Calc_LowestBar", xClose, fpPX + nX2) var nY23 = xLowestIndexBar.getValue(0); nBottom = lowest(fpPX + nX2).getValue(0); if (nY23 == null || nBottom == null) return; nPole = nTop - nBottom; if (nPole > fpPoleMin * nATR && nY23 > nX2){ nTop = highest(nX2).getValue(-2); var nFlagBot = lowest(nX2).getValue(0); var nLowestUPT = xLowestUPT.getValue(0); var nLinearRegSlopeX1 = xLinearRegSlopeX1.getValue(0); var xLinearRegSlopeX2 = efsInternal("Calc_LinearRegression_Slope", xClose, nX1 - 1); var nLinearRegSlopeX2 = xLinearRegSlopeX2.getValue(-2); if (nLinearRegSlopeX2 != null) prLinearRegSlopeX2 = nLinearRegSlopeX2; if (nLinearRegSlopeX2 == null) nLinearRegSlopeX2 = prLinearRegSlopeX2; var nATR_Y23 = xATR.getValue(-nY23) if (nTop == null || nLowestUPT == null || nATR_Y23 == null){ return; } var nUPT1 = nBottom - nLowestUPT; var nLRSX1 = nLinearRegSlopeX1 * 100; var nLRSX2 = nLinearRegSlopeX2 * 100; if (getCurrentBarIndex() != 0){ if ((!Strategy.isInTrade()) && (nTop - nLF < fpFlagMin * nATR && (nLRSX1 < 0 || nLRSX2 < 0)) && (nPole > fpPoleMin * nATR) && (nUPT1 > 0) && ( (BarSinceExit == 0) || ( (getCurrentBarCount() - BarSinceExit) > fpDistBFlags) )&& (nATR / nATR_Y23 - 1) * 100 > fpATRMin) { nPrice = highest(nX1).getValue(0); if (nPrice == null) return; nPrice = Math.max(nNextOpen, nPrice); Strategy.doLong("Flag", Strategy.STOP, Strategy.NEXTBAR, Strategy.DEFAULT, nPrice) if (Strategy.isInTrade()){ drawShapeRelative(1, BelowBar1, Shape.UPTRIANGLE, null, fpLongColor, Text.PRESET, getCurrentBarCount()+"Flag"); drawTextRelative(1, BelowBar2, "Flag", fpLongColor, null, Text.PRESET|Text.CENTER, null, null, getCurrentBarCount()+"Flag_Text"); EntryPrice = nPrice; BarSinseEntry = getCurrentBarCount()+1; return; } } } } } if (Strategy.isInTrade()){ var nBSE = getCurrentBarCount() - BarSinseEntry; var nX3 = (xHighestIndexBar.getValue(-(nBSE + 2))) - nBSE + 1; nX3 = nX3 + nBSE + 2; nTop = highest(nX3).getValue(-(nBSE + 1)); nBottom = lowest(fpPX + nX3).getValue(-(nBSE + 1)); nPole = (nTop - nBottom)/(nBottom + 0.0001) * 100; var nTargetPer = fpK * nPole; var nPrTarget = (1 + nTargetPer / 100) * EntryPrice; var nL3 = lowest( nX3, low()).getValue(-nBSE); if (nClose >= nPrTarget){ Strategy.doSell("pTARGET", Strategy.CLOSE, Strategy.THISBAR); drawShapeRelative(0, AboveBar1, Shape.DOWNTRIANGLE, null, fpShortColor, Text.PRESET, getCurrentBarIndex()+"XLall_t"); drawTextRelative(0, AboveBar2, "pTARGET", fpShortColor, null, Text.PRESET|Text.CENTER, null, null, getCurrentBarIndex()+"XLall_ts"); BarSinceExit = getCurrentBarCount(); return; } if (nBSE > fpBSEMIN){ var nSell = nL3 - fpATRLL * nATR; nSell = Math.min(nNextOpen, nSell); Strategy.doSell("UNDER FLAG", Strategy.STOP, Strategy.NEXTBAR, Strategy.DEFAULT, nSell) if (!Strategy.isInTrade()){ drawShapeRelative(1, AboveBar1, Shape.DOWNTRIANGLE, null, fpShortColor, Text.PRESET, getCurrentBarIndex()+"XLall_u"); drawTextRelative(1, AboveBar2, "UNDER FLAG", fpShortColor, null, Text.PRESET|Text.CENTER, null, null, getCurrentBarIndex()+"XLall_ut"); BarSinceExit = getCurrentBarCount(); return; } } if (nClose < nHighestTrail - fpATRTRAIL * nATR){ Strategy.doSell("TRAIL", Strategy.MARKET, Strategy.NEXTBAR) drawShapeRelative(1, AboveBar1, Shape.DOWNTRIANGLE, null, fpShortColor, Text.PRESET, getCurrentBarIndex()+"XLall_tr"); drawTextRelative(1, AboveBar2, "TRAIL", fpShortColor, null, Text.PRESET|Text.CENTER, null, null, getCurrentBarIndex()+"XLall_trt"); BarSinceExit = getCurrentBarCount() + 1; return; } if (nBSE > fpBSEINACT && nClose < EntryPrice + fpATRINACT*nATR){ Strategy.doSell("INACTIVITY", Strategy.MARKET, Strategy.NEXTBAR) drawShapeRelative(1, AboveBar1, Shape.DOWNTRIANGLE, null, fpShortColor, Text.PRESET, getCurrentBarIndex()+"XLall_i"); drawTextRelative(1, AboveBar2, "INACTIVITY", fpShortColor, null, Text.PRESET|Text.CENTER, null, null, getCurrentBarIndex()+"XLall_it"); BarSinceExit = getCurrentBarCount(); return; } if (nBSE > fpTimeExit){ Strategy.doSell("TIME", Strategy.MARKET, Strategy.NEXTBAR) drawShapeRelative(1, AboveBar1, Shape.DOWNTRIANGLE, null, fpShortColor, Text.PRESET, getCurrentBarIndex()+"XLall_m"); drawTextRelative(1, AboveBar2, "TIME", fpShortColor, null, Text.PRESET|Text.CENTER, null, null, getCurrentBarIndex()+"XLall_mt"); BarSinceExit = getCurrentBarCount(); return; } } return; } function Calc_HighestBar(xSource, nLength){ var nBarNumber = 0; var nBarValue = xSource.getValue(0); for (i = 1; i <= nLength - 1; i++){ var nSource = xSource.getValue(-i); if (nSource == null) return; if (nSource > nBarValue){ nBarValue = nSource; nBarNumber = i; } } return nBarNumber; } function Calc_LowestBar(xSource, nLength){ var nBarNumber = 0; var nBarValue = xSource.getValue(0); for (i = 1; i <= nLength - 1; i++){ var nSource = xSource.getValue(-i); if (nSource == null) return; if (nSource < nBarValue){ nBarValue = nSource; nBarNumber = i; } } return nBarNumber; } function Calc_LinearRegression_Slope(xSourse, nPeriod) { if (getCurrentBarCount() < nPeriod) return; var nPer = 1; var nTime = 0; var nTSq = 0; var nPrice = 0; var nTimePrice = 0; var nVal = 0; var nSlope = 0; for (var i = 0; i < nPeriod; i++) { nTime += nPer; nVal = xSourse.getValue(-(nPeriod - 1) + i); nPrice += nVal; nTimePrice += (nVal * nPer); nTSq += (nPer * nPer); nPer++; } nPer--; nSlope = ((nPer * nTimePrice) - (nTime * nPrice)) / ((nPer * nTSq) - (nTime * nTime)); return nSlope; } var xClose = null; var xHigh = null; var xLow = null; function Calc_TrueRange(){ if (getBarState() == BARSTATE_ALLBARS) { xClose = close(); xHigh = high(); xLow = low(); } var nPrClose = xClose.getValue(-1); if (nPrClose == null) return; var nHigh = xHigh.getValue(0); var nLow = xLow.getValue(0); var nTrueHigh = null; var nTrueLow = null; if (nPrClose > nHigh) nTrueHigh = nPrClose else nTrueHigh = nHigh; if (nPrClose < nLow) nTrueLow = nPrClose else nTrueLow = nLow; var nReturnValue = nTrueHigh - nTrueLow; return nReturnValue; } function verify(){ var b = false; if (getBuildNumber() < 779){ drawTextAbsolute(5, 35, "This study requires version 8.0 or later.", Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT, null, 13, "error"); drawTextAbsolute(5, 20, "Click HERE to upgrade.@URL=https://www.esignal.com/download/default.asp", Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT, null, 13, "upgrade"); return b; } else b = true; return b; }
Just in time for the holidays, technician Markos Katsanos has delivered us a fantastic gift in the form of his article in this issue, “Detecting Flags In Intraday Charts.” In the article, Katsanos demonstrates that even the most classic of flag patterns can be used with intraday data.
At thinkorswim, we’ve used our proprietary scripting language thinkScript to build a strategy for detecting trends using this method. We’ve made the loading process extremely easy — simply click on the link https://tos.mx/TQSAVh and choose Backtest in thinkorswim, then choose to rename your study to “IntradayFlagFormation.” You can adjust the parameters of these within the edit studies window to fine-tune your variables.
You can see from the chart in Figure 3 that the strategy on thinkorswim charts will give you entry points — shown as the two blue arrows — when a flag is beginning and when the point forms. You will also see that the peak of the pole is the indicator with the exit point. To see how this Strategy performed simply right click on the exit point and choose Show report. For a detailed description see the article in Technical Analysis of STOCKS & COMMODITIES. Happy swimming!
FIGURE 3: THINKORSWIM. The two blue arrows show entry points for the strategy when a flag is beginning and when the point forms. The peak of the pole is the indicator with the exit point.
Detecting chart patterns is always a special joy to code if the provided rules are well-thought-out and fully mechanical. Such is the clear definition of flag patterns given by author Markos Katsanos in his article in this issue, “Detecting Flags In Intraday Charts.”
We’ve used the following simplified set of rules to detect flags:
See Figure 4 for a sample chart.
FIGURE 4: WEALTH-LAB, FLAG PATTERN. This sample Wealth-Lab 6 chart illustrates the detection of the flag pattern on a five-minute chart of the SPY (S&P 500 SPDR).
With minimal tweaks to the system’s parameters, this set of rules can be applied to charts of different time frames (such as end-of-day, Figure 5).
FIGURE 5: WEALTH-LAB, FAILED FLAG. This shows a failed flag on the daily chart of American Express (AXP) in Wealth-Lab 6.
Yet there’s room for improvement: Consider adding a filter against erratic price movement; phase out the less probable trades against the medium-term (daily) trend; and play around with the exits. (To our taste, the initial stop at the flag bottom may result in premature exits; for example, subtracting an ATR from that level could be a more robust approach.)
In addition, we already offer a similar system that looks for tight consolidation ranges, and it is available to Wealth-Lab users along with other related systems that mechanically identify chart patterns. To download it, first download all publicly available strategies (that is, click download in the open strategy dialog). Then look for the strategy named “Rectangle Trading System (Acme R)” in the chart patterns folder.
using System; using System.Collections.Generic; using System.Text; using System.Drawing; using WealthLab; using WealthLab.Indicators; namespace WealthLab.Strategies { public class Katsanos201412 : WealthScript { private StrategyParameter paramPoleTimeout; private StrategyParameter paramFlagTimeout; private StrategyParameter paramUptrendBeforePole; private StrategyParameter paramMinFlagDuration; private StrategyParameter paramMaxFlagDuration; private StrategyParameter paramSMAPeriod; private StrategyParameter paramInactivityStop; private StrategyParameter paramTimeout; private StrategyParameter paramPoleHeight; private StrategyParameter paramFlagHeight; private StrategyParameter paramProfitTarget; void DrawRectangle(int b1, int b2, double p1, double p2, Color c) { double[] rect = { b1, p1, b1, p2, b2, p2, b2, p1 }; DrawPolygon( PricePane, Color.Blue, c, LineStyle.Solid, 1, true, rect ); } public Katsanos201412() { paramPoleTimeout = CreateParameter("Pole Timeout", 23, 10, 50, 1); paramPoleHeight = CreateParameter("Pole Height", 5.5, 1.0, 10, 0.5); paramUptrendBeforePole = CreateParameter("Uptrend Before Pole", 70, 10, 100, 10); paramSMAPeriod = CreateParameter("SMA Period", 50, 10, 100, 5); paramFlagHeight = CreateParameter("Flag Height", 2.5, 0.5, 5.0, 0.5); paramFlagTimeout = CreateParameter("Flag Timeout", 15, 3, 30, 1); paramMinFlagDuration = CreateParameter("Min Flag Duration", 3, 3, 30, 1); paramInactivityStop = CreateParameter("Inactivity Stop", 70, 10, 100, 10); paramTimeout = CreateParameter("Timeout", 100, 10, 100, 10); paramProfitTarget = CreateParameter("Profit Target ATR", 1.2, 0.2, 3.0, 0.2); } protected override void Execute() { int PoleTimeout = paramPoleTimeout.ValueInt, FlagTimeout = paramFlagTimeout.ValueInt, UptrendLeadingToPole = paramUptrendBeforePole.ValueInt, MinFlagDuration = paramMinFlagDuration.ValueInt, smaPeriod = paramSMAPeriod.ValueInt, inactivityStop = paramInactivityStop.ValueInt, timeout = paramTimeout.ValueInt, PoleBar = 0, FlagBar = 0, ba = 0; double poleHeight = paramPoleHeight.Value, flagHeight = paramFlagHeight.Value, currPoleHeight = 0, ProfitTarget = paramProfitTarget.Value, InitialStop = 0, ws = 0.5, flagTop = 0, flagBottom = 0; bool PoleValid = false, FlagValid = false; SMA sma = SMA.Series( Close,smaPeriod ); LinearRegSlope lrs = LinearRegSlope.Series( Close, FlagTimeout ); HideVolume(); for(int bar = GetTradingLoopStartBar(100); bar < Bars.Count; bar++) { if (IsLastPositionActive) { // Exits Position p = LastPosition; double atr = ATR.Series( Bars, 40 )[bar]; double high = p.HighestHighAsOfBar(bar); double chandelier = high - atr * 3; double inactivity = atr * 4; if( ( bar+1 - p.EntryBar >= inactivityStop ) && ( p.MFEAsOfBar( bar ) < inactivity ) ) SellAtMarket( bar+1, p, "Inactivity+MFE" ); else if( bar+1 - p.EntryBar >= timeout ) SellAtMarket( bar+1, p, "Time exit" ); else if( !SellAtStop( bar+1, p, p.RiskStopLevel, "Stop loss" ) ) if( !SellAtStop( bar+1, p, chandelier, "Trailing (Chandelier)" ) ) SellAtLimit( bar+1, p, p.AutoProfitLevel, "Profit Target" ); } else { if( !PoleValid ) { //Uptrend during the last 70 bars leading to the pole. if(Lowest.Value( bar, Close, PoleTimeout ) > Lowest.Value( bar, Close, UptrendLeadingToPole )) { //A steep pole of 5.5 times the average true range (ATR) or more, in 23 bars or less. currPoleHeight = Close[PoleBar] - Close[bar - PoleTimeout]; double atr = ATR.Value(bar, Bars, 40); PoleBar = bar; PoleValid = currPoleHeight >= atr * poleHeight ? true: false; } } if( PoleValid ) { if( !FlagValid ) { //A flag breaking out in 15 bars or less from the pole top and sloping horizontally or slightly down. if( bar <= PoleBar + FlagTimeout && bar >= PoleBar + MinFlagDuration ) // To avoid premature triggering { flagTop = Highest.Value( bar, Close, FlagTimeout ); flagBottom = Lowest.Value( bar, Close, FlagTimeout ); InitialStop = flagBottom; double flagRange = flagTop - flagBottom; double atr = ATR.Value(bar, Bars, 40); double slope = lrs[bar]; bool isSlopeOK = slope > -0.04 && slope <= 0.01; //Flag depth not more than 2.5 times the ATR measured from the highest to the lowest point in the flag. if( flagRange <= atr * flagHeight && isSlopeOK ) { FlagValid = true; FlagBar = bar; } } else PoleValid = bar + 1 - PoleBar < PoleTimeout; // reset if Setup has timed out } if( FlagValid ) { if( BuyAtStop( bar + 1, Highest.Value(bar, High, FlagTimeout) ) != null ) { // Draw flag and pole DrawRectangle( FlagBar, FlagBar-FlagTimeout, flagTop, flagBottom, Color.LightSteelBlue ); DrawRectangle( PoleBar, PoleBar-PoleTimeout, Close[PoleBar], Close[PoleBar-PoleTimeout], Color.Transparent ); // Assign initial stop and profit target levels LastPosition.AutoProfitLevel = LastPosition.EntryPrice + currPoleHeight * ProfitTarget; LastPosition.RiskStopLevel = InitialStop; PoleValid = false; FlagValid = false; // reset Setup variables } else // reset if Setup has timed out { PoleValid = bar + 1 - PoleBar < PoleTimeout; FlagValid = false; flagTop = 0; flagBottom = 0; } } } } } } } }
To sum up, the proposed technique is useful to recognize brief retreats in steep trends.
The strategy presented in Markos Katsanos’ article in this issue for flag detection (“Detecting Flags In Intraday Charts”) can be easily implemented in NeuroShell Trader using NeuroShell Trader’s ability to call external programs. The programs may be written in C, C++, Power Basic, or Delphi.
After moving the EasyLanguage code given in Katsanos’ article to your preferred compiler and creating a dynamic link library (DLL), you can insert the resulting entry and profit target indicators as follows:
To recreate the intraday flag trading system, select “New trading strategy” from the Insert menu and enter the following in the appropriate locations of the trading strategy wizard:
BUY LONG CONDITIONS: Intraday Flag Entry Signal( High, Low, Close) BUY STOP PRICE: Intraday Flag Entry Stop( High, Low, Close) LONG TRAILING STOP PRICES: TrailPriceATR(Trading Strategy, 40, 3) SELL LONG CONDITIONS: [1 of which must be true] Intraday Flag Profit Target Signal( High, Low, Close) Inactivity%(Trading Strategy, 10, 15)
If you have NeuroShell Trader Professional, you can also choose whether the parameters should be optimized. After backtesting the trading strategy, use the detailed analysis button to view the backtest and trade-by-trade statistics for the strategy.
Users of NeuroShell Trader can go to the Stocks & Commodities section of the NeuroShell Trader free technical support website to download a copy of this or any previous Traders’ Tips.
A sample chart is shown in Figure 6.
FIGURE 6: NEUROSHELL TRADER, intraday flag strategy. This NeuroShell Trader chart displays the intraday flag trading strategy.
In “Detecting Flags In Intraday Charts” in this issue, author Markos Katsanos proposes using flag chart formations as a entry signal for 15-minute chart trading. Chart formations are highly subjective and difficult to express in strict mathematical rules. What is “clearly visible” to the eye is not that clear when mathematical rules are applied. In the article, Katsanos has made an attempt to code flag formation detection and provides EasyLanguage code for his technique. The formula we are presenting is more or less a direct translation of his code into AmiBroker Formula Language (AFL).
The AFL code is shown below, as well as at the AmiBroker site.
LISTING 1. maxfldur = 15; // max flag duration flagmin = 2.5; // max atr in lowest point in flag px = 23; //Max Pole Duration. upt1bars = 70; // Bars for Uptrend leading to flag polemin = 5.5; //Min ATR Height of the pole lbf = 50; // Min distance between flags ATRmin = 5;// Min volatility change K = 1.2; //Profit Target constant timeexit = 100; //Time exit bars atrLL = 3; // max loss distance in ATR units atrTrail = 3; // trail distance in ATR uints // FLAG CALCULATION X1 = Ref( HHVBars( C, maxfldur ), -2 );//Flag duration ex pole top X2 = X1 + 1; //Flag duration including pole top LF = LLV( C, X2 ); TOP = Ref( HHV( C, X2 ), -2 ); X2 = IIF( Ref( LinRegSlope( C, X1 ), -1 ) < 0 AND TOP - LF < flagmin * ATR( 40 ), X1 + 1, 100 ); PreCond1 = X2 > 2 AND X2 <= maxfldur; //Limits flag duration Y23 = LLVBars( C, PX + X2 ); // Pole bottom bar BOTTOM = LLV( C, ( PX + X2 ) );// Pole bottom POLE = TOP - BOTTOM; PreCond2 = TOP - BOTTOM > polemin * ATR( 40 ) AND Y23 > X2; TOP = Ref( HHV( C, X2 ), -2 ); FLAGBOT = LLV( C, X2 ); UPT1 = BOTTOM - LLV( L, upt1bars ); //Uptrend leading to flag LRSX1 = LinRegSlope( C, X1 ) * 100; //Slope in flag LRSX2 = Ref( LinRegSlope( C, X1 - 1 ), -2 ) * 100; //Slope in flag before breakout Condition1 = TOP - LF < flagmin * ATR( 40 ) AND ( LRSX1 < 0 OR LRSX2 < 0 ); //Limits flag slope between 0 and -3 ATR Condition2 = POLE > polemin * ATR( 40 ); //Limits min pole height Condition3 = UPT1 > 0; //Uptrend leading to flag Condition4 = ( ATR( 40 ) / Ref( ATR( 40 ), Y23 ) - 1 ) * 100 > ATRmin; //Volatility TriggerPrice = HHV( C, X1 ); Buy = PreCond1 AND PreCond2 AND Condition1 AND Condition2 AND Condition3 and Condition4 AND H > TriggerPrice; BuyPrice = TriggerPrice; Sell = 0; // all exits using stops ApplyStop( stopTypeNBar, stopModeBars, timeexit ); // time exit ApplyStop( stopTypeProfit, stopModePoint, k * pole, True ); // profit target ApplyStop( stopTypeTrailing, stopModePoint, atrTrail * ATR( 40 ), True, True ); // trailing ApplyStop( stopTypeLoss, stopModePoint, atrLL * ATR( 40 ), True ); Equity( 1 ); // evaluate stops Plot( C, "Price", colorDefault, styleBar | styleThick ); PlotShapes( IIf( Buy, shapeUpArrow, 0 ), colorGreen, 0, Low ); PlotShapes( IIf( Sell, shapeDownArrow, 0 ), colorRed, 0, High ); Title = Name() + " " + Date() + " Price = " + Close + " Signal : " + WriteIf( Buy, "Entry", WriteIf( Sell == 2, "Max loss", WriteIf( Sell == 3, "Profit target", WriteIf( Sell == 4, "Trailing stop", WriteIf( Sell == 5, "Time stop", "None" ) ) ) ) );
A sample chart demonstrating flag detection based on Katsanos’ approach is shown in Figure 7.
FIGURE 7: AMIBROKER, FLAG. Here is an example of a detected flag formation on a 15-minute chart of AAPL. The buy signal is represented by the green arrow and a trailing stop is represented by the red arrow.
The intraday flag strategy presented in “Detecting Flags In Intraday Charts” by Markos Katsanos in this issue has been made available for download at www.ninjatrader.com/SC/December2014SC.zip.
Once you have it downloaded, from within the NinjaTrader Control Center window, select the menu File → Utilities → Import NinjaScript and select the downloaded file. This file is for NinjaTrader version 7 or greater.
You can review the strategy source code by selecting the menu Tools → Edit NinjaScript → Strategy from within the NinjaTrader Control Center window and selecting the “IntradayFlags” file.
A sample chart implementing the strategy is shown in Figure 8.
FIGURE 8: NINJATRADER. The screenshot shows the IntradayFlags strategy applied to a 15-minute chart of Tesla Motors (TSLA) in NinjaTrader.
Our Traders’ Tip for this month is based on “Detecting Flags In Intraday Charts” by Markos Katsanos in this issue.
In the article, the author proposes a set of rules to find flag continuation patterns in intraday financial data. Flags are defined according to five key criteria or conditions, mostly relating to some minimum height and width requirements of the flag, as measured by average true range (ATR). Once you are in a trade, there are five types of time-based or price-based exit strategies you can use.
We have added some custom Updata code based on Katsanos’ article to the Updata library, which may be downloaded by clicking the custom menu and then system library. Those who cannot access the library due to a firewall may copy the code shown below and paste it into the Updata custom editor and save it. See Figure 9 for a sample chart.
'Intraday Flag Strategy PARAMETER "Max Flag Duration" #MAXFLDUR=15 PARAMETER "Max ATR @ Flag Low" @FLAGMIN=2.5 PARAMETER "Max Pole Duration" #PX=23 PARAMETER "Bars Up To Flag" #UPTIBARS=70 PARAMETER "Min ATR Pole Height" @POLEMIN=5.5 PARAMETER "Min Flag Distance" #LBF=50 PARAMETER "Min Volatility Change" #ATRMin=5 PARAMETER "Profit Target Const." @K=1.2 PARAMETER "Time Exit Bars" #TIMEEXIT=100 PARAMETER "Below Flag StopLoss" #ATRLL=3 PARAMETER "Trailing Stop ATR" @ATRTRAIL=3 PARAMETER "Trailing Stop Bars" #TRAILBARS=5 PARAMETER "Entry Inactivity Bars" #BSEINACT=70 PARAMETER "Entry Inactivity ATR" #ATRINACT=4 @X1=0 @X2=0 @X3=0 @LF=0 @TOP=0 @Y23=0 @BOTTOM=0 @POLE=0 @FLAGBOT=0 @UPT1=0 @LRSX1=0 @LRSX2=0 @CONDITION1=0 @CONDITION2=0 @CONDITION3=0 @CONDITION4=0 @CONDITION5=0 #BARSSINCEEXIT=0 #BUYORDER=0 @BUYENTRYLEVEL=0 @ENTRYPRICE=0 @targetPER=0 @targetPRICE=0 @L3=0 @TRAILINGSTOP=100000 @STOP=0 FOR #CURDATE=0 TO #LASTDATE 'FLAG CALCULATION @X1=PHIGH(CLOSE,#MAXFLDUR) @X2=@X1+1 @LF=PLOW(CLOSE,@X2) @TOP=PHIGH(CLOSE,@X2) IF (LSR(CLOSE,@X2,0,0)-LSR(CLOSE,@X2,0,@X2))<0 AND (@TOP-@LF)<@FLAGMIN*ATR(40) @X2=@X1+1 ELSE @X2=100 ENDIF IF @X2>2 AND @X2<#MAXFLDUR @Y23=PLOW(CLOSE,#PX+@X2) @BOTTOM=PLOW(CLOSE,#PX+@X2) @POLE=@TOP-@BOTTOM ENDIF IF @POLE>@POLEMIN*ATR(40) AND @Y23>@X2 @TOP=PHIGH(CLOSE,@X2) @FLAGBOT=PLOW(CLOSE,@X2) @UPT1=@BOTTOM-PLOW(LOW,#UPTIBARS) ENDIF @LRSX1=(LSR(CLOSE,@X1,0,0)-LSR(CLOSE,@X1,0,@X1))*(100/@X1) @LRSX2=(LSR(CLOSE,@X1-1,0,0)-LSR(CLOSE,@X1-1,0,@X1-1))*(100/(@X1-1)) @CONDITION1=(@TOP-@LF)<@FLAGMIN*ATR(40) AND (@LRSX1<0 OR @LRSX2<0) @CONDITION2=@POLE>@POLEMIN*ATR(40) @CONDITION3=@UPT1>0 @CONDITION4=(#BARSSINCEEXIT=0) OR (#BARSSINCEEXIT>#LBF) @CONDITION5=ATR(40)/HIST(ATR(40),@Y23-1)*100>#ATRMin IF #BUYORDER=TRUE AND HIGH>@BUYENTRYLEVEL BUY @BUYENTRYLEVEL #BUYORDER=FALSE @ENTRYPRICE=@BUYENTRYLEVEL ENDIF IF @CONDITION1=@CONDITION2=@CONDITION3=@CONDITION4=@CONDITION5=1 #BUYORDER=TRUE @BUYENTRYLEVEL=PHIGH(CLOSE,@X1) ENDIF 'EXIT CONDITIONS IF ORDERISOPEN=1 @X3=PHIGH(CLOSE,ORDEROPENFOR+#MAXFLDUR) @TOP=PHIGH(CLOSE,@X3+ORDEROPENFOR) @BOTTOM=PLOW(CLOSE,#PX+@X3) @POLE=100*(@TOP-@BOTTOM)/(@BOTTOM+0.0001) @targetPER=@K*@POLE @targetPRICE=(1+@targetPER/100)*@ENTRYPRICE @L3=PLOW(LOW,@X3+ORDEROPENFOR) ENDIF 'PROFIT TARGET IF CLOSE>@targetPRICE SELL @targetPRICE ENDIF 'STOP IF ORDEROPENFOR>#BSEINACT @STOP=@L3-#ATRLL*ATR(40) If CLOSE<@STOP SELL @STOP EndIf ENDIF 'TRAILING STOP IF HIST(CLOSE<PHIGH(CLOSE,#TRAILBARS)-@ATRTRAIL*ATR(40),1) SELL CLOSE ENDIF 'INACTIVITY EXIT IF ORDEROPENFOR>#BSEINACT AND HIST(CLOSE<@ENTRYPRICE+#ATRINACT*ATR(40),1) SELL CLOSE ENDIF 'TIME EXIT IF HIST(ORDEROPENFOR>#TIMEEXIT,1) SELL CLOSE ENDIF NEXT
FIGURE 9: UPDATA. Here is an example of the intraday flag strategy applied to a chart of Apple Inc. (AAPL) in 15-minute resolution.
The AIQ code based on Markos Katsanos’ article in this issue, “Detecting Flags In Intraday Charts,” is provided at www.TradersEdgeSystems.com/traderstips.htm, and is also shown here:
!DETECTING FLAGS IN INTRADAY CHARTS !Author: Markos Katsanos, TASC December 2014 !Coded by: Richard Denning 10/18/14 !USER DEFINED FUNCTIONS: C is [close]. Name is description(). !COMPLETED FLAG PATTERN: FLAG is [Flag]. FLAG_breakoutup if FLAG > 0. FLAG_breakoutdn if FLAG < 0. !EMERGING FLAG PATTERN: e_FLAG is [eFLAG]. e_FLAGup if e_FLAG > 0. e_FLAGdn if e_FLAG < 0. !REPORTS TO LIST ALL FLAG PATTERS: ShowAllCompleted if C>0 and FLAG <> 0. ShowAllEmerging if C>0 and e_FLAG <>0.
The AIQ program has a chart-pattern recognition module that operates only in daily mode. I am providing code to find both completed flag patterns and also emerging flag patterns.
In Figure 10, I show a chart of G-III Apparel Group Ltd., which shows a flag pattern completed on June 25, 2014 (green up arrow), when the price broke above the down-sloping flag top. Although the volume was above average on the breakout, the followthrough was lacking.
FIGURE 10: AIQ. This sample chart shows G-III Apparel Group Ltd. (GIII) with a completed flag pattern (indicated by the green up arrow).
Note that I did not code exits for the pattern, as the built-in exits can be used to experiment with the flag pattern entry. Note also that the AIQ version of flags does not match exactly the intraday flags that are defined by Katsanos in his article.
The TradersStudio code based on Markos Katsanos’ article in this issue, “Detecting Flags In Intraday Charts,” is provided at the following websites:
The following code files are provided in the download:
The code is also shown here:
'DETECTING FLAGS IN INTRADAY CHARTS 'Author: Markos Katsanos, TASC December 2014 'Coded by: Richard Denning 10/18/14 'www.adersEdgeSystems.com Function POLE(atrPoleHeight,flagPoleLen,atrLen,ByRef poleHeight) 'atrPoleHeight=5.5,flagPoleLen,atrLen poleHeight = Highest(H,flagPoleLen)-Lowest(L,flagPoleLen) If poleHeight/avgtruerange(atrLen)>atrPoleHeight Then POLE = 1 Else POLE = 0 End If End Function '----------------------------------------------------------------------------------------------- Function FLAG(maxFlagLen,minFlagLen,maxFlagDepth,atrLen) Dim poleHigh Dim barsToHigh poleHigh = Highest(H,maxFlagLen) barsToHigh = MRO(H=poleHigh,maxFlagLen,1) If countof(H > poleHigh,barsToHigh,0)=0 And barsToHigh >= minFlagLen Then If (Highest(H,maxFlagLen) - Lowest(L,maxFlagLen))/avgtruerange(atrLen) <= maxFlagDepth Then FLAG = 1 End If Else FLAG = 0 End If End Function '------------------------------------------------------------------------------------------------ Function FLAG_BREAKUP(maxFlagLen,minFlagLen,maxFlagDepth,flagPoleLen,atrLen) 'maxFlagLen=15,minFlagLen=5,maxFlagDepth=2.5,flagPoleLen=23,atrLen=50 If Highest(H,maxFlagLen+flagPoleLen,1)=Highest(H,flagPoleLen,1) Then If H > Highest(H,maxFlagLen,1) And FLAG(maxFlagLen,minFlagLen,maxFlagDepth,atrLen) Then FLAG_BREAKUP = 1 End If Else FLAG_BREAKUP = 0 End If End Function '------------------------------------------------------------------------------------------------- 'COUNTOF Function 'returns how many times a rule is true in the lookback length 'coded by Richard Denning 01/04/08 Function COUNTOF(rule As BarArray, countLen As Integer, offset As Integer) Dim count As Integer Dim counter As Integer For counter = 0 + offset To countLen + offset - 1 If rule[counter] Then count = count + 1 End If Next COUNTOF = count End Function '------------------------------------------------------------------------------------------------------------------------ Sub HIGH_FLAG_sys(atrPoleHeight,flagPoleLen,atrLen,maxFlagLen,minFlagLen,maxFlagDepth,trendLen,maxBars,profitTargetMult) 'atrPoleHeight=5.5,flagPoleLen=23,atrLen=50,maxFlagLen=15,minFlagLen=5,maxFlagDepth=2.5,maxBars=20,profitTargetMult=1.2 Dim isPOLE As BarArray Dim isFlagBU As BarArray Dim isUpTrend As BarArray Dim higherVola As BarArray Dim poleHeight As BarArray isPOLE = POLE(atrPoleHeight,flagPoleLen,atrLen,poleHeight) isFlagBU = FLAG_BREAKUP(maxFlagLen,minFlagLen,maxFlagDepth,flagPoleLen,atrLen) isUpTrend = IIF(C>Average(C,trendLen),1,0) higherVola = avgtruerange(atrLen) < Avgtruerange(maxFlagLen) If isPOLE And isFlagBU And isUpTrend Then Buy("LE",1,0,Market,Day) If BarsSinceEntry > maxBars Then ExitLong("LX_time","",1,0,Market,Day) If C >= EntryPrice + poleHeight[BarsSinceEntry] Then ExitLong("LX_pt","",1,0,Market,Day) If C < Lowest(L,flagPoleLen,BarsSinceEntry) Then ExitLong("LX_sl","",1,0,Market,Day) End Sub '--------------------------------------------------------------------------------------------------------------------------
Note that the parameters were taken from the author’s intraday testing and may not be the desired ones for daily bar trading. Note also that this system cannot be used to trade intraday, as TradersStudio does not as yet have a real-time module, although historical intraday testing can be done on saved intraday data.
In Figure 11, I show a chart of Autodesk (ADSK) with a flag breakout trade that exited at the profit target.
FIGURE 11: TRADERSSTUDIO. Here is an example flag breakout trade that reached the profit target on Autodesk (ADSK).
The techniques presented in Markos Katsanos’ article in this issue, “Detecting Flags In Intraday Charts, can be applied at tick, end-of-day, weekly, and longer bar-size intervals. For ease of data access, I have used American Airlines (AAL) end-of-day data for this example (Figure 12).
FIGURE 12: EXCEL, TRADE COMBINATIONS. Price chart with two pole, flag, and trade combinations. A short LBF falue found these.
The 16 user control values used in the article provide a lot of opportunities for exploration. For example, here, an LBF of 4 finds two adjacent profitable flags. LBF=50 as shown in the article only finds one of these.
K, the pole multiplier used to set the price target, also can have significant impact on trades and the location of additional examples.
As built, this spreadsheet will not highlight pole and flag combinations that do not lead to a breakout and trade initiation.
Also, as built, the high of a bar may exceed the target price, but the trade may not exit. The logic provided in the article requires that the close of the bar must exceed the target price to initiate a price target close.
The spreadsheet file for this Traders’ Tip can be downloaded below. To successfully download it, follow these steps: