Traders' Tip text
This month's article by David Cline reminds of how hard is to invent something new in today's technical analysis. Seasoned readers may recall that a loosely related idea was featured in the March and September 2001 issues of Stocks & Commodities by Viktor Likhovidov who offered a candlestick coding technique. His quantitative approach called “CandleCode” expresses a candlestick value as a binary number, coding the body and shadows in different "bits" of the number.
The idea behind the "CandleSticktistics" is to make candlesticks sort of binary, simplified, consistent instrument to identify patterns. By turning them into a fixed number of patterns, author's automatic pattern search method performs statistical analysis in order to gauge their predictive capability. Our preliminary testing suggests that it delivers quite interesting results.
The C# code we're presenting this month splits the backtest range in two equal parts: in-sample and out-of-sample periods. At first, it discovers the most popular HO:HC:OL signatures across the in-sample range. Leaving the actual candlestick probability programming to motivated users, we enter quick trades using the knowledge gained on in-sample data. The number of signatures is configurable; the more the Top-N consists of, the more trades will there be, and vice versa. For example, if you choose to trade only the Top-1 pattern, the resulting system will be very selective. The percentage of price change following a pattern is also interactively configurable through a “parameter slider” at the bottom of the screen. By default, each trade lasts for a single bar only and is exited at the close of the entry bar.
Figure 1. A Wealth-Lab 6 chart illustrating the application of the Top-1 candlestick pattern on out-of-sample data using a Daily chart of a continuous contract of crude oil futures (Brent).
On a closing note, our code uses LINQ – a C# / .NET way of producing flexible SQL-like statements on any data contained in arrays, lists, databases and so on. Advanced users can leverage the power of LINQ to perform statistical analysis on candlestick patterns in terse, compact and almost natural language form.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
using System.Linq;
namespace WealthLab.Strategies
{
public class Signature
{
public int Bar;
public string Sig;
public double ROC;
public Signature( int Bar, string Sig, double ROC ){ this.Bar = Bar; this.Sig = Sig; this.ROC = ROC;}
}
public class TASC201502 : WealthScript
{
private StrategyParameter paramTop;
private StrategyParameter paramRoc;
public TASC201502()
{
paramTop = CreateParameter("Top N",10,1,10,1);
paramRoc = CreateParameter("ROC period",1,1,10,1);
}
protected override void Execute()
{
int roc = paramRoc.ValueInt;
const char tab = '\u0009';
int halfData = Bars.Count / 2; // For in-sample and out-of-sample
int oosBar = Bars.Count - halfData;
int segmentCount = 5;
double segmentDivisor = 100d / (double)segmentCount;
List lstSig1 = new List();
if (Bars.Count < roc )
{
DrawLabel(PricePane, "Insufficient data");
Abort();
}
for(int bar = GetTradingLoopStartBar(10); bar < Bars.Count; bar++)
{
List lstSig = new List();
double open = Openbar, high = Highbar, low = Lowbar, close = Closebar;
double averageRange = ATR.Series(Bars,10)bar;
double candleRange = high - low;
double rangeMultiplier = candleRange / averageRange;
candleRange = candleRange /= 100d;
rangeMultiplier = rangeMultiplier > 1 ? 1.0 : rangeMultiplier;
double HO = Math.Round((((high - open) / candleRange) * rangeMultiplier) / segmentDivisor);
double HC = Math.Round((((high - close) / candleRange) * rangeMultiplier) / segmentDivisor);
double OL = Math.Round((((open - low) / candleRange) * rangeMultiplier) / segmentDivisor);
//Candle signature = HO-HC-OL
var sig = HO+":"+HC+":"+OL;
if( bar < oosBar )
lstSig1.Add(new Signature(bar,sig,ROC.Series(Close,roc)bar+1));
else
{
if (IsLastPositionActive)
{
SellAtClose(bar,LastPosition);
}
else
{
var listSignaturesOOS = lstSig1.OrderBy(p => p.ROC).Reverse().Take(paramTop.ValueInt).ToList();
foreach(var s in listSignaturesOOS )
{
if( sig == s.Sig )
{
int count = lstSig1.Count( t => t.Sig == sig );
AnnotateBar(s.Sig,bar,true,Color.Blue);
BuyAtClose(bar);
break;
}
}
}
}
}
var listSignaturesIS = lstSig1.OrderBy(p => p.ROC).Reverse().Take(paramTop.ValueInt).ToList();
DrawLabel(PricePane, "Most frequent signatures from in-sample period containing 50% data: " + "\n\r");
DrawLabel(PricePane, "Signature:" + tab + "Count:" + tab + "% Change next bar:");
foreach(var s in listSignaturesIS )
{
int count = lstSig1.Count( t => t.Sig == s.Sig );
DrawLabel(PricePane, s.Sig + tab + tab + count + tab + Bars.FormatValue(s.ROC));
AnnotateBar(s.Sig,s.Bar,true,Color.Black);
}
AnnotateBar("Out of sample starts",oosBar,true,Color.Red);
for( int bar = 0; bar < oosBar; bar++ )
SetBackgroundColor(bar,Color.FromArgb(30,Color.Blue));
}
}
}
Eugene
Wealth-Lab team
www.wealth-lab.com