Candle-Stick Evaluation (v2)

This indicator formula allows you to select Candlestick patterns, mark their occurrence on the chart, display their frequency, and test their predictive value. Apply the code to an Indicator, open up the Param window, and drag the CandlePattern# slider left and right to change to a different pattern. The selected pattern is identified in the chart Title text and graphically on your chart by a upwards pointing Blue arrow. The white line on your chart represents the resulting equity (“profit plot” in the Param window) for the pattern for the selected Profit Period. This line can be turned On or Off in the Param window. Entries (Buy or Short) are at the Close of the Pattern bar. Exits (Sell or Cover) are at the Open and can be delayed up to 10 bars with respect to the entry, as set in the Param window.

This code was originally written many years ago and was revised to take advantage of newer AFL functions. Also, code has been added to allow you to Backtest and Optimize to find the best pattern, delay, and trading mode. Note that trading mode “1” in the optimizer report indicates Long trades, a “0” indicates Short trades. Red Arrows are either Sell or Short, Green Arrows are either Buy or Cover, depending on your Mode selection. The chart below shows how typical CandlePatterns are identified:

 

Try different tickers; they will all behave differently. Only basic stats are provided in the chart Title, please add your own price patterns and metrics.

function ParamOptimizedescriptiondefaultValminvmaxvstep )
{
    return OptimizedescriptionParamdescriptiondefaultValminvmaxvstep ), minvmaxvstep );
}
 
O1 Ref(O,-1);O2 Ref(O,-2);  
H1 Ref(H,-1);H2 Ref(H,-2);  
L1 Ref(L,-1);L2 Ref(L,-2);  
C1 Ref(C,-1);C2 Ref(C,-2);  
function CandlePattern(P)  
{  
global PatternName;  
if(== 0) { PatternName "NearDoji"Pv = (abs(O-C)<= ((H-L)*0.1)); }  
else if(== 1) { PatternName "BlackCandle"Pv = (O>C); }  
else if(== 2) { PatternName "LongBlackCandle"Pv = (O>AND (O-C)/(.001+H-L)>.6); }  
else if(== 3) { PatternName "SmallBlackCandle"Pv = ((O>C) AND ((H-L)>(3*(O-C)))); }  
else if(== 4) { PatternName "WhiteCandle"Pv = (C>O); }  
else if(== 5) { PatternName "LongWhiteCandle"Pv = ((C>O) AND ((C-O)/(.001+H-L)>.6)); }  
else if(== 6) { PatternName "SmallWhiteCandle"Pv = ((C>O) AND ((H-L)>(3*(C-O)))); }  
else if(== 7) { PatternName "BlackMaubozu"Pv = (O>AND H==AND C==L); }  
else if(== 8) { PatternName "WhiteMaubozu"Pv = (C>AND H==AND O==L); }  
else if(== 9) { PatternName "BlackClosingMarubozu"Pv = (O>AND C==L); }  
else if(== 10) { PatternName "WhiteClosingMarubozu"Pv = (C>AND C==H); }  
else if(== 11) { PatternName "BlackOpeningMarubozu"Pv = (O>AND O==H); }  
else if(== 12) { PatternName "WhiteOpeningMarubozu"Pv = (C>AND O==L); }  
else if(== 13) { PatternName "HangingMan"Pv = (((H-L)>4*(O-C)) AND ((C-L)/(.001+H-L)>= 0.75) AND ((O-L)/(.001+H-L)>= 0.75)); }  
else if(== 14) { PatternName "Hammer"Pv = (((H-L)>3*(O-C)) AND ((C-L)/(.001+H-L)>0.6) AND ((O-L)/(.001+H-L)>0.6)); }  
else if(== 15) { PatternName "InvertedHammer"Pv = (((H-L)>3*(O-C)) AND ((H-C)/(.001+H-L)>0.6) AND ((H-O)/(.001+H-L)>0.6)); }  
else if(== 16) { PatternName "ShootingStar"Pv = (((H-L)>4*(O-C)) AND ((H-C)/(.001+H-L)>= 0.75) AND ((H-O)/(.001+H-L)>= 0.75)); }  
else if(== 17) { PatternName "BlackSpinningTop"Pv = ((O>C) AND ((H-L)>(3*(O-C))) AND (((H-O)/(.001+H-L))<.4) AND (((C-L)/(.001+H-L))<.4)); }  
else if(== 18) { PatternName "WhiteSpinningTop"Pv = ((C>O) AND ((H-L)>(3*(C-O))) AND (((H-C)/(.001+H-L))<.4) AND (((O-L)/(.001+H-L))<.4)); }  
else if(== 19) { PatternName "BearishAbandonedBaby"Pv = ((C1 == O1) AND (C2>O2) AND (O>C) AND (L1>H2) AND (L1>H)); }  
else if(== 20) { PatternName "BearishEveningDojiStar"Pv = ((C2>O2) AND ((C2-O2)/(.001+H2-L2)>.6) AND (C2<O1) AND (C1>O1) AND ((H1-L1)>(3*(C1-O1))) AND (O>C) AND (O<O1)); }  
else if(== 21) { PatternName "DarkCloudCover"Pv = (C1>O1 AND ((C1+O1)/2)>AND O>AND O>C1 AND C>O1 AND (O-C)/(.001+(H-L)>0.6)); }  
else if(== 22) { PatternName "BearishEngulfing"Pv = ((C1>O1) AND (O>C) AND (O>= C1) AND (O1>= C) AND ((O-C)>(C1-O1))); }  
else if(== 23) { PatternName "ThreeOutsideDownPattern"Pv = ((C2>O2) AND (O1>C1) AND (O1>= C2) AND (O2>= C1) AND ((O1-C1)>(C2-O2)) AND (O>C) AND (C<C1)); }  
else if(== 24) { PatternName "BullishAbandonedBaby"Pv = ((C1 == O1) AND (O2>C2) AND (C>O) AND (L2>H1) AND (L>H1)); }  
else if(== 25) { PatternName "BullishMorningDojiStar"Pv = ((O2>C2) AND ((O2-C2)/(.001+H2-L2)>.6) AND (C2>O1) AND (O1>C1) AND ((H1-L1)>(3*(C1-O1))) AND (C>O) AND (O>O1)); }  
else if(== 26) { PatternName "BullishEngulfing"Pv = ((O1>C1) AND (C>O) AND (C>= O1) AND (C1>= O) AND ((C-O)>(O1-C1))); }  
else if(== 27) { PatternName "ThreeOutsideUpPattern"Pv = ((O2>C2) AND (C1>O1) AND (C1>= O2) AND (C2>= O1) AND ((C1-O1)>(O2-C2)) AND (C>O) AND (C>C1)); }  
else if(== 28) { PatternName "BullishHarami"Pv = ((O1>C1) AND (C>O) AND (C<= O1) AND (C1<= O) AND ((C-O)<(O1-C1))); }  
else if(== 29) { PatternName "ThreeInsideUpPattern"Pv = ((O2>C2) AND (C1>O1) AND (C1<= O2) AND (C2<= O1) AND ((C1-O1)<(O2-C2)) AND (C>O) AND (C>C1) AND (O>O1)); }  
else if(== 30) { PatternName "PiercingLine"Pv = ((C1<O1) AND (((O1+C1)/2)<C) AND (O<C) AND (O<C1) AND (C<O1) AND ((C-O)/(.001+(H-L))>0.6)); }  
else if(== 31) { PatternName "BearishHarami"Pv = ((C1>O1) AND (O>C) AND (O<= C1) AND (O1<= C) AND ((O-C)<(C1-O1))); }  
else if(== 32) { PatternName "ThreeInsideDownPattern"Pv = ((C2>O2) AND (O1>C1) AND (O1<= C2) AND (O2<= C1) AND ((O1-C1)<(C2-O2)) AND (O>C) AND (C<C1) AND (O<O1)); }  
else if(== 33) { PatternName "ThreeWhiteSoldiers"Pv = (C>O*1.01) AND (C1>O1*1.01) AND (C2>O2*1.01) AND (C>C1) AND (C1>C2) AND (O<C1) AND (O>O1) AND (O1<C2) AND (O1>O2) AND (((H-C)/(H-L))<.2) AND (((H1-C1)/(H1-L1))<.2) AND (((H2-C2)/(H2-L2))<.2); }  
else if(== 34) { PatternName "DarkCloudCover"Pv = (C1>O1*1.01) AND (O>C) AND (O>H1) AND (C>O1) AND (((C1+O1)/2)>C) AND (C>O1) AND (MA(C,13)-Ref(MA(C,13),-4)>0); }  
else if(== 35) { PatternName "ThreeBlackCrows"Pv = (O>C*1.01) AND (O1>C1*1.01) AND (O2>C2*1.01) AND (C<C1) AND (C1<C2) AND (O>C1) AND (O<O1) AND (O1>C2) AND (O1<O2) AND (((C-L)/(H-L))<.2) AND (((C1-L1)/(H1-L1))<.2) AND (((C2-L2)/(H2-L2))<.2); }  
else if(== 36) { PatternName "doji"Pv = (== C); }  
else if(== 37) { PatternName "GapUp"Pv GapUp(); }  
else if(== 38) { PatternName "GapDown"Pv GapDown(); }  
else if(== 39) { PatternName "BigGapUp"Pv L>1.01*H1; }  
else if(== 40) { PatternName "BigGapDown"Pv H<0.99*L1; }  
else if(== 41) { PatternName "HugeGapUp"Pv L>1.02*H1; }  
else if(== 42) { PatternName "HugeGapDown"Pv H<0.98*L1; }  
else if(== 43) { PatternName "DoubleGapUp"Pv GapUp() AND Ref(GapUp(),-1); }  
else if(== 44) { PatternName "DoubleGapDown"Pv GapDown() AND Ref(GapDown(),-1); }  
return Pv;  
}  
SetTradeDelays(0,0,0,0);
SetOption("AllowSameBarExit",False);
 
 
ParamOptimize("CandlePattern#",20,0,44,1);
PlotProfit ParamToggle("Profit Plot","HIDE|SHOW",0);  
PP paramOptimize("Profit period (bars)",1,1,10,1);  
CandlePatternTrue CandlePattern(P);  
Mode Paramoptimize("Mode: Long=0, Short=1",1,0,1,1);

if ( Mode == )
{
    Short Cover 0;
    Buy CandlePatternTrue;
    BuyPrice C;
    Sell RefBuy, -PP );
    SellPrice O;
}
else
{
    Buy Sell 0;
    Short CandlePatternTrue;
    ShortPrice C;
    Cover RefShort, -PP );
    CoverPrice O;
}
 
E                 Equity(1);
Profit         E-GetOption("InitialEquity");  
Upday         IIf(Ref(CandlePatternTrue,-1), C>O,0);  
downDay         IIf(Ref(CandlePatternTrue,-1), C<O,0);  
PatternColorIIf(CandlePatternTrue,2,1);  
NumUpDays     LastValue(Cum(UpDay));  
NumDownDays LastValue(Cum(downDay));  
NumPatterns LastValue(Cum(CandlePatternTrue ));  
 
GraphXSpace 10;
Plot(C,"Close",colorBlack,64);  
if( PlotProfit Plot(E,"",2,styleStaircase|styleOwnScale);  
Pointer IIfCandlePatternTrue0.97*L,0);
//PlotOHLC(0,0,Pointer ,0,"",6,styleCloud|styleNoLabel);
PlotShapesIIf(Pointer,shapeUpArrow,shapeNone),colorBlue,0,0.99*L,0);  
PlotShapes(IIf(Buy OR Cover,shapeUpTriangle,shapeNone),colorBrightGreen,0,C,0);  
PlotShapes(IIf(Sell OR Short,shapeDownTriangle,shapeNone),colorRed,0,O,0);
 
Title "\nCANDLE PATTERN ANALYSIS, Ticker: "Name()+"\n\n"+  
" Candle Pattern: "+PatternName+"\n"+
"#Patterns found: "+NumToStr(NumPatterns ,1.0)+"\n"+  
"   Next #Updays: "+NumToStr(NumUpDays,1.0,False)+  
" ["+NumToStr(Nz(NumUpDays/NumPatterns*100),1.0,False)+"%]\n"+  
" Next #DownDays: "+NumToStr(NumDownDays,1.0,False)+  
" ["+NumToStr(Nz(NumDownDays/numpatterns*100),1.0,False)+"%]\n\n"+  
"  Profit at cursor: $"+NumToStr(Profit,1.2)+"\n"+  
"Ave.Profit/Pattern: $"+NumToStr(Nz(Profit/NumPatterns),1.2)+"\n"+  
"      Final Profit: $"+NumToStr(LastValue(Profit),1.2)+"\n\n"+
" Open: "+NumToStr(O,1.2)+"\n"+  
" High: "+NumToStr(H,1.2)+"\n"+  
"  Low: "+NumToStr(L,1.2)+"\n"+  
"Close: "+NumToStr(C,1.2);  

Edited by Al Venosa.

Introduction to Research and Exploration

All system designs are the result of extensive research, exploration, and trial and error experiments. Traditional research tends to build on already existing knowledge, while empirical testing tends to explore more of the unknown and can lead to interesting new solutions. This category will cover ideas and programs you might want to explore when you are looking for a new system.

Edited by Al venosa

Using Zig() to reveal profit potential

Indicators like the Zig() can be used to reveal the profit potential for different stocks and in different time frames. Using this function is purely a fun experiment to give you a sense of where the profits are and what happens when you reduce trade duration. This function looks into the future and, of course, should never be used for trading with real money.

As shown below in the last line of the code, the Chart Title displays profit potential for combinations of trade duration and % change. Author’s note: while this is arguable, my interpretation is that shorter trade durations will give you smaller DDs, lower risk, and greater profits. The old argument of commission costs killing a High Frequency trading system no longer holds; commission costs may almost be ignored. For managing a high number of short term trades, using the IBc can allow 100 or more automated trades per day with no effort at all.

To run this code, copy it to an Indicator, click Insert, right-click on the chart to bring up the Param window, and vary the parameters to see for yourself where the profits occur. Try changing the ticker, commissions, the test period, etc. You may be surprised and decide you are trading the wrong Tickers.

Opt1 = Param("%Zig(Change)",0.1,0,1,0.05);
Opt2 = Param("Min.AllowableDuration",1,1,20,1);
Z = Zig(C,Opt1);
Buy = Z < Ref(Z,1) AND Z < Ref(Z,-1);
Sell = Z > Ref(Z,1) AND Z > Ref(Z,-1);
Buy = Buy AND BarsSince(Sell) > Opt2;
Sell = Sell AND BarsSince(Buy) > Opt2;
Short = Sell;
Cover = Buy;
Eq = Equity(1);
Plot(C,"",colorBlack,styleBar);
Plot(z,"",colorWhite,styleLine);
ShowTriangles = ParamToggle("Arrows","HIDE|SHOW",1);
if(showTriangles)
{
PlotShapes(IIf(Buy,shapeSmallUpTriangle,shapeNone),5,0,BuyPrice,0);
PlotShapes(IIf(Sell,shapeHollowDownTriangle,shapeNone),4,0,SellPrice,0);
PlotShapes(IIf(Cover,shapeHollowUpTriangle,shapeNone),5,0,CoverPrice,0);
PlotShapes(IIf(Short,shapeSmallDownTriangle,shapeNone),4,0,ShortPrice,0);
}
InitialEquity = GetOption("InitialEquity");
Title = "\n"+"\n"+"FinalProfit:"+NumToStr((LastValue(Eq)-InitialEquity)/InitialEquity*100,1.2)+"%\n";

Edited by Al Venosa

Analyzing Ticker Relationships

Note: There may be a problem with this code since the possibility of padded data occuring within the correlation period wasn’t considered. This code is under review right now. [Herman]

When experimenting with or looking for trading ideas, you may want to analyze the relationships or correlation between any two tickers. For example, you could be looking for lagging, leading, tracking, or contrarian qualities. You can do this very easily using the Automatic Analysis (AA) Explore function and the AFL Correlation() function.

The example below generates an X-Y table or correlation matrix showing the correlation between tickers selected in the AA Filter (Y-axis) and the tickers from the Watchlist selected in the AA Param window (X-axis). The resulting table looks like this:

snag-0762.jpg

For the X-Y axis to have the same order, you should sort your Watchlist alphabetically in your workspace (right-click on the Watchlist and click Sort Alphabetically). To run the exploration below, copy the code to the AA editor, set the AA Filter for your Watchlist, select your Watchlist in the AA Param window, and click Explore.

WLNum = Param("WatchList Number",0,0,64,1);
CorrPd = Param("Correlation Period",8,1,30,1);
list = GetCategorySymbols( categoryWatchlist, WLNum);
SetOption("NoDefaultColumns",True);
Filter = Status("LastBarInTest");
SetSortColumns( 1 );
AddTextColumn(Name(),"Correlation",1.0);
Ticker1= Name();
for( Col=0; (Ticker2=StrExtract( List, Col))!= ""; Col++)
{
Var2 = Foreign(Ticker2,"C");
Corr = Correlation( C, Var2, CorrPd);
Color = IIf(Corr>0, colorBrightGreen, IIf(Corr<0, colorRed,colorWhite));
Color = IIf(Ticker1==Ticker2, 1, Color);
AddColumn( Corr, Ticker2, 1.3, 1, Color);
}

Edited by Al Venosa