Skip to content

Instantly share code, notes, and snippets.

@caub
Last active August 29, 2015 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caub/10067926 to your computer and use it in GitHub Desktop.
Save caub/10067926 to your computer and use it in GitHub Desktop.
zigzag chart indicator, a piecewise linear curve fit with alternate slopes (up, down, up, down...), used here to detect double-top and double-bottom patterns http://en.wikipedia.org/wiki/Chart_pattern
function [z,s,offsetH,offsetL]=zigzag(depth, h, l)
% return the zigzag points (=last points of a series of local extrema)
% and signals (= first point of these series) from a series of prices
% (h=l by default, or else specify the high prices per period, and low price from ohlc)
% depth: the number of points looked back
if nargin<=1
rng(0);
depth=3;
h = cumsum(rand(40,1)-0.5);
end
if nargin==2
l = h;
end
s = []; % the signals (=first maxima/minima)
z = []; % the zigzags (= last maxima/minima)
offsetH=1; %first index of highs
offsetL=1; %first index of lows, it will be [offsetH,offsetL]=[1,2] or [2,1]
cur=0; % current max/min index, minima are *-1
for i=depth:length(h)
[~,j]=max(flipud(h(i-depth+1:i)));
[~,k]=min(flipud(l(i-depth+1:i)));
% flipud so as to make sure to take last values in case of equality
if j==1 && k==1 %rare case
if cur==0 % oh damn let's choose to start with min
offsetH =2;
s= [s;i];
cur = -i;
elseif cur>0
if h(i)>=h(cur)
cur = i;
end
else
if l(i)<=l(-cur)
cur = -i;
end
end
elseif j==1
if cur==0
offsetL =2;
s= [s;i];
cur = i;
elseif cur>0
if h(i)>=h(cur)
cur = i;
end
else
z=[z;-cur];
s= [s;i];
cur = i;
end
elseif k==1
if cur==0
offsetH =2;
s= [s;i];
cur = -i;
elseif cur<0
if l(i)<=l(-cur)
cur = -i;
end
else
z=[z;cur];
s= [s;i];
cur = -i;
end
end
end
%% Demo fake dataset
rng(4)
h = cumsum(rand(1010,1)-0.5);
% smooth with ema(8)
%w=linspace(-1,0,8);w =w/sum(w);
%h=conv(h,w);
h=h(1:length(h));
%h = movavg(h, 8,8,'e'); %the same as the 3 lines above
l = h;
depth = 8;
d0 = 0.2;
D0 = 0.5;
%% Real dataset
tic
f = fopen('EURCHF_1m_2014.csv'); % http://www.dukascopy.com/swiss/french/marketwatch/historical/
A = textscan(f, '%s %f %f %f %f %f', 'Delimiter', ',j', 'HeaderLines', 1);
fclose(f);
A{1} = char(A{1});
A{1} = datenum(A{1}(:, 1:19), 'dd.mm.yyyy hh:MM:ss');
toc
Hour = hour(A{1});
Weekday = weekday(A{1});
iw = find(Hour >21 & Weekday==1 | Weekday>1 & Weekday<6 | Weekday==6 & Hour<21);
HLC = [A{3}(iw) A{4}(iw) A{5}(iw)];
cost = 0.0001;
depth = 20;
d0 = 1e-4;
D0 = 10e-4;
hlc = HLC(end-2e5:end-1e5,:);
[h,l,c] = deal(hlc(:,1), hlc(:,2), hlc(:,3));
%% Extract zigzags and plot them
[z,s,offsetH,offsetL]=zigzag(depth, h, l);
close all;
figure, hold on
plot([h l])
ss = zeros(size(s));
ss(offsetH:2:end) = h(s(offsetH:2:end));
ss(offsetL:2:end) = l(s(offsetL:2:end));
plot(s, ss, '+c');
zz = zeros(size(z));
zz(offsetH:2:end) = h(z(offsetH:2:end));
zz(offsetL:2:end) = l(z(offsetL:2:end));
plot(z, zz, 'dr');
line(z, zz, 'Color', 'k')
%% Double-top/bottom (http://bit.ly/double-top)
D = abs(zz(1:end-3)-zz(2:end-2));
d = abs(zz(4:end)-zz(2:end-2));
i = d<d0 & D >=D0;
% isolate double-tops from double bottom
ih = mod((1:length(z)-3)',2)==offsetH-1 & i;
il = mod((1:length(z)-3)',2)==offsetL-1 & i;
fprintf('found %d double-tops, %d double-bottoms out of %d zigzags\n', length(find(ih)), length(find(il)), length(z));
ih_ = [0;ih;0;0]|[0;0;ih;0]|[0;0;0;ih];% keep points 2,3,4 of zigzags
ihs = logical([0;0;0;ih(1:end-1)]);
il_ = [0;il;0;0]|[0;0;il;0]|[0;0;0;il];
ils = logical([0;0;0;il(1:end-1)]);
zzh = nan(size(zz));
zzh(ih_) = zz(ih_);
zzl = nan(size(zz));
zzl(il_) = zz(il_);
% plot fancy zigzag lines, double-top in blue, double-bottom in red
plot(z, zzh, 'LineWidth', 3, 'Color', 'b');
plot(z, zzl, 'LineWidth', 3, 'Color', 'm');
% calculate profit on a strategy that sells on double-top, buys on
% double-bottom
signals = zeros(size(h));
signals(s(logical([0;0;0;0;ih(1:end-4)]))) = 1;
signals(s(logical([0;0;0;0;il(1:end-4)]))) = -1;
for i = 2:length(h) % fill with prior non-null sign
if signals(i) == 0
signals(i) = signals(i-1);
end
end
costPerTrade=0;
trades = [0; 0; diff(signals(1:end-1))]; % shift trading by 1 period, time for the order to pass
cash = cumsum(-trades.*h-abs(trades)*costPerTrade/2);
pandl = [0; signals(1:end-1)].*h + cash;
r = diff(pandl);
figure,plot(pandl);
%% Head-and-shoulders (http://bit.ly/double-top)
D = abs(zz(1:end-5)-zz(4:end-2));
dh = abs(zz(2:end-4)-zz(6:end));
dl = abs(zz(3:end-3)-zz(5:end-1));
i = D>D0 & dh<d0 & dl<d0 ...
& sign(zz(1:end-5)-zz(4:end-2)) == sign(zz(6:end)-zz(4:end-2)) ...
& sign(zz(1:end-5)-zz(4:end-2)) == sign(zz(2:end-4)-zz(4:end-2));
% isolate double-tops from double bottom
ih = mod((1:length(z)-5)',2)==offsetH-1 & i;
il = mod((1:length(z)-5)',2)==offsetL-1 & i;
ih_ = [0;ih;0;0;0;0]|[0;0;ih;0;0;0]|[0;0;0;ih;0;0]|[0;0;0;0;ih;0]|[0;0;0;0;0;ih];
ihs = logical([0;0;0;ih(1:end-1)]);
il_ = [0;il;0;0;0;0]|[0;0;il;0;0;0]|[0;0;0;il;0;0]|[0;0;0;0;il;0]|[0;0;0;0;0;il];
ils = logical([0;0;0;il(1:end-1)]);
zzh = nan(size(zz));
zzh(ih_) = zz(ih_);
zzl = nan(size(zz));
zzl(il_) = zz(il_);
%
plot(z, zzh, 'LineWidth', 4, 'Color', 'b');
plot(z, zzl, 'LineWidth', 4, 'Color', 'm');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment