## Copyright (C) 2017 dekalog
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{retval} =} candle (@var{highprices}, @var{lowprices}, @var{closeprices}, @var{openprices})
## @deftypefnx {Function File} {@var{retval} =} candle (@var{highprices}, @var{lowprices}, @var{closeprices}, @var{openprices}, @var{color})
## @deftypefnx {Function File} {@var{retval} =} candle (@var{highprices}, @var{lowprices}, @var{closeprices}, @var{openprices}, @var{color}, @var{dates})
## @deftypefnx {Function File} {@var{retval} =} candle (@var{highprices}, @var{lowprices}, @var{closeprices}, @var{openprices}, @var{color}, @var{dates}, @var{dateform})
##
## Plot the @var{highprices}, @var{lowprices}, @var{closeprices} and @var{openprices} of a security as a candlestick chart.
##
## HighPrices - High prices for a security. A column vector.
##
## LowPrices - Low prices for a security. A column vector.
##
## ClosePrices - Close prices for a security. A column vector.
##
## OpenPrices - Open prices for a security. A column vector.
##
## Color - (optional) Candlestick color is specified as a case insensitive four
## character row vector, e.g. "brwk". The characters that are accepted are
## k, b, c, r, m, w, g and y for black, blue, cyan, red, magenta, white, green
## and yellow respectively. Default colors are "brwk" applied in order to bars
## where the closing price is greater than the opening price, bars where the
## closing price is less than the opening price, the chart background color and
## the candlestick wicks. If fewer than four colors are specified, they are
## applied in turn in the above order with default colors for unspecified colors.
## For example, user supplied colors "gm" will plot green upbars and magenta
## downbars with a default white background and black wicks. If the user
## specified color for background is black, without specifying the wick color,
## e.g. "gmk", the default wick color is white. All other choices for background
## color will default to black for wicks. If all four colors are user specified,
## those colors will be used. Doji bars and single price bars, e.g. open = high
## = low = close, are plotted with the color for wicks, with single price bars
## being plotted as points/dots.
##
## Dates - (Optional) Dates for user specified x-axis tick labels. Dates can be
## a serial date number column (see datenum), a datevec matrix (See datevec)
## or a character vector of dates. If specified as either a datenum or a datevec,
## the Dateform argument is required. If the Dates argument is supplied, the
## Color argument must also be explicitly specified.
##
## Dateform - (Optional) Either a Date character string or a single integer code
## number used to format the x-axis tick labels (See datestr). Only required if
## Dates is specified as a serial date number column (See datenum) or a datevec
## matrix (See datevec).
##
## @seealso{datenum, datestr, datevec, highlow, bolling, dateaxis, movavg,
## pointfig}
## @end deftypefn
## Author: dekalog
## Created: 2017-12-05
function candle ( varargin )
if ( nargin < 4 || nargin > 7 )
print_usage ();
elseif ( nargin == 4 )
HighPrices = varargin{1}; LowPrices = varargin{2}; ClosePrices = varargin{3};
OpenPrices = varargin{4}; color = "brwk";
elseif ( nargin == 5 )
HighPrices = varargin{1}; LowPrices = varargin{2}; ClosePrices = varargin{3};
OpenPrices = varargin{4}; color = varargin{5};
elseif ( nargin == 6 )
HighPrices = varargin{1}; LowPrices = varargin{2}; ClosePrices = varargin{3};
OpenPrices = varargin{4}; color = varargin{5}; dates = varargin{6};
elseif ( nargin == 7 )
HighPrices = varargin{1}; LowPrices = varargin{2}; ClosePrices = varargin{3};
OpenPrices = varargin{4}; color = varargin{5}; dates = varargin{6};
dateform = varargin{7};
endif
if ( ! isnumeric ( HighPrices ) || ! isnumeric ( LowPrices ) || ...
! isnumeric ( ClosePrices ) || ! isnumeric ( OpenPrices ) ) # one of OHLC is not numeric
error ("candle: The inputs for HighPrices, LowPrices, ClosePrices and OpenPrices must be numeric vectors.");
endif
if ( size ( OpenPrices ) != size ( HighPrices ) )
error ("candle: OpenPrices and HighPrices vectors are different lengths.");
endif
if ( size ( HighPrices ) != size ( LowPrices ) )
error ("candle: HighPrices and LowPrices vectors are different lengths.");
endif
if ( size ( LowPrices ) != size ( ClosePrices ) )
error ("candle: LowPrices and ClosePrices vectors are different lengths.");
endif
if ( size ( ClosePrices, 1 ) == 1 && size ( ClosePrices, 2 ) > 1 ) # ohlc inputs are row vectors, so transpose them
OpenPrices = OpenPrices'; HighPrices = HighPrices'; LowPrices = LowPrices';
ClosePrices = ClosePrices';
warning ("candle: The HighPrices, LowPrices, ClosePrices and OpenPrices should be column vectors. They have been transposed.");
endif
## check the user input Color argument, if it's character row vector
if ( ( nargin >= 5 && ischar ( color ) ) && size ( color, 1 ) == 1 )
if ( size ( color, 2 ) == 1 ) # only one color has been user specified
color = [ tolower( color ) "rwk" ]; # so add default colors for down bars, background and wicks
elseif ( size ( color, 2 ) == 2 ) # two colors have been user specified
color = [ tolower( color ) "wk" ]; # so add default colors for background and wicks
elseif ( size ( color, 2 ) == 3 ) # three colors have been user specified
if ( color ( 3 ) == "k" || color ( 3 ) == "K" ) # if user selected background is black
color = [ tolower( color ) "w" ]; # set wicks to default white
else
color = [ tolower( color ) "k" ]; # else default black wicks
endif
elseif ( size ( color, 2 ) >= 4 ) # all four colors have been user specified, extra character inputs ignored
color = tolower( color ); # correct in case user input contains upper case e.g. "BRWK"
endif
elseif ( nargin >= 5 && ! ischar ( color ) ) # the user input for color is not a charcter vector
warning ("candle: The fifth input argument, Color, should be a character row vector for Color.\nThe chart has been plotted with default colors.");
color = "brwk";
elseif ( ischar ( color ) && size ( color, 1 ) != 1 ) # user input is more than one row of characters - a date character array by mistake?
warning ("candle: Color is not a single row character vector. Possibly a column Dates character vector?\nThe chart has been plotted with default colors.");
color = "brwk";
endif # end of nargin >= 5 && ischar ( color ) ) && size ( color, 1 ) == 1 if statement
wicks = HighPrices .- LowPrices;
body = ClosePrices .- OpenPrices;
up_down = sign ( body );
body_width = 20;
wick_width = 1;
doji_size = 10;
one_price_size = 15;
hold on;
## first, plot the chart background color
plot ( HighPrices, color( 3 ), LowPrices, color( 3 ) );
fill ( [ min( xlim ) max( xlim ) max( xlim ) min( xlim ) ], ...
[ min( ylim ) min( ylim ) max( ylim ) max( ylim ) ], color( 3 ) );
## plot the wicks
x = ( 1 : length ( ClosePrices ) ); # the x-axis
idx = x;
high_nan = nan ( size ( HighPrices ) ); high_nan( idx ) = HighPrices; # highs
low_nan = nan ( size ( LowPrices ) ); low_nan( idx ) = LowPrices; # lows
x = reshape ( [ x; x; nan( size ( x ) ) ], [], 1 );
y = reshape ( [ high_nan(:)'; low_nan(:)'; nan( 1 , length ( HighPrices ) ) ], ...
[] , 1 );
plot ( x, y, color( 4 ), "linewidth", wick_width ); # plot wicks
## plot the up bar bodies
x = ( 1 : length ( ClosePrices ) ); # the x-axis
idx = ( up_down == 1 ); idx = find ( idx ); # index by condition close > open
high_nan = nan ( size ( HighPrices ) ); high_nan( idx ) = ClosePrices( idx ); # body highs
low_nan = nan ( size ( LowPrices ) ); low_nan( idx ) = OpenPrices( idx ); # body lows
x = reshape ( [ x; x; nan( size ( x ) ) ], [], 1 );
y = reshape ( [ high_nan(:)'; low_nan(:)'; nan( 1, length ( HighPrices ) ) ], ...
[], 1 );
plot ( x, y, color( 1 ), "linewidth", body_width ); # plot bodies for up bars
## plot the down bar bodies
x = ( 1 : length ( ClosePrices ) ); # the x-axis
idx = ( up_down == -1 ); idx = find ( idx ); # index by condition close < open
high_nan = nan ( size ( HighPrices ) ); high_nan( idx ) = OpenPrices( idx ); # body highs
low_nan = nan ( size ( LowPrices ) ); low_nan( idx ) = ClosePrices( idx ); # body lows
x = reshape ( [ x; x; nan( size ( x ) ) ], [], 1 );
y = reshape ( [ high_nan(:)'; low_nan(:)'; nan( 1, length ( HighPrices ) ) ], ...
[], 1 );
plot ( x, y, color( 2 ), "linewidth", body_width ); # plot bodies for down bars
## plot special cases
## doji bars
doji_bar = ( HighPrices > LowPrices ) .* ( ClosePrices == OpenPrices ); ...
doji_ix = find ( doji_bar );
if ( length ( doji_ix ) >= 1 )
x = ( 1 : length ( ClosePrices ) ); # the x-axis
plot ( x( doji_ix ), ClosePrices( doji_ix ), [ "+" char ( color( 4 ) ) ], ...
"markersize", doji_size ); # plot the open/close as horizontal dash
endif
## OpenPrices == HighPrices == LowPrices == ClosePrices
one_price = ( HighPrices == LowPrices ) .* ( ClosePrices == OpenPrices ) .* ...
( OpenPrices == HighPrices ); one_price_ix = find ( one_price );
if ( length ( one_price_ix ) >= 1 )
x = ( 1 : length ( ClosePrices ) ); # the x-axis
plot ( x( one_price_ix ), ClosePrices( one_price_ix ), ...
[ "." char ( color( 4 ) ) ], "markersize", one_price_size ); # plot as a point/dot
endif
hold off;
## now add the x-axis tick labels, if the user has supplied the correct arguments
if ( nargin == 6 && isnumeric ( dates ) )
error ("candle: If the sixth input argument, Dates, is a serial date number column (See datenum) or a datevec matrix (See datevec), Dateform input is required.\nThe chart has been plotted without x-axis dates.");
endif
if ( nargin == 6 && ischar ( dates ) ) # user has given a character vector of dates for dates
if ( size ( dates, 1 ) != size ( ClosePrices, 1 ) )
error ("candle: The sixth input argument, Dates, and the OHLC prices vectors are different lengths.\nThe chart has been plotted without x-axis dates.");
else
ticks = cellstr ( dates ) ;
ax = "x" ;
xticks = 1 : length ( ClosePrices );
h = gca ();
set ( h, "xtick", xticks );
set ( h, [ ax "ticklabel" ], ticks );
endif
endif
if ( nargin == 7 && isnumeric ( dates ) ) # input arguments 6 and 7 assumed to be a dates vector and dateform respectively
if ( size ( dates, 1 ) != size ( ClosePrices, 1 ) )
error ("candle: The sixth input argument, Dates, and the OHLC prices vectors are different lengths.\nThe chart has been plotted without x-axis dates.");
endif
if ( size ( dates, 2 ) == 1 ) # user has given a possible serial date number column for dates
is_monotonically_increasing = sum ( dates == cummax ( dates ) ) / size ( dates, 1 );
if ( is_monotonically_increasing != 1 )
error ("candle: Dates does not appear to be a serial date number column as it is not monotonically increasing.\nThe chart has been plotted without x-axis dates.");
endif
if ( isnumeric ( dateform ) && ( dateform < 0 || dateform > 31 ) )
error ("candle: Dateform integer code number is out of bounds (See datestr).\nThe chart has been plotted without x-axis dates.");
endif
if ( isnumeric ( dateform ) && rem ( dateform, 1 ) > 0 )
error ("candle: Dateform code number should be an integer 0 - 31 (See datestr).\nThe chart has been plotted without x-axis dates.");
endif
ticks = datestr ( dates, dateform );
ticks = mat2cell ( ticks, ones ( size ( ticks, 1 ), 1 ), size ( ticks, 2 ) );
ax = "x";
xticks = 1 : length ( ClosePrices );
h = gca ();
set ( h, "xtick", xticks );
set ( h, [ ax "ticklabel" ], ticks );
elseif ( size ( dates, 2 ) == 6 ) # user has given a possible datevec matrix for dates
if ( isnumeric ( dateform ) && ( dateform < 0 || dateform > 31 ) )
error ("candle: Dateform integer code number is out of bounds (See datestr).\nThe chart has been plotted without x-axis dates.");
endif
if ( isnumeric ( dateform ) && rem ( dateform, 1 ) > 0 )
error ("candle: Dateform code number should be an integer 0 - 31 (See datestr).\nThe chart has been plotted without x-axis dates.");
endif
ticks = datestr ( dates, dateform );
ticks = mat2cell ( ticks, ones ( size ( ticks, 1 ), 1 ), size ( ticks, 2 ) );
ax = "x";
xticks = 1 : length ( ClosePrices );
h = gca ();
set ( h, "xtick", xticks );
set ( h, [ ax "ticklabel" ], ticks );
else
error ("candle: The numerical Dates input is neither a single column serial date number nor a six column datevec format.\nThe chart has been plotted without x-axis dates.");
endif
endif # end of ( nargin == 7 && isnumeric ( dates ) ) if statement
endfunction
%!demo 1
%! Open = [ 1292.4; 1291.7; 1291.8; 1292.2; 1291.5; 1291.0; 1291.0; 1291.5; 1291.7; 1291.5; 1290.7 ];
%! High = [ 1292.6; 1292.1; 1292.5; 1292.3; 1292.2; 1292.2; 1292.7; 1292.4; 1292.3; 1292.1; 1292.9 ];
%! Low = [ 1291.3; 1291.3; 1291.7; 1291.1; 1290.7; 1290.2; 1290.3; 1291.1; 1291.2; 1290.5; 1290.4 ];
%! Close = [ 1291.8; 1291.7; 1292.2; 1291.5; 1291.0; 1291.1; 1291.5; 1291.7; 1291.6; 1290.8; 1292.8 ];
%! graphics_toolkit('fltk'); candle( High, Low, Close, Open );
%! title("default plot.");
%!demo 2
%! Open = [ 1292.4; 1291.7; 1291.8; 1292.2; 1291.5; 1291.0; 1291.0; 1291.5; 1291.7; 1291.5; 1290.7 ];
%! High = [ 1292.6; 1292.1; 1292.5; 1292.3; 1292.2; 1292.2; 1292.7; 1292.4; 1292.3; 1292.1; 1292.9 ];
%! Low = [ 1291.3; 1291.3; 1291.7; 1291.1; 1290.7; 1290.2; 1290.3; 1291.1; 1291.2; 1290.5; 1290.4 ];
%! Close = [ 1291.8; 1291.7; 1292.2; 1291.5; 1291.0; 1291.1; 1291.5; 1291.7; 1291.6; 1290.8; 1292.8 ];
%! graphics_toolkit('fltk'); candle( High, Low, Close, Open, 'brk' );
%! title("default plot with user selected black background");
%!demo 3
%! Open = [ 1292.4; 1291.7; 1291.8; 1292.2; 1291.5; 1291.0; 1291.0; 1291.5; 1291.7; 1291.5; 1290.7 ];
%! High = [ 1292.6; 1292.1; 1292.5; 1292.3; 1292.2; 1292.2; 1292.7; 1292.4; 1292.3; 1292.1; 1292.9 ];
%! Low = [ 1291.3; 1291.3; 1291.7; 1291.1; 1290.7; 1290.2; 1290.3; 1291.1; 1291.2; 1290.5; 1290.4 ];
%! Close = [ 1291.8; 1291.7; 1292.2; 1291.5; 1291.0; 1291.1; 1291.5; 1291.7; 1291.6; 1290.8; 1292.8 ];
%! graphics_toolkit('fltk'); candle( High, Low, Close, Open, 'brkg' );
%! title("default color candlestick bodies and user selected background and wick colors");
%!demo 4
%! Open = [ 1292.4; 1291.7; 1291.8; 1292.2; 1291.5; 1291.0; 1291.0; 1291.5; 1291.7; 1291.5; 1290.7 ];
%! High = [ 1292.6; 1292.1; 1292.5; 1292.3; 1292.2; 1292.2; 1292.7; 1292.4; 1292.3; 1292.1; 1292.9 ];
%! Low = [ 1291.3; 1291.3; 1291.7; 1291.1; 1290.7; 1290.2; 1290.3; 1291.1; 1291.2; 1290.5; 1290.4 ];
%! Close = [ 1291.8; 1291.7; 1292.2; 1291.5; 1291.0; 1291.1; 1291.5; 1291.7; 1291.6; 1290.8; 1292.8 ];
%! graphics_toolkit('fltk'); candle( High, Low, Close, Open, 'gmby' );
%! title("all four colors being user selected");
%!demo 5
%! Open = [ 1292.4; 1291.7; 1291.8; 1292.2; 1291.5; 1291.0; 1291.0; 1291.5; 1291.7; 1291.5; 1290.7 ];
%! High = [ 1292.6; 1292.1; 1292.5; 1292.3; 1292.2; 1292.2; 1292.7; 1292.4; 1292.3; 1292.1; 1292.9 ];
%! Low = [ 1291.3; 1291.3; 1291.7; 1291.1; 1290.7; 1290.2; 1290.3; 1291.1; 1291.2; 1290.5; 1290.4 ];
%! Close = [ 1291.8; 1291.7; 1292.2; 1291.5; 1291.0; 1291.1; 1291.5; 1291.7; 1291.6; 1290.8; 1292.8 ];
%! datenum_vec = [ 7.3702e+05; 7.3702e+05 ;7.3702e+05; 7.3702e+05; 7.3702e+05; 7.3702e+05; 7.3702e+05; ...
%! 7.3702e+05; 7.3702e+05; 7.3702e+05; 7.3702e+05 ];
%! graphics_toolkit('fltk'); candle( High, Low, Close, Open, 'brwk', datenum_vec, "yyyy-mm-dd HH:MM" );
%! title("default plot with datenum dates and character dateform arguments");
%!demo 6
%! Open = [ 1292.4; 1291.7; 1291.8; 1292.2; 1291.5; 1291.0; 1291.0; 1291.5; 1291.7; 1291.5; 1290.7 ];
%! High = [ 1292.6; 1292.1; 1292.5; 1292.3; 1292.2; 1292.2; 1292.7; 1292.4; 1292.3; 1292.1; 1292.9 ];
%! Low = [ 1291.3; 1291.3; 1291.7; 1291.1; 1290.7; 1290.2; 1290.3; 1291.1; 1291.2; 1290.5; 1290.4 ];
%! Close = [ 1291.8; 1291.7; 1292.2; 1291.5; 1291.0; 1291.1; 1291.5; 1291.7; 1291.6; 1290.8; 1292.8 ];
%! datenum_vec = [ 7.3702e+05; 7.3702e+05 ;7.3702e+05; 7.3702e+05; 7.3702e+05; 7.3702e+05; 7.3702e+05; ...
%! 7.3702e+05; 7.3702e+05; 7.3702e+05; 7.3702e+05 ];
%! graphics_toolkit('fltk'); candle( High, Low, Close, Open, 'brk', datenum_vec, 31 );
%! title("default plot with user selected black background with datenum dates and integer dateform arguments");
%!demo 7
%! Open = [ 1292.4; 1291.7; 1291.8; 1292.2; 1291.5; 1291.0; 1291.0; 1291.5; 1291.7; 1291.5; 1290.7 ];
%! High = [ 1292.6; 1292.1; 1292.5; 1292.3; 1292.2; 1292.2; 1292.7; 1292.4; 1292.3; 1292.1; 1292.9 ];
%! Low = [ 1291.3; 1291.3; 1291.7; 1291.1; 1290.7; 1290.2; 1290.3; 1291.1; 1291.2; 1290.5; 1290.4 ];
%! Close = [ 1291.8; 1291.7; 1292.2; 1291.5; 1291.0; 1291.1; 1291.5; 1291.7; 1291.6; 1290.8; 1292.8 ];
%! datenum_vec = [ 7.3702e+05; 7.3702e+05 ;7.3702e+05; 7.3702e+05; 7.3702e+05; 7.3702e+05; 7.3702e+05; ...
%! 7.3702e+05; 7.3702e+05; 7.3702e+05; 7.3702e+05 ];
%! datevec_vec = datevec( datenum_vec );
%! graphics_toolkit('fltk'); candle( High, Low, Close, Open, 'brwk', datevec_vec, 21 );
%! title("default plot with datevec dates and integer dateform arguments");
%!demo 8
%! Open = [ 1292.4; 1291.7; 1291.8; 1292.2; 1291.5; 1291.0; 1291.0; 1291.5; 1291.7; 1291.5; 1290.7 ];
%! High = [ 1292.6; 1292.1; 1292.5; 1292.3; 1292.2; 1292.2; 1292.7; 1292.4; 1292.3; 1292.1; 1292.9 ];
%! Low = [ 1291.3; 1291.3; 1291.7; 1291.1; 1290.7; 1290.2; 1290.3; 1291.1; 1291.2; 1290.5; 1290.4 ];
%! Close = [ 1291.8; 1291.7; 1292.2; 1291.5; 1291.0; 1291.1; 1291.5; 1291.7; 1291.6; 1290.8; 1292.8 ];
%! character_dates = char ( [] );
%! for i = 1 : 11
%! character_dates = [ character_dates ; "a date" ] ;
%! endfor
%! graphics_toolkit('fltk'); candle( High, Low, Close, Open, 'brk', character_dates );
%! title("default plot with user selected black background with character dates argument");
I hope readers who are Octave users find this useful.
"Trading is statistics and time series analysis." This blog details my progress in developing a systematic trading system for use on the futures and forex markets, with discussion of the various indicators and other inputs used in the creation of the system. Also discussed are some of the issues/problems encountered during this development process. Within the blog posts there are links to other web pages that are/have been useful to me.
Tuesday, 5 December 2017
Candlestick Plotting Function Submitted for Inclusion in Octave Financial Package
I have today submitted an improved version of my basic candlestick plotting function, candle.m ( see previous post ) for inclusion in the Octave Financial package. As I am not sure when, or even if, it will be accepted, I provide a copy of it below.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment