Log in to see Cloud of Tags

Wealth-Lab Wiki

TASC 2014-12 | Detecting Flags In Intraday Charts (Katsanos)

RSS

Traders' Tip text

Detecting chart patterns is always a special joy to code if provided rules are well thought-out and fully mechanical. Such is the clear definition of flag pattern given by author Markos Katsanos in December 2014 issue of Stocks & Commodities.

We've used this simplified set of rules to detect flags:

  • A steep pole of A times the average true range (ATR) in B bars
  • A flag breaking out in C bars or less from the pole top and sloping horizontally
  • Flag depth not more than D times the ATR measured from the highest to the lowest point in the flag.
  • Uptrend during the last E bars leading to the pole.

Image

Figure 1. A Wealth-Lab 6 chart illustrating the detection of the flag pattern on a 5-minute chart chart of SPY (S&P 500 SPDR).

With minimal tweaks to the system's parameters it can be applied to charts of different time frames (e.g. EOD):

Image

Figure 2. A failed flag on the Daily chart of AXP (American Express) in Wealth-Lab 6.

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, playing with the exits (to our taste, the initial stop at the flag bottom may result in premature exits; subtracting an ATR from that level could be a more robust approach etc).

Also, there's a closely similar system looking for tight consolidation ranges, available to Wealth-Lab users for download called «Rectangle Trading System (Acme R)». Along with other related systems mechanically identifying chart patterns, look for it in the «Chart Patterns» folder after having downloaded all publicly available strategies (click “Download” in the “Open Strategy” dialog).

To sum up, the proposed technique is useful to recognize brief retreats in steep trends.


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; } } } } } } } }

Eugene
Wealth-Lab team
www.wealth-lab.com

Important Disclaimer: The information provided by Wealth-Lab is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security.  The owner of Wealth-Lab.com assumes no liability resulting from the use of the material contained herein for investment purposes. By using this web site, you agree to the terms of this disclaimer and our Terms of Use.


ScrewTurn Wiki. Some of the icons created by FamFamFam.