Showing posts with label Currency Strength. Show all posts
Showing posts with label Currency Strength. Show all posts

Monday, 3 November 2025

Expressing an Indicator in Neural Net Form, Part 3.

The results of the first set of tests of optimising an indicator via the framework of training a neural net are in, and this post is a presentation of these results and a reflection on this in more general terms. I would encourage readers to look at my previous 2 posts to put this one in context.

The following chart plot shows 8 weeks of 10 minute price action in the EURUSD forex pair, with the white equity curve being the cumulative sum of open to open tick returns over this period. The green equity curve is a similar cumulative sum of the tick returns based on going long/short when the basic indicator crosses above/below its zero line. Finally, the mass of magenta coloured equity curves are those that result from various simple moving average smoothing, momentum of and smooths of momentum, and short term MACDs of the basic indicator, with the same zero line crossing positioning.  

These different magenta equity curves are expressed by setting different weights for the "decision weights" shown in the neural net diagram in the previous 2 posts, e.g. a weight matrix [ 0.25 0.25 0.25 0.25 ] is obviously a 4 bar simple moving, whilst weight matrices [ 0.25 0.25 -0.5 -0.5 ] and [ -0.25 -0.25 0.5 0.5 ] are MACDs of 2 bar fast and 4 bar slow simple moving averages (respectively a fast minus slow MACD and slow minus fast MACD - short term trend following vs. mean reversion?) In this way it is possible to create all the various smooths etc. shown above.

Taking this idea a bit further, it is possible to characterise the more "traditional" way of back testing, i.e. testing over a range of possible indicator look back periods, smooths etc. as an extremely limited or constrained form of neural net training. Consider the neural net diagram of my previous posts - a "traditional" back test is functionally equivalent to:-

  • setting the neural net architecture as shown in the above mentioned diagram, but only allowing linear hidden activation units
  • not allowing the addition of any bias units
  • fixing the input weights and output weights matrices prior to any training and not allowing any updating of these weights to take place
  • and only allowing the decision weights to be updated, and limiting this updating to a choice between a limited set of fixed weights and not allowing any error based backpropagation to take place

The next chart plot shows the out of sample result of such limited training. The white and green equity curves are the same as previously described, the cumulative sum of open to open returns and zero line crossings of the basic indicator. The single magenta coloured equity curve is the result of following this limited, "traditional" walk forward optimization 

  • choose the best set of fixed decision weights over a 2 week window. This best set is decided by choosing the equity curve with the highest Sortino ratio. For the first train/test iteration the 2 week window of training data is not shown but is that data immediately prior to the left hand edge of the plot
  • this best set is used out of sample over 1 week of data, the first week of the shown magenta equity curve
  • at the end of this first test week, roll the training window forward 1 week so that the new training data consists of the latter week of the first set of training data and the data that has just formed the test week
  • repeat the training over this new set of 2 week training data and test out of sample on the immediately following week's data
  • keep rolling the window forward, repeating the above, until the end of the test period  

The red equity curve is the equity curve that comes from the decision weights that are equivalent to a 3 bar simple moving average of the 1 bar momentum of the basic indicator, shown because this was visually identified in the first post in this series as being "the best." Comparing this equity curve with the magenta ones in the first chart it is obvious that this set of decision weights, out of sample, turns out to be probably the worst of all sets of weights. This is significant because these weights were the ones that were used to initialize the decision weights prior to neural net training.

Finally, the blue equity curve is the out of sample curve for the trained neural net, trained/tested following the same rolling methodology just described above for the magenta curve. In a hand-wavy way, the improvement in performance from the red to the blue equity curves can be said to show the benefits of optimising an indicator's performance via the framework of neural net training. This then begs the question, what if the neural net was initialised with a better set of decision weights, i.e. those of the magenta curve? This will be the subject of my next post.

More in due course. 

Wednesday, 24 September 2025

Expressing an Indicator in Neural Net form, Part 2.

Following on from my previous post, I have been experimenting with various tweaks to the basic set-up of expressing an indicator in the form of a neural net, shown again below to avoid the necessity of having readers flip between posts. 

The tweaks explored relate to the weight matrices, activation functions, targets and loss functions, the end results of which are now briefly summarized in turn.

The weight matrices are sparse and with shared weights within each separate matrix because they simply calculate the 4 indicator outputs t which represent the indicator and 3 lagged outputs of it, hence sharing the same weights. The input weights matrix is a 40 by 20 matrix with only 10 unique parameters to train/update (expressed during training by averaging across the 40 non zero weights in the matrix). Similarly, the output weights matrix only has 1 unique trainable weight. Together with bias units (not shown in the diagram above) and the 4 decision weights, the whole neural net has a total of 16 trainable weights.

The activation units are exactly as discussed in my previous post and shown in the diagram above, namely tanh on the 2 hidden layers and a sigmoid on the final output layer.

For the targets I used the sign of the slope of a sliding, centred smooth of the OHLC bar opens, on the premise that the first trade opportunity would be acted upon at the opening price following a buy/sell signal calculated on the close of a bar. The smoothing eliminates the whipsaws that would result from a single adverse return in an otherwise smooth set of returns.   

The loss is a combination of a weighted cross-entropy loss and a Sortino ratio loss. The weights for the weighted cross-entropy loss are simply the absolute values of the returns, the idea being that it is more important to get the direction right on the big returns. The Sortino loss is implemented by minimizing the negative of the Sortino ratio, i.e. maximizing the ratio. There are 2 versions of this Sortino loss: an analytical gradient that spreads the cost over each training sample per training epoch similar to the cross-entropy loss, and a global loss which uses finite differences over the 4 output decision weights. Both of these Sortino losses were coded with the help of deepseek-talkai. The losses are mixed via a mixing parameter, Lambda, which varies from 0 to 1, with 0 being a pure cross-entropy loss and 1 being pure Sortino loss. 

The following .gif shows the training data equity curves for each type of loss (see the titles of each plot) for every hundredth epoch up to epoch 600 over a week's worth of 10 minute forex data limited to the trading hours described in my previous post. The legend in the top left corner identifies each equity curve.

Obviously these plots show that it is possible to improve results over those of the original indicator (shown in red in the above .gif), but of course these are in-sample results. My next post will be about cross validation and out-of sample test results.
 

Wednesday, 3 September 2025

Expressing an Indicator in Neural Net Form

Recently I started investigating relative rotation graphs with a view to perhaps implementing a version of this for use on forex currency pairs. The underlying idea of a relative rotation graph is to plot an asset's relative strength compared to a benchmark and the momentum of this relative strength and to plot this in a form similar to a Polar coordinate system plot, which rotates around a zero point representing zero relative strength and zero relative strength momentum. After some thought it occurred to me that rather than using relative strength against a benchmark I could use the underlying relative strengths of the individual currencies, as calculated by my currency strength indicator, against each other. Furthermore, these underlying log changes in the individual currencies can be normalised using the ideas of brownian motion and then averaged together over different look back periods to create a unique indicator.

This indicator was coded up in the "traditional" way that will be familiar to anybody who has ever tried coding a trading indicator in any coding language. However, I thought that if the calculation of this indicator could be expressed in the form of a Feedforward neural network then the optimisation opportunities available using backpropagation and regularization could be used to tweek the indicator in more useful ways than just varying a look back length and amount of averaging. After some work I was able to make this work in just these two lines of Octave code:

indicator_middle_layer = tanh( full_feature_matrix * input_weights ) ;
indicator_nn_output = tanh( indicator_middle_layer * output_weights ) ; 

Of course, prior to calling these two lines of code, there is some feature engineering to create the input full_feature_matrix, and the input weights and output_weights matrices taken together are mathematically equivalent to the original indicator calculations. Finally, because this is a neural net expression of the indicator, the non-linear tanh activation function is applied to the hidden middle and output layers of the net.

The following plot shows the original indicator in black and the neural net version of it in blue 

over the data shown in this plot of 10 minute bars of the EURUSD forex pair. 

The red indicator in the plot above is the 1 bar momentum of the neural net indicator plot.

To judge the quality of this indicator I used the entropy measure (measured over 14,000+ 10 minute bars of EURUSD), the results of which are shown next.

entropy_indicator_original = 0.9485
entropy_indicator_nn_output = 0.9933

An entropy reading of 0.9933 is probably as good as any trading indicator could hope to achieve (a perfect reading is 1.0) and so the next thing was to quickly back test the indicator performance. Based on the logic of the indicator the obvious long (short) signals are being above (below) the zeroline, or equivalently the sign of the indicator, and for good measure I also tested the sign of the momentum and some simple moving averages thereof.

The following plot shows the equity curves of this quick test where it is visually clear that the blue equity curves are "the best" when plotted in relation to the black "buy and hold" equivalent equity curve. These represent the equity curves of a 3 bar simple moving average of the 1 bar momentum of both the original formulation of the indicator and the neural net implementation. I would point out that these equity curves represent the theoretical equity resulting from trading the London session after 9:00am BST and stopping at 7:00am EST (usually about noon BST) and then starting trading again at 9:00am EST until 17:00pm EST. This schedule avoids the opening sessions (7:00 to 9:00am) in both London and New York because, from my observations of many OHLC charts such as shown above, there are frequently wild swings where price is being pushed to significant points such as volume profile clusters, accumulations of buy/sell orders etc. and in my opinion no indicator can be expected to perform well in that sort of environment. Also avoided are the hours prior to 7:00am BST, i.e. the Asian session or overnight session.    

Although these equity curves might not, at first glance, be that impressive, especially as they do not account for trading costs etc. my intent on doing these tests was to determine the configuration of a final "decision" output layer to be added to the neural net implementation of the indicator. A 3 bar simple moving average of the 1 bar momentum implies the necessity to include 4 consecutive readings of the indicator output as input to a final " decision" layer. The following simple, hand-drawn sketch shows what I mean:
A discussion of this will be the subject of my next post.

Friday, 19 April 2024

Trading Forex News

This post, as the title suggests, is about trading forex news releases and, incidently, is a small update to the appearances of my PositionBook chart and OrderLevels chart.

I recently came across this forexfactory post which shows how to download the underlying data for the forexfactory calendar, a screenshot of which is shown immediately below, and I thought I would look into the idea of trading around forex news

releases. If you look carefully at the screenshot you will see that at 2.30pm (CET) there was a high impact (red folder icon) news release regarding the Canadian dollar (CAD) which came in under expectations. The following OrderLevels chart
and PositionBook chart
clearly show the big move that immediately followed this news release (CAD weakness). The OrderLevels chart also shows the accumulation of sell orders (red background colour) that would have been an almost perfect take profit level for the day, whilst the PositionBook chart shows the accumulation of long positions (blue background colour) during the sideways movement that preceded the news release. These two charts both show the above mentioned appearance update resulting from the use of the b2r colormap function.
 
The following "overview" chart shows that the big move in the USDCAD forex pair was
definitely the result of this CAD weakness rather than USD strength (see the two rightmost currency strength charts).

Having finally "scratched the itch" of getting my PositionBook charts sorted out, my next project is to investigate the possibility of creating a forex news release trading methodology.
 
More in due course.

Sunday, 20 August 2023

Currency Strength Revisited

Recently I responded to a Quantitative Finance forum question here, where I invited the questioner to peruse certain posts on this blog. Apparently the posts do not provide enough information to fully answer the question (my bad) and therefore this post provides what I think will suffice as a full and complete reply, although perhaps not scientifically rigorous.

The original question asked was "Is it possible to separate or decouple the two currencies in a trading pair?" and I believe what I have previously described as a "currency strength indicator" does precisely this (blog search term ---> https://dekalogblog.blogspot.com/search?q=currency+strength+indicator). This post outlines the rationale behind my approach.

Take, for example, the GBPUSD forex pair, and further give it a current (imaginary) value of 1.2500. What does this mean? Of course it means 1 GBP will currently buy you 1.25 USD, or alternatively 1 USD will buy you 1/1.25 = 0.8 GBP. Now rather than write GBPUSD let's express GBPUSD as a ratio thus:- GBP/USD, which expresses the idea of "how many USD are there in a GBP?" in the same way that 9/3 shows how many 3s there are in 9. Now let's imagine at some time period later there is a new pair value, a lower case "gbp/usd" where we can write the relationship

                    (1)     ( GBP / USD ) * ( G / U ) = gbp / usd

to show the change over the time period in question. The ( G / U ) term is a multiplicative term to show the change in value from old GBP/USD 1.2500 to say new value gbp/usd of 1.2600, 

e.g.                ( G / U ) == ( gbp / usd ) / ( GBP / USD ) == 1.26 / 1.25 == 1.008

from which it is clear that the forex pair has increased by 0.8% in value over this time period. Now, if we imagine that over this time period the underlying, real value of USD has remained unchanged this is equivalent to setting the value U in ( G / U ) to exactly 1, thereby implying that the 0.8% increase in the forex pair value is entirely attributable to a 0.8% increase in the underlying, real value of GBP, i.e. G == 1.008. Alternatively, we can assume that the value of GBP remains unchanged,

 e.g.                G == 1, which means that U == 1 / 1.008 == 0.9921

which implies that a ( 1 - 0.9921 ) == 0.79% decrease in USD value is responsible for the 0.8% increase in the pair quote.

Of course, given only equation (1) it is impossible to solve for G and U as either can be arbitrarily set to any number greater than zero and then be compensated for by setting the other number such that the constant ( G / U ) will match the required constant to account for the change in the pair value.

However, now let's introduce two other forex pairs (2) and (3) and thus we have:-

                    (1)     ( GBP / USD ) * ( G / U ) = gbp / usd

                    (2)     ( EUR / USD ) * ( E / U ) = eur / usd

                    (3)     ( EUR / GBP ) * ( E / G ) = eur / gbp

We now have three equations and three unknowns, namely G, E and U, and so this system of equations could be laboriously, mathematically solved by substitution. 

However, in my currency strength indicator I have taken a different approach. Instead of solving mathematically I have written an error function which takes as arguments a list of G, E, U, ... etc. for all currency multipliers relevant to all the forex quotes I have access to, approximately 47 various crosses which themselves are inputs to the error function, and this function is supplied to Octave's fminunc function to simultaneously solve for all G, E, U, ... etc. given all forex market quotes. The initial starting values for all G, E, U, ... etc. are 1, implying no change in values across the market. These starting values consistently converge to the same final values for G, E, U, ... etc for each separate period's optimisation iterations.

Having got all G, E, U, ... etc. what can be done? Well, taking G for example, we can write

                    (4)     GBP * G = gbp

for the underlying, real change in the value of GBP. Dividing each side of (4) by GBP and taking logs we get

                    (5)     log( G ) = log( gbp / GBP )

i.e. the log of the fminunc returned value for the multiplicative constant G is the equivalent of the log return of GBP independent of all other currencies, or as the original forum question asked, the (change in) value of GBP separated or decoupled the from the pair in which it is quoted.

Of course, having the individual log returns of separated or decoupled currencies, there are many things that can be done with them, such as:-

  • create indices for each currency
  • apply technical analysis to these separate indices
  • intermarket currency analysis
  • input to machine learning (ML) models
  • possibly create new and unique currency indicators

Examples of the creation of "alternative price charts" and indices are shown below

where the black line is the actual 10 minute closing prices of GBPUSD over the last week (13th to 18th August) with the corresponding GBP price (blue line) being the "alternative" GBPUSD chart if U is held at 1 in the ( G / U ) term and G allowed to be its derived, optimised value, and the USD price (red line) being the alternative chart if G is held at 1 and U allowed to be its derived, optimised value.

This second chart shows a more "traditional" index like chart

where the starting values are 1 and both the G and U values take their derived values. As can be seen, over the week there was upwards momentum in both the GBP and USD, with the greater momentum being in the GBP resulting in a higher GBPUSD quote at the end of the week. If, in the second chart the blue GBP line had been flat at a value of 1 all week, the upwards momentum in USD would have resulted in a lower week ending quoted value of GBPUSD, as seen in the red USD line in the first chart. Having access to these real, decoupled returns allows one to see through the given, quoted forex prices in the manner of viewing the market as though through X-ray vision. 

I hope readers find this post enlightening, and if you find some other uses for this idea, I would be interested in hearing how you use it.
 

Friday, 5 February 2021

A Forex Pair Snapshot Chart

After yesterday's Heatmap Plot of Forex Temporal Clustering post I thought I would consolidate all the chart types I have recently created into one easy, snapshot overview type of chart. Below is a typical example of such a chart, this being today's 10 minute EUR_USD forex pair chart up to a few hours after the London session close (the red vertical line).


The top left chart is a Market/Volume Profile Chart with added rolling Value Area upper and lower bounds (the cyan, red and white lines) and also rolling Volume Weighted Average Price with upper and lower standard deviation lines (magenta).

The bottom left chart is the turning point heatmap chart as described in yesterday's post.

The two rightmost charts are also Market/Volume Profile charts, but of my Currency Strength Candlestick Charts based on my Currency Strength Indicator. The upper one is the base currency, i.e. EUR, and the lower is the quote currency. 

The following charts are the same day's charts for:

GBP_USD,

USD_CHF
and finally USD_JPY
The regularity of the turning points is easily seen in the lower lefthand charts although, of course, this is to be expected as they all share the USD as a common currency. However, there are also subtle differences to be seen in the "shadows" of the lighter areas.

For the nearest future my self-assigned task will be to observe the forex pairs, in real time, through the prism of the above style of chart and do some mental paper trading, and perhaps some really small size, discretionary live trading, in additional to my normal routine of research and development.


Saturday, 15 August 2020

Candlestick Pattern Scanner Functions

Since my last currency strength candlestick chart post it seemed to make sense to be able to scan said charts for signals, so below is the code for two Octave functions which act as candlestick pattern scanners. The code is fully vectorised and self-contained, and on my machine they can scan more than 300,000 OHLC bars for 27/29 separate patterns in less than 0.5 seconds. Both functions have a self-explanatory help and within the body of the code there are ample comments which describe the patterns. Enjoy!

Bullish Reversal Indicator

## Copyright (C) 2020 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 {} {@var{bullish_signal_matrix} =} candle_bullish_reversal (@var{high}, @var{low}, @var{close}, @var{open}, @var{downtrend}, @var{avge_hi_lo_range})
##
## The HIGH, LOW, CLOSE and OPEN should be vectors of equal length, and are required.
##
## Internally the DOWNTREND is determined as a bar's CLOSE being lower than the CLOSE
## of the bar 5 bars previously. This can be over-ruled by an optional, user supplied
## vector DOWNTREND of zeros and ones, where one(s) represent a bar in a DOWNTREND.
## If a DOWNTREND vector consists solely of ones, this effectively turns off the
## discriminative ability of the DOWNTREND condition as all bars will be considered
## to be DOWNTREND bars. If the DOWNTREND vector consists solely of zeros, no bar
## will be classified as being in a DOWNTREND and those candle patterns that require a 
## DOWNTREND as a condition will not be indicated.
##
## The AVGE_HI_LO_RANGE is calculated as a rolling 5 bar simple moving average of
## the HI-LO range of bars. A bar is considered a "long line" if its HI-LO range
## is greater than the AVGE_HI_LO_RANGE. This can also be over-ruled, as above, by
## an optional, user supplied vector AVGE_HI_LO_RANGE, which consists of zeros and
## ones, with ones representing "long line" bars. An AVGE_HI_LO_RANGE vector of all
## ones or zeros will have a similar effect as described for the DOWNTREND vector.
##
## The candlestick pattern descriptions are predominantly taken from
##
## https://www.candlescanner.com
##
## and the bullish only patterns implemented are:
##
## Bullish Reversal High Reliability
##
##     01 - Bullish Abandoned Baby
##
##     02 - Concealing Baby Swallow
##
##     03 - Kicking
##
##     04 - Morning Doji Star
##
##     05 - Morning Star
##
##     06 - Piercing Line
##
##     07 - Three Inside Up
##
##     08 - Three Outside Up
##
##     09 - Three White Soldiers
##
## Bullish Reversal Moderate Reliability
##
##     10 - Breakaway
##
##     11 - Counter Attack
##
##     12 - Doji Star
##
##     13 - Dragonfly Doji
##
##     14 - Engulfing
##
##     15 - Gravestone Doji
##
##     16 - Harami Cross
##
##     17 - Homing Pigeon
##
##     18 - Ladder Bottom
##
##     19 - Long Legged Doji
##
##     20 - Matching Low
##
##     21 - Meeting Lines
##
##     22 - Stick Sandwich
##
##     23 - Three Stars in the South
##
##     24 - Tri Star
##
##     25 - Unique Three River Bottom
##
## Bullish Reversal Low Reliability
##
##     26 - Belt Hold
##
##     27 - Hammer
##
##     28 - Harami
##
##     29 - Inverted Hammer
##
## The output BULLISH_SIGNAL_MATRIX has a row length the same as the OHLC
## input vectors and 29 columns. The matrix is a zero filled matrix with the
## value 1 when a candle pattern is indicated. The row ix of the value is the
## row ix of the last candle in the pattern. The column ix corresponds to the
## pattern index given above.
##
## @seealso{candle_bearish_reversal}
## @end deftypefn

## Author: dekalog 
## Created: 2020-08-10

function bullish_signal_matrix = candle_bullish_reversal ( varargin )
 
if ( nargin < 4 || nargin > 6 )
  print_usage () ;
elseif ( nargin == 4 )
  high = varargin{1} ; low = varargin{2} ; close = varargin{3} ; open = varargin{4} ;
  downtrend = close < shift( close , 5 ) ;
  avge_hi_lo_range = filter( [0.2 ; 0.2 ; 0.2 ; 0.2 ; 0.2 ] , 1 , high .- low ) ; ## 5 bar simple moving average
elseif ( nargin == 5 )
  high = varargin{1} ; low = varargin{2} ; close = varargin{3} ; open = varargin{4} ;
  downtrend = varargin{5} ;
  avge_hi_lo_range = filter( [0.2 ; 0.2 ; 0.2 ; 0.2 ; 0.2 ] , 1 , high .- low ) ; ## 5 bar simple moving average
elseif ( nargin == 6 )
  high = varargin{1} ; low = varargin{2} ; close = varargin{3} ; open = varargin{4} ;
  downtrend = varargin{5} ; avge_hi_lo_range = varargin{6} ;
endif
 
bullish_signal_matrix = zeros( size( close , 1 ) , 29 ) ;

## pre-compute some basic vectors for re-use below, trading memory use for
## speed of execution
downtrend_1 = shift( downtrend , 1 ) ;
downtrend_2 = shift( downtrend , 2 ) ;
downtrend_3 = shift( downtrend , 3 ) ;
long_line = ( high .- low ) > shift( avge_hi_lo_range , 1 ) ;
long_line_1 = shift( long_line , 1 ) ;
long_line_2 = shift( long_line , 2 ) ;
long_line_3 = shift( long_line , 3 ) ;
long_line_4 = shift( long_line , 4 ) ;
open_1 = shift( open , 1 ) ;
open_2 = shift( open , 2 ) ;
open_3 = shift( open , 3 ) ;
open_4 = shift( open , 4 ) ;
open_5 = shift( open , 5 ) ;
high_1 = shift( high , 1 ) ;
high_3 = shift( high , 3 ) ;
low_1 = shift( low ,1 ) ;
low_2 = shift( low ,2 ) ;
low_3 = shift( low , 3 ) ;
close_1 = shift( close , 1 ) ;
close_2 = shift( close , 2 ) ;
close_3 = shift( close , 3 ) ;
close_4 = shift( close , 4 ) ;
black_body = close < open ;
black_body_1 = shift( black_body , 1 ) ;
black_body_2 = shift( black_body , 2 ) ;
black_body_3 = shift( black_body , 3 ) ;
black_body_4 = shift( black_body , 4 ) ;
white_body = close > open ;
white_body_1 = shift( white_body , 1 ) ;
white_body_2 = shift( white_body , 2 ) ;
doji = ( close == open ) ;
doji_1 = shift( doji , 1 ) ;
doji_2 = shift( doji , 2 ) ;
body_high = max( [ open , close ] , [] , 2 ) ;
body_high_1 = shift( body_high , 1 ) ;
body_high_2 = shift( body_high , 2 ) ;
body_low = min( [ open , close ] , [] , 2 ) ;
body_low_1 = shift( body_low , 1 ) ;
body_low_2 = shift( body_low , 2 ) ;
body_range = body_high .- body_low ;
body_range_1 = shift( body_range , 1 ) ;
body_range_2 = shift( body_range , 2 ) ;
body_midpoint = ( body_high .+ body_low ) ./ 2 ;
body_midpoint_1 = shift( body_midpoint , 1 ) ;
body_midpoint_2 = shift( body_midpoint , 2 ) ;
wick_gap_up = ( low > high_1 ) ;
wick_gap_down = ( high < low_1 ) ;
wick_gap_down_1 = shift( wick_gap_down , 1 ) ;
black_marubozu = ( high == open ) .* ( low == close ) ;
black_marubozu_1 = shift( black_marubozu , 1 ) ;
black_marubozu_2 = shift( black_marubozu , 2 ) ;
black_marubozu_3 = shift( black_marubozu , 3 ) ;
white_marubozu = ( high == close ) .* ( low == open ) ;

## 01 - Bullish Abandoned Baby
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     a doji candle
##     the high price below the prior low price
## Third candle
##     white body
##     the low price above the prior high price
bullish_signal_matrix(:,1) = downtrend_2 .* black_body_2 .* ...
                              doji_1 .* wick_gap_down_1 .* ...
                              white_body .* wick_gap_up ; 
## 02 - Concealing Baby Swallow
## First candle
##     a Black Marubozu candle in a downtrend
## Second candle
##     a Black Marubozu candle
##     candle opens within the prior candle's body
##     candle closes below the prior closing price
## Third candle
##     a High Wave basic candle with no lower shadow
##     candle opens below the prior closing price
##     upper shadow enters the prior candle's body
## Fourth candle
##     black body
##     candle’s body engulfs the prior candle’s body including the shadows
bullish_signal_matrix(:,2) = downtrend_3 .* black_marubozu_3 .* ...
                              black_marubozu_2 .* (open_2 < high_3) .* (open_2 > low_3) .* (close_2 < close_3) .* ...
                              (high_1 .- body_high_1 >= 3.*body_range_1) .* (low_1 == body_low_1) .* (open_1 < close_2) .* (high_1 > body_low_2) .* ...
                              black_body .* (open > high_1) .* (close < low_1) ;
## 03 - Kicking
## First candle
##     a Black Marubozu
##     appears on as a long line
## Second candle
##     a White Marubozu
##     price gaps upward
##     appears on as a long line
bullish_signal_matrix(:,3) = black_marubozu_1 .* long_line_1 .* ...
                              white_marubozu .* (low > high_1) .* long_line ;
## 04 - Morning Doji Star
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     a doji candle
##     a doji body below the previous candle body
##     the high price above the previous candle low price
## Third candle
##     white body
##     candle body above the previous candle body
##     the closing price above the midpoint of the first candle body
bullish_signal_matrix(:,4) = downtrend_2 .* black_body_2 .* ...
                              doji_1 .* (open_1 < body_low_2) .* (high_1 > low_2) .* ...
                              white_body .* (body_low > body_high_1) .* (close > body_midpoint_2) ;
## 05 - Morning Star
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     white or black body
##     the candle body is located below the prior body
## Third candle
##     white body
##     the candle body is located above the prior body
##     the candle closes at least halfway up the body of the first line
bullish_signal_matrix(:,5) = downtrend_2 .* black_body_2 .* ...
                              (body_high_1 < body_low_2) .* ...
                              white_body .* (body_low > body_high_1) .* (close >= body_midpoint_2) ;
## 06 - Piercing Line
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     white body
##     the opening below or equal of the prior low
##     the closing above the midpoint of the prior candle's body
##     the closing below the previous opening
bullish_signal_matrix(:,6) = downtrend_1 .* black_body_1 .* ...
                              white_body .* (open <= low_1) .* (close > body_midpoint_1) .* (close < open_1) ;
## 07 - Three Inside Up
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     white body
##     the candle body is engulfed by the prior candle body
## Third candle
##     the closing price is above the previous closing price
bullish_signal_matrix(:,7) = downtrend_2 .* black_body_2 .* ...
                              white_body_1 .* (body_high_1 < body_high_2) .* (body_low_1 > body_low_2) .* ...
                              (close > close_1) ;
## 08 - Three Outside Up
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     white body
##     candle’s body engulfs the prior (black) candle’s body
## Third candle
##     closing price above the previous closing price
##     white body
bullish_signal_matrix(:,8) = downtrend_2 .* black_body_2 .* ...
                              white_body_1 .* (body_high_1 > body_high_2) .* (body_low_1 < body_low_2) .* ...
                              (close > close_1) .* white_body ;
## 09 - Three White Soldiers
## First candle
##     a candle in a downtrend
##     white body
## Second candle
##     white body
##     the opening price within the previous body
##     the closing price above the previous closing price
## Third candle
##     white body
##     the opening price within the previous body
##     the closing price above the previous closing price
bullish_signal_matrix(:,9) = downtrend_2 .* white_body_2 .* ...
                              white_body_1 .* (open_1 < body_high_2) .* (open_1 > body_low_2) .* (close_1 > close_2) .* ...
                              white_body .* (open < body_high_1) .* (open > body_low_1) .* (close > close_1) ; 
## 10 - Breakaway
## First candle
##     a tall black candle
## Second candle
##     a black candle
##     candle opens below the previous closing price (downward price gap, shadows can overlap)
## Third candle
##     a white or black candle
##     candle opens below the previous opening price
## Fourth candle
##     a black candle
##     candle closes below the previous closing price
## Fifth candle
##     a tall white candle
##     candle opens above the previous closing price
##     candle closes above the second line's opening price and below the first line's opening price
bullish_signal_matrix(:,10) = black_body_4 .* long_line_4 .*...
                               black_body_3 .* (open_3 < close_4) .* ...
                               (open_2 < open_3) .* ...
                               black_body_1 .* (close_1 < close_2) .* ...
                               white_body .* long_line .* (open > close_1) .* (close > open_3) .* (close < open_4) ; 
## 11 - Counter Attack
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     white body
##     the opening price is lower than the previous closing price
##     the closing price is at or higher than the previous closing price
bullish_signal_matrix(:,11) = downtrend_1 .* black_body_1 .* ...
                               white_body .* (open < close_1) .* (close >= close_1) ;
## 12 - Doji Star
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     a doji candle
##     a body below the first candle's body
bullish_signal_matrix(:,12) = downtrend_1 .* black_body_1 .* ...
                               doji .* (open < body_low_1) ; 
## 13 - Dragonfly Doji
##     Opening, closing and maximum prices are the same or very similar
##     Long lower shadow
##     appears on as a long line
bullish_signal_matrix(:,13) = (open == close) .* (close == high) .* long_line ;

## 14 - Engulfing
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     white body
##     candle's body engulfs the prior (black) candle's body
bullish_signal_matrix(:,14) = downtrend_1 .* black_body_1 .* ...
                               white_body .* (body_high > body_high_1) .* (body_low < body_low_1) ; 
## 15 - Gravestone Doji
##     Opening, closing and minimum prices are the same or very similar
##     Long upper shadow
##     appears on as a long line
bullish_signal_matrix(:,15) = (open == close) .* (close == low) .* long_line ; 

## 16 - Harami Cross
## First candle
##     a candle in a downtrend
##     black body
##     appears on as a long line
## Second candle
##     a doji candle with two shadows
##     the candle (including shadows) is engulfed by the previous candle's body
bullish_signal_matrix(:,16) = downtrend_1 .* black_body_1 .* long_line_1 .* ...
                               doji .* (high > close) .* (low < close) .* (high < body_high_1) .* (low > body_low_1) ;
## 17 - Homing Pigeon
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     black body
##     candle’s body engulfed by the prior candle’s body
bullish_signal_matrix(:,17) = downtrend_1 .* black_body_1 .* ...
                               black_body .* (body_high < body_high_1) .* (body_low > body_low_1) ;
## 18 - Ladder Bottom
## First candle
##     a tall black candle
## Second candle
##     a tall black candle
##     candle opens below the previous opening price
## Third candle
##     a tall black candle
##     candle opens below the previous opening price
## Fourth candle
##     a black candle
##     candle closes below the previous closing price
##     candle has a long upper shadow
## Fifth candle
##     a tall white candle
##     candle opens above the previous opening price
##     candle closes at or above the third line's opening price and below the first line's opening price
bullish_signal_matrix(:,18) = black_body_4 .* long_line_4 .* ...
                               black_body_3 .* (open_3 < open_4) .* long_line_3 .* ...
                               black_body_2 .* (open_2 < open_3) .* long_line_2 .* ...
                               black_body_1 .* (close_1 < close_2) .* ((high_1 .- body_high_1) > body_range_1) .* ...
                               white_body .* (open > open_1) .* (close >= open_2) .* (close < open_5) ; 
## 19 - Long Legged Doji
##     a doji candle
##     opening and closing prices are the same or similar
##     upper and lower shadow are very long
##     body is located in the middle of the candle or nearly mid-range
##     appears on as a long line
bullish_signal_matrix(:,19) = doji .* ((high .- close) > 0) .* ((close .- low) > 0) .* long_line ;

## 20 - Matching Low
## First candle
##     a candle in a downtrend
##     black body
##     no lower shadow
##     appears as a long line
## Second candle
##     black body
##     the opening price is below the previous opening price
##     the closing price is at the level of the previous closing price
##     no lower shadow
bullish_signal_matrix(:,20) = downtrend_1 .* black_body_1 .* (close_1 == low_1) .* long_line_1 .* ...
                               black_body .* (open < open_1) .* (close == close_1) .* (close == low) ;
## 21 - Meeting Lines
## First candle
##     a candle in a downtrend
##     black body
##     appears as a long line
## Second candle
##     white body
##     the closing price is equal to the previous closing price
bullish_signal_matrix(:,21) = downtrend_1 .* black_body_1 .* long_line_1 .* ...
                               white_body .* (close == close_1) ;
## 22 - Stick Sandwich
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     white body
##     the opening price is higher than the previous closing price
##     the closing price is higher than the previous opening price
## Third candle
##     black_body
##     the opening price is higher than the previous closing price
##     the closing price is equal to or higher than first line close
bullish_signal_matrix(:,22) = downtrend_2 .* black_body_2 .* ...
                               white_body_1 .* (open_1 > close_2) .* (close_1 > open_2) .* ...
                               black_body .* (open > close_1) .* (close >= close_2) ;
## 23 - Three Stars in the South
## First candle
##     a candle in a downtrend
##     black body
##     long lower shadow
## Second candle
##     black body
##     the opening below the prior opening
##     the closing below or at the prior closing
##     the low above the prior low
## Third candle
##     a marubozu candle with black body
##     appears as a short line
##     a candle is located within the prior candle
bullish_signal_matrix(:,23) = downtrend_2 .* black_body_2 .* ((body_low_2 .- low_2) > body_range_2) .* ...
                               black_body_1 .* (open_1 < open_2) .* (close_1 <= close_2) .* (low_1 > low_2) .* ...
                               black_marubozu .* (high < high_1) .* (low > low_1) ;
## 24 - Tri Star
## First candle
##     a doji candle in a downtrend
## Second candle
##     a doji candle
##     a body below the prior body
## Third candle
##     a doji candle
##     a body above the prior body
bullish_signal_matrix(:,24) = downtrend_2 .* doji_2 .* ...
                               doji_1 .* (open_1 < close_2) .* ...
                               doji .* (open > close_1) ;
## 25 - Unique Three River Bottom
## First candle
##     black candle
##     a candle in a downtrend
## Second candle
##     black candle
##     a body within the prior body
##     the lower shadow is at least twice longer than the body
##     the low price below the prior low price
## Third candle
##     white candle
##     a body located below the prior body
##     the low price above the prior low price
bullish_signal_matrix(:,25) = downtrend_2 .* black_body_2 .* ...
                               black_body_1 .* (body_high_1 < body_high_2) .* (body_low_1 > body_low_2) .* ((body_low_1 .- low_1) >= body_range_1) .* (low_1 < low_2) .* ...
                               white_body .* (body_high < body_low_1) .* (low > low_1) ;
## 26 - Belt Hold
##     white body
##     no lower shadow
##     short upper shadow
##     appears as a long line
bullish_signal_matrix(:,26) = downtrend .* white_body .* (open == low) .* long_line ;

## 27 - Hammer
##     downtrend
##     white or black candle with a small body
##     no upper shadow or the shadow cannot be longer than the body
##     lower shadow between two to three times longer than the body
##     if the gap is created at the opening or at the closing, it makes the signal stronger
##     appears as a long line
bullish_signal_matrix(:,27) = downtrend .* (doji == 0) .* ((high .- body_high) < body_range) .* ((body_low .- low) > 2.*body_range) .* long_line ;

## 28 - Harami
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     white body
##     candle's body engulfed by the prior (black) candle's body
bullish_signal_matrix(:,28) = downtrend_1 .* black_body_1 .* ...
                               white_body .* (body_high < body_high_1) .* (body_low > body_low_1) ;
## 29 - Inverted Hammer 
## First candle
##     a candle in a downtrend
##     black body
## Second candle
##     a white or black candle with a small body
##     no lower shadow or the shadow cannot be longer than then body
##     upper shadow at least 2.5 times longer than the body
##     the open below or at the level of the previous candle's close
bullish_signal_matrix(:,29) = downtrend_1 .* black_body_1 .* ...
                               (doji == 0 ) .* ((body_low .- low) < body_range) .* ((high .- body_high) >= 2.5.*body_range) .* (open <= close_1) ;
                               
bullish_signal_matrix( 1 : 10 , : ) = 0 ;

endfunction

and Bearish Reversal Indicator
## Copyright (C) 2020 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 {} {@var{bearish_signal_matrix} =} candle_bearish_reversal (@var{high}, @var{low}, @var{close}, @var{open}, @var{uptrend}, @var{avge_hi_lo_range})
##
## The HIGH, LOW, CLOSE and OPEN should be vectors of equal length, and are required.
##
## Internally the UPTREND is determined as a bar's CLOSE being higher than the CLOSE
## of the bar 5 bars previously. This can be over-ruled by an optional, user supplied
## vector UPTREND of zeros and ones, where one(s) represent a bar in an UPTREND.
## If an UPTREND vector consists solely of ones, this effectively turns off the
## discriminative ability of the UPTREND condition as all bars will be considered
## to be UPTREND bars. If the UPTREND vector consists solely of zeros, no bar
## will be classified as being in an UPTREND and those candle patterns that require an 
## UPTREND as a condition will not be indicated.
##
## The AVGE_HI_LO_RANGE is calculated as a rolling 5 bar simple moving average of
## the HI-LO range of bars. A bar is considered a "long line" if its HI-LO range
## is greater than the AVGE_HI_LO_RANGE. This can also be over-ruled, as above, by
## an optional, user supplied vector AVGE_HI_LO_RANGE, which consists of zeros and
## ones, with ones representing "long line" bars. An AVGE_HI_LO_RANGE vector of all
## ones or zeros will have a similar effect as described for the UPTREND vector.
##
## The candlestick pattern descriptions are predominantly taken from
##
## https://www.candlescanner.com
##
## and the bearish only patterns implemented are:
##
## Bearish Reversal High Reliability
##
##     01 - Bearish Abandoned Baby
##
##     02 - Dark Cloud Cover
##
##     03 - Evening Doji Star
##
##     04 - Evening Star
##
##     05 - Kicking
##
##     06 - Three Black Crows
##
##     07 - Three Inside Down
##
##     08 - Three Outside Down
##
##     09 - Upside Gap Two Crows
##
## Bearish Reversal Moderate Reliability
##
##     10 - Advance Block
##
##     11 - Breakaway
##
##     12 - Counter Attack
##
##     13 - Deliberation
##
##     14 - Doji Star
##
##     15 - Dragonfly Doji
##
##     16 - Engulfing
##
##     17 - Gravestone Doji
##
##     18 - Harami Cross
##
##     19 - Identical Three Crows
##
##     20 - Long Legged Doji
##
##     21 - Meeting Lines
##
##     22 - Tri Star
##
##     23 - Two Crows
##
## Bearish Reversal Low Reliability
##
##     24 - Belt Hold 
##
##     25 - Hanging Man
##
##     26 - Harami
##
##     27 - Shooting Star
##
## The output BEARISH_SIGNAL_MATRIX has a row length the same as the OHLC
## input vectors and 27 columns. The matrix is a zero filled matrix with the
## value 1 when a candle pattern is indicated. The row ix of the value is the
## row ix of the last candle in the pattern. The column ix corresponds to the
## pattern index given above.
##
## @seealso{candle_bullish_reversal}
## @end deftypefn

## Author: dekalog 
## Created: 2020-08-10

function bearish_signal_matrix = candle_bearish_reversal ( varargin )

if ( nargin < 4 || nargin > 6 )
  print_usage () ;
elseif ( nargin == 4 )
  high = varargin{1} ; low = varargin{2} ; close = varargin{3} ; open = varargin{4} ;
  uptrend = close > shift( close , 5 ) ;
  avge_hi_lo_range = filter( [0.2 ; 0.2 ; 0.2 ; 0.2 ; 0.2 ] , 1 , high .- low ) ; ## 5 bar simple moving average
elseif ( nargin == 5 )
  high = varargin{1} ; low = varargin{2} ; close = varargin{3} ; open = varargin{4} ;
  uptrend = varargin{5} ;
  avge_hi_lo_range = filter( [0.2 ; 0.2 ; 0.2 ; 0.2 ; 0.2 ] , 1 , high .- low ) ; ## 5 bar simple moving average
elseif ( nargin == 6 )
  high = varargin{1} ; low = varargin{2} ; close = varargin{3} ; open = varargin{4} ;
  uptrend = varargin{5} ; avge_hi_lo_range = varargin{6} ;
endif

bearish_signal_matrix = zeros( size( close , 1 ) , 27 ) ;

## pre-compute some basic vectors for re-use below, trading memory use for
## speed of execution
uptrend_1 = shift( uptrend , 1 ) ;
uptrend_2 = shift( uptrend , 2 ) ;
long_line = ( high .- low ) > shift( avge_hi_lo_range , 1 ) ;
long_line_1 = shift( long_line , 1 ) ;
long_line_4 = shift( long_line , 4 ) ;
open_1 = shift( open , 1 ) ;
open_2 = shift( open , 2 ) ;
open_3 = shift( open , 3 ) ;
high_1 = shift( high , 1 ) ;
high_2 = shift( high , 2 ) ;
high_4 = shift( high , 4 ) ;
low_1 = shift( low , 1 ) ;
close_1 = shift( close , 1 ) ;
close_2 = shift( close , 2 ) ;
close_4 = shift( close , 4 ) ;
black_body = close < open ;
black_body_1 = shift( black_body , 1 ) ;
black_body_2 = shift( black_body , 2 ) ;
white_body = close > open ;
white_body_1 = shift( white_body , 1 ) ;
white_body_2 = shift( white_body , 2 ) ;
white_body_3 = shift( white_body , 3 ) ;
white_body_4 = shift( white_body , 4 ) ;
doji = ( close == open ) ;
doji_1 = shift( doji , 1 ) ;
doji_2 = shift( doji , 2 ) ;
body_high = max( [ open , close ] , [] , 2 ) ;
body_high_1 = shift( body_high , 1 ) ;
body_high_2 = shift( body_high , 2 ) ;
body_low = min( [ open , close ] , [] , 2 ) ;
body_low_1 = shift( body_low , 1 ) ;
body_low_2 = shift( body_low , 2 ) ;
body_midpoint = ( body_high .+ body_low ) ./ 2 ;
body_midpoint_1 = shift( body_midpoint , 1 ) ;
body_midpoint_2 = shift( body_midpoint , 2 ) ;
candle_midpoint = ( high .+ low ) ./ 2 ;
candle_midpoint_1 = shift( candle_midpoint , 1 ) ; 
body_range = body_high .- body_low ;
wick_gap_up = low > shift( high , 1 ) ;
wick_gap_up_1 = shift( wick_gap_up , 1 ) ;
wick_gap_down = high < shift( low , 1 ) ;
black_marubozu = ( high == open ) .* ( low == close ) ;
white_marubozu = ( high == close ) .* ( low == open ) ;
white_marubozu_1 = shift( white_marubozu , 1 ) ;

## 01 - Bearish Abandoned Baby
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     a doji candle
##     the low price above the prior high price
## Third candle
##     black body
##     the high price below the prior low price
bearish_signal_matrix(:,1) = uptrend_2 .* white_body_2 .* ...
                              doji_1 .* wick_gap_up_1 .* ...
                              black_body .* wick_gap_down ; 
## 02 - Dark Cloud Cover
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     black body
##     the opening above or equal of the prior high
##     the closing below the midpoint of the prior candle
##     the closing above the previous opening
bearish_signal_matrix(:,2) = uptrend_1 .* white_body_1 .* ...
                              black_body .* (open >= high_1) .* (close < candle_midpoint_1) .* (close > open_1) ;  
## 03 - Evening Doji Star
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     a doji candle
##     a doji body above the previous candle body
##     the low price below the previous candle high price
## Third candle
##     black body
##     candle body below the previous candle body
##     the closing price below the midpoint of the first candle body
bearish_signal_matrix(:,3) = uptrend_2 .* white_body_2 .* ...
                              doji_1 .* (open_1 > body_high_2) .* (low_1 < high_2) .* ...
                              black_body .* (body_high < close_1) .* (close < body_midpoint_2) ;
## 04 - Evening Star
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     white or black body
##     the candle body is located above the prior body
## Third candle
##     black body
##     the candle body is located below the prior body
##     the candle closes at least halfway down the body of the first line
bearish_signal_matrix(:,4) = uptrend_2 .* white_body_2 .* ...
                              (body_low_1 > body_high_2) .* ...
                              black_body .* (body_high < body_low_1) .* (close <= body_midpoint_2) ;
## 05 - Kicking
## First candle
##     a White Marubozu
##     appears on as a long line
## Second candle
##     a Black Marubozu
##     price gaps downward
##     appears on as a long line
bearish_signal_matrix(:,5) = white_marubozu_1 .* long_line_1 .* ...
                              black_marubozu .* (high < low_1) .* long_line ; 
## 06 - Three Black Crows
## First candle
##     a candle in an uptrend
##     black body
## Second candle
##     black body
##     the opening price within the previous body
##     the closing price below the previous closing price
## Third candle
##     black body
##     the opening price within the previous body
##     the closing price below the previous closing price
bearish_signal_matrix(:,6) = uptrend_2 .* black_body_2 .* ...
                              black_body_1 .* (open_1 < body_high_2) .* (open_1 > body_low_2) .* (close_1 < close_2) .* ...
                              black_body .* (open < body_high_1) .* (open > body_low_1) .* (close < close_1) ; 
## 07 - Three Inside Down
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     black body
##     the candle body is engulfed by the prior candle body
## Third candle
##     black_body
##     the closing price is below the previous closing price
bearish_signal_matrix(:,7) = uptrend_2 .* white_body_2 .* ...
                              black_body_1 .* (body_high_1 < body_high_2) .* (body_low_1 > body_low_2) .* ...
                              black_body .* (close < close_1) ;
## 08 - Three Outside Down
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     black body
##     candle’s body engulfs the prior (white) candle’s body
## Third candle
##     closing price below the previous closing price
##     black body
bearish_signal_matrix(:,8) = uptrend_2 .* white_body_2 .* ...
                              black_body_1 .* (body_high_1 > body_high_2) .* (body_low_1 < body_low_2) .* ...
                              black_body .* (close < close_1) ;
## 09 - Upside Gap Two Crows
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     black body
##     candle's body above the previous candle's body
## Third candle
##     black body
##     candle’s body engulfs the prior candle’s body
bearish_signal_matrix(:,9) = uptrend_2 .* white_body_2 .* ...
                              black_body_1 .* (body_low_1 > body_high_2) .* ...
                              black_body .* (body_high > body_high_1) .* (body_low < body_low_1) ;
## 10 - Advance Block
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     white body
##     the opening price is within the previous body
##     the closing price is above the previous closing price
## Third candle
##     white body
##     the opening price is within the previous body
##     the closing price is above the previous closing price
bearish_signal_matrix(:,10) = uptrend_2 .* white_body_2 .* ...
                               white_body_1 .* (open_1 < body_high_2) .* (open_1 > body_low_2) .* (close_1 > close_2) .* ...
                               white_body .* (open < body_high_1) .* (open > body_low_1) .* (close > close_1) ;
## 11 - Breakaway
## First candle
##     a tall white candle
## Second candle
##     a white candle
##     candle opens above the previous closing price (upward price gap, shadows can overlap)
## Third candle
##     a white or black candle
##     candle opens above the previous opening price
## Fourth candle
##     a white candle
##     candle closes above the previous closing price
## Fifth candle
##     a tall black candle
##     candle opens below the previous closing price
##     candle closes below the second line's opening price and above the first line's closing price
##     the price gap formed between the first and the second line is not closed
bearish_signal_matrix(:,11) = white_body_4 .* long_line_4 .* ...
                               white_body_3 .* (open_3 > close_4) .* ...
                               (open_2 > open_3 ) .* ...
                               white_body_1 .* (close_1 > close_2) .* ...
                               black_body .* long_line .* (open < close_1) .* (close < open_3) .* (close > close_4) .* (low > high_4) ;
## 12 - Counter Attack
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     black_body
##     the opening price is higher than the previous closing price
##     the closing price is at or lower than the previous closing price
bearish_signal_matrix(:,12) = uptrend_1 .* white_body_1 .* ...
                               black_body .* (open > close_1) .* (close <= close_1) ;
## 13 - Deliberation
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     white body
##     the opening price is above the previous opening price
##     the closing price is above the previous closing price
## Third candle
##     white body
##     the opening price is slightly lower or higher than the previous closing price
##     the closing price is above the previous closing price
bearish_signal_matrix(:,13) = uptrend_2 .* white_body_2 .* ...
                               white_body_1 .* (open_1 > open_2) .* (close_1 > close_2) .* ...
                               white_body .* (open > body_midpoint_1) .* (close > close_1) ;
## 14 - Doji Star
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     a doji candle
##     a body above the first candle's body
bearish_signal_matrix(:,14) = uptrend_1 .* white_body_1 .* ...
                               doji .* (open > close_1) ; 
## 15 - Dragonfly Doji
##     Opening, closing and maximum prices are the same or very similar
##     Long lower shadow
##     appears on as a long line
bearish_signal_matrix(:,15) = (open == close) .* (close == high) .* long_line ;

## 16 - Engulfing
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     black body
##     candle's body engulfs the prior (white) candle's body
bearish_signal_matrix(:,16) = uptrend_1 .* white_body_1 .* ...
                               black_body .* (body_high > body_high_1) .* (body_low < body_low_1) ;
## 17 - Gravestone Doji
##     Opening, closing and minimum prices are the same or very similar
##     Long upper shadow
##     appears on as a long line
bearish_signal_matrix(:,17) = (open == close) .* (close == low) .* long_line ;

## 18 - Harami Cross
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     a doji candle with two shadows
##     candle's body engulfed by the prior (white) candle's body
bearish_signal_matrix(:,18) = uptrend_1 .* white_body_1 .* ...
                               doji .* (high > close) .* (low < close) .* (high < body_high_1) .* (low > body_low_1) ;
## 19 - Identical Three Crows
## First candle
##     a candle in an uptrend
##     black body
## Second candle
##     black body
##     the opening price at or near the prior close
## Third candle
##     black body
##     the opening price at or near the prior close
bearish_signal_matrix(:,19) = uptrend_2 .* black_body_2 .* ...
                               black_body_1 .* (open_1 == close_2) .* ...
                               black_body .* (open == close_1) ;
## 20 - Long Legged Doji
##     a doji candle
##     opening and closing prices are the same or similar
##     upper and lower shadow are very long
##     body is located in the middle of the candle or nearly mid-range
##     appears on as a long line
bearish_signal_matrix(:,20) = doji .* ((high .- close) > 0) .* ((close .- low) > 0) .* long_line ;

## 21 - Meeting Lines
## First candle
##     a candle in an uptrend
##     white body
##     appears as a long line
## Second candle
##     black body
##     the closing price is equal to the previous closing price
bearish_signal_matrix(:,21) = uptrend_1 .* white_body_1 .* long_line_1 .* ...
                               black_body .* (close == close_1) ;
## 22 - Tri Star
## First candle
##     a doji candle in an uptrend
## Second candle
##     a doji candle
##     a body above the prior body
## Third candle
##     a doji candle
##     a body below the prior body
bearish_signal_matrix(:,22) = uptrend_2 .* doji_2 .* ...
                               doji_1 .* (open_1 > close_2) .* ...
                               doji .* (open < close_1) ;
## 23 - Two Crows
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     black body
##     the closing price above the prior closing price (gap between bodies)
## Third candle
##     black body
##     the opening price within the prior body
##     the closing price within the body of the first line (gap close)
bearish_signal_matrix(:,23) = uptrend_2 .* white_body_2 .* ...
                               black_body_1 .* (close_1 > close_2) .* ...
                               black_body .* (open < body_high_1) .* (open > body_low_1) .* (close < close_2) ;
## 24 - Belt Hold
##     black body
##     no upper shadow
##     short lower shadow
##     appears as a long line
bearish_signal_matrix(:,24) = black_body .* (open == high) .* (low < close) .* long_line ;

## 25 - Hanging Man
##     uptrend
##     white or black candle with a small body
##     no upper shadow or the shadow cannot be longer than the body
##     lower shadow at least two times longer than the body
##     if the gap is created at the opening or at the closing, it makes the signal stronger
##     appears as a long line
##     the body fully located above the trendline
bearish_signal_matrix(:,25) = uptrend .* (doji == 0) .* ((high .- body_high) < body_range) .* ((body_low .- low) >= 2.*body_range) ; 

## 26 - Harami
## First candle
##     a candle in an uptrend
##     white body
## Second candle
##     black body
##     candle's body engulfed by the prior (white) candle's body
bearish_signal_matrix(:,26) = uptrend_1 .* white_body_1 .* ...
                               black_body .* (body_high < body_high_1) .* (body_low > body_low_1) ;
## 27 - Shooting Star
##     white or black candle with a small body
##     no lower shadow or the shadow cannot be longer than the body
##     upper shadow at least two times longer than the body
##     if the gap is created at the opening or the closing, it makes the signal stronger
##     appears as a long line
bearish_signal_matrix(:,27) = (doji == 0) .* ((body_low .- low) < body_range) .* ((high .- body_high) >= 2.*body_range) ;

bearish_signal_matrix( 1 : 10 , : ) = 0 ;

endfunction

Friday, 31 July 2020

Currency Strength Candlestick Chart

In my previous posts on currency strength indices I have always visualised the indicator(s) as a line chart, e.g. here. However, after some deep thought, I have now created a way to visualise this as a candlestick chart using Octave's candle function, which, by the way, was written by me. Creating the candlestick body of a currency strength index was quite straight forward - just use the previous currency strength value as the bar's open and the current currency strength value as the close. A simple plot of this, with an overlaid currency strength index line chart, is

Of course the problem with this rendering is that there are no candlestick wicks.

My solution to create the wicks is showcased by the following code snippets
retval_high_wicks( ii , 6 ) = log( str2double( S.candles{ ii }.mid.h ) / max( str2double( S.candles{ ii }.mid.o ) , str2double( S.candles{ ii }.mid.c ) ) ) ;
retval_low_wicks( ii , 6 ) = log( str2double( S.candles{ ii }.mid.l ) / min( str2double( S.candles{ ii }.mid.o ) , str2double( S.candles{ ii }.mid.c ) ) ) ;
and
[ ii , ~ , v ] = find( [ retval_high_wicks( : , [32 33 34 35 36 37].+5 ) , -1.*retval_low_wicks( : , [5 20 28].+5 ) ] ) ;
new_index_high_wicks( : , 13 ) = accumarray( ii , v , [] , @mean ) ;
[ ii , ~ , v ] = find( [ retval_low_wicks( : , [32 33 34 35 36 37].+5 ) , -1.*retval_high_wicks( : , [5 20 28].+5 ) ] ) ;
new_index_low_wicks( : , 13 ) = accumarray( ii , v , [] , @mean ) ;
The first snippet shows an additional bit of code to the code here to record the log values of highs (lows) over (under) the candlestick bodies of all relevant currencies used in creating the currency strength indices.

The second snippet shows how the wicks are created, namely by taking the mean log values of high (low) wicks indexed by e.g.
[32 33 34 35 36 37].+5 and [5 20 28].+5
columns of downloaded forex crosses.

The reasoning behind this is as follows: take, for example, the EUR_USD forex pair - the upper wicks of these bars are recorded as upper wicks for the EUR index candles and as lower wicks for the USD index candles, reflecting the fact that upper wicks in EUR_USD can be viewed as intrabar EUR strength pushing to new highs or, alternatively, USD index candle's weakness pushing to new lows which, because the USD is the quote currency of the pair, also leads to new highs in the cross. A similar, reversed logic applies to the low wicks of the cross.

Below are charts of currency strength index candles created according to this methodology
The upper pane shows GBP currency strength index candles and the lower pane the same for USD. This is basically price action for Thursday, 30th July, 2020. The green vertical lines are the London and New York opens respectively, the red vertical line is the London close and the charts end at more or less the New York close. Bars prior to the London open are obviously the overnight Asian session.

My contemporaneous volume profile chart is the upper right pane below
 
From these charts it is easy to discern that the upward movement of GBP_USD during the main London session was due to GBP strength, whilst after the London close the continued upward movement of GBP_USD was due to USD weakness.

However, the point of this blog post was not to pass commentary on FX price movements, but to illustrate a methodology of creating candlestick charts for currency strength indices.

Enjoy!

Wednesday, 15 July 2020

Forex Intraday Seasonality

Over the last week or so I have been reading about/investigating this post's title matter. Some quotes from various papers' abstracts on the matter are:
  • "We provide empirical evidence that the unique signature of the FX market seasonality is indeed due to the different time zones market participants operate from. However, once normalised using our custom-designed procedure, we observe a pattern akin to equity markets. Thus, we have revealed an important FX market property that has not been reported before." - Phd. paper - April 2013
  • "Using 10 years of high‐frequency foreign exchange data, we present evidence of time‐of‐day effects in foreign exchange returns through a significant tendency for currencies to depreciate during local trading hours. We confirm this pattern across a range of currencies and find that, in the case of EUR/USD, it can form a simple, profitable trading strategy" - Paper date - November 2010 - emphasis is mine
  • "This paper examines the intraday seasonality of transacted limit and market orders in the DEM/USD foreign exchange market. Empirical analysis of completed transactions data based on the Dealing 2000-2 electronic inter-dealer broking system indicates significant evidence of intraday seasonality in returns and return volatilities under usual market conditions. Moreover, analysis of realised tail outcomes supports seasonality for extraordinary market conditions across the trading day." - Paper date - May 2007
  • "In this article, we search for the evidence of intraweek and intraday anomalies on the spot foreign exchange (FOREX) market. Having in mind the international scope of this market ... We find that intraday and interaction between day and hour anomalies are present in trading EUR/USD on the spot FOREX market over the period of 10 years" - Paper date - 2014
  • "We find that the underpinnings for the time-varying pattern of the probability of informed trading are rooted in the strategic arrival of informed traders on a particular hour-of-day, day-of-week, and geographic location (market)." - Paper date - April 2008
In addition to this there seem to be numerous blogs, articles online etc. which also suggest that forex seasonality is a real phenomenon, so I thought I'd have a quick look into it myself.

Rather than do a full, statistical analysis I have used the following Octave function
clear all ;
data = dlmread( '/home/path/to/hourly_currency_index_g_mults' ) ;
## aud_x = x( 1)  ; cad_x = x( 2 ) ; chf_x = x( 3 ) ; eur_x = x( 4 ) ; gbp_x = x( 5 ) ; hkd_x = x( 6 ) ;
## jpy_x = x( 7 ) ; nzd_x = x( 8 ) ; sgd_x = x( 9 ) ; usd_x = x( 10 ) ; ## plus 6 for ix to account for date cols
## first 6 cols are YYYY MM DD HH-GMT HH-BST HH-EST
logged_data = data ; logged_data( : , 7 : end ) = log( logged_data( : , 7 : end ) ) ;

## get the days. The days of the week are numbered 1–7 with the first day being Sunday.
days_num = weekday( datenum( [ data(:,1) , data(:,2) , data(:,3) , data(:,5) ] ) ) ; ## BST time

start = input( 'Do you want to enter start date? Y or N ' , 's' ) ;
if ( strcmp( tolower( start ) , 'y' ) )
 year_start = input( 'Enter year YYYY:  ' ) ;
 month_start = input( 'Enter month MM:  ' ) ;
 day_start = input( 'Enter day date:  ' ) ;
 delete_ix = find( (logged_data(:,1)==year_start) .* (logged_data(:,2)==month_start) .* (logged_data(:,3)==day_start) ) ;
 
 if ( !isempty( delete_ix ) )
 logged_data( 1 : delete_ix , : ) = [] ; days_num( 1 : delete_ix , : ) = [] ;
 else
 disp( 'Invalid start date, so charts will show all data.' ) ;
 endif

endif

## create individual day indices
monday_indices = [ ( 0 : 1 : 23 )' , zeros( 24 , 10 ) ] ;
tuesday_indices = monday_indices ;
wednesday_indices = monday_indices ;
thursday_indices = monday_indices ;
friday_indices = monday_indices ;
alldays_indices = monday_indices ;

running_denom = zeros( 24 , 10 ) ;

for jj = 0 : 23
ix = find( ( days_num == 2 ) .* ( logged_data( : , 5 ) == jj ) ) ;
running_denom( jj + 1 , : ) = running_denom( jj + 1 , : ) + numel( ix ) ;
monday_indices( jj + 1 , 2 : end ) = sum( logged_data( ix , 7 : end ) , 1 ) ./ numel( ix ) ;
alldays_indices( jj + 1 , 2 : end ) = sum( logged_data( ix , 7 : end ) , 1 ) ;
endfor

for jj = 0 : 23
ix = find( ( days_num == 3 ) .* ( logged_data( : , 5 ) == jj ) ) ;
running_denom( jj + 1 , : ) = running_denom( jj + 1 , : ) + numel( ix ) ;
tuesday_indices( jj + 1 , 2 : end ) = sum( logged_data( ix , 7 : end ) , 1 ) ./ numel( ix ) ;
alldays_indices( jj + 1 , 2 : end ) = alldays_indices( jj + 1 , 2 : end ) .+ sum( logged_data( ix , 7 : end ) , 1 ) ;
endfor

for jj = 0 : 23
ix = find( ( days_num == 4 ) .* ( logged_data( : , 5 ) == jj ) ) ;
running_denom( jj + 1 , : ) = running_denom( jj + 1 , : ) + numel( ix ) ;
wednesday_indices( jj + 1 , 2 : end ) = sum( logged_data( ix , 7 : end ) , 1 ) ./ numel( ix ) ;
alldays_indices( jj + 1 , 2 : end ) = alldays_indices( jj + 1 , 2 : end ) .+ sum( logged_data( ix , 7 : end ) , 1 ) ;
endfor

for jj = 0 : 23
ix = find( ( days_num == 5 ) .* ( logged_data( : , 5 ) == jj ) ) ;
running_denom( jj + 1 , : ) = running_denom( jj + 1 , : ) + numel( ix ) ;
thursday_indices( jj + 1 , 2 : end ) = sum( logged_data( ix , 7 : end ) , 1 ) ./ numel( ix ) ;
alldays_indices( jj + 1 , 2 : end ) = alldays_indices( jj + 1 , 2 : end ) .+ sum( logged_data( ix , 7 : end ) , 1 ) ;
endfor

for jj = 0 : 20 ## market closes at 17:00 EST
ix = find( ( days_num == 6 ) .* ( logged_data( : , 5 ) == jj ) ) ;
running_denom( jj + 1 , : ) = running_denom( jj + 1 , : ) + numel( ix ) ;
friday_indices( jj + 1 , 2 : end ) = sum( logged_data( ix , 7 : end ) , 1 ) ./ numel( ix ) ;
alldays_indices( jj + 1 , 2 : end ) = alldays_indices( jj + 1 , 2 : end ) .+ sum( logged_data( ix , 7 : end ) , 1 ) ;
endfor

alldays_indices( : , 2 : end ) = alldays_indices( : , 2 : end ) ./ running_denom ;

monday_indices( : , 2 : end ) = cumsum( monday_indices( : , 2 : end ) ) ;
tuesday_indices( : , 2 : end ) = cumsum( tuesday_indices( : , 2 : end ) ) ;
wednesday_indices( : , 2 : end ) = cumsum( wednesday_indices( : , 2 : end ) ) ;
thursday_indices( : , 2 : end ) = cumsum( thursday_indices( : , 2 : end ) ) ;
friday_indices( : , 2 : end ) = cumsum( friday_indices( : , 2 : end ) ) ;
alldays_indices( : , 2 : end ) = cumsum( alldays_indices( : , 2 : end ) ) ;

if ( ishandle(1) )
 clf(1) ;
endif
figure( 1 ) ;
h1 = axes( 'position' , [ 0.03 , 0.54 , 0.30 , 0.43 ] ) ; plot( monday_indices(:,3) , 'k' , 'linewidth' , 2 , ...
monday_indices(:,4) , 'c' , 'linewidth' , 2 , ...
monday_indices(:,5) , 'b' , 'linewidth' , 2 , ...
monday_indices(:,6) , 'r' , 'linewidth' , 2 , ...
monday_indices(:,11) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'MONDAY' ) ;
legend( 'CAD' , 'CHF' , 'EUR' , 'GBP' , 'USD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h2 = axes( 'position' , [ 0.36 , 0.54 , 0.30 , 0.43 ] ) ; plot( tuesday_indices(:,3) , 'k' , 'linewidth' , 2 , ...
tuesday_indices(:,4) , 'c' , 'linewidth' , 2 , ...
tuesday_indices(:,5) , 'b' , 'linewidth' , 2 , ...
tuesday_indices(:,6) , 'r' , 'linewidth' , 2 , ...
tuesday_indices(:,11) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'TUESDAY' ) ;
legend( 'CAD' , 'CHF' , 'EUR' , 'GBP' , 'USD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h3 = axes( 'position' , [ 0.69 , 0.54 , 0.30 , 0.43 ] ) ; plot( wednesday_indices(:,3) , 'k' , 'linewidth' , 2 , ...
wednesday_indices(:,4) , 'c' , 'linewidth' , 2 , ...
wednesday_indices(:,5) , 'b' , 'linewidth' , 2 , ...
wednesday_indices(:,6) , 'r' , 'linewidth' , 2 , ...
wednesday_indices(:,11) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'WEDNESDAY' ) ;
legend( 'CAD' , 'CHF' , 'EUR' , 'GBP' , 'USD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h4 = axes( 'position' , [ 0.03 , 0.04 , 0.30 , 0.43 ] ) ; plot( thursday_indices(:,3) , 'k' , 'linewidth' , 2 , ...
thursday_indices(:,4) , 'c' , 'linewidth' , 2 , ...
thursday_indices(:,5) , 'b' , 'linewidth' , 2 , ...
thursday_indices(:,6) , 'r' , 'linewidth' , 2 , ...
thursday_indices(:,11) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'THURSDAY' ) ;
legend( 'CAD' , 'CHF' , 'EUR' , 'GBP' , 'USD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h5 = axes( 'position' , [ 0.36 , 0.04 , 0.30 , 0.43 ] ) ; plot( friday_indices(:,3) , 'k' , 'linewidth' , 2 , ...
friday_indices(:,4) , 'c' , 'linewidth' , 2 , ...
friday_indices(:,5) , 'b' , 'linewidth' , 2 , ...
friday_indices(:,6) , 'r' , 'linewidth' , 2 , ...
friday_indices(:,11) , 'g' , 'linewidth' , 2 ) ; xlim([0 21]) ; grid minor on ; title( 'FRIDAY' ) ;
legend( 'CAD' , 'CHF' , 'EUR' , 'GBP' , 'USD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h6 = axes( 'position' , [ 0.69 , 0.04 , 0.30 , 0.43 ] ) ; plot( alldays_indices(:,3) , 'k' , 'linewidth' , 2 , ...
alldays_indices(:,4) , 'c' , 'linewidth' , 2 , ...
alldays_indices(:,5) , 'b' , 'linewidth' , 2 , ...
alldays_indices(:,6) , 'r' , 'linewidth' , 2 , ...
alldays_indices(:,11) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'ALL DAYS COMBINED' ) ;
legend( 'CAD' , 'CHF' , 'EUR' , 'GBP' , 'USD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

if ( ishandle(2) )
 clf(2) ;
endif
figure( 2 ) ;
h1 = axes( 'position' , [ 0.03 , 0.54 , 0.30 , 0.43 ] ) ; plot( monday_indices(:,2) , 'k' , 'linewidth' , 2 , ...
monday_indices(:,7) , 'c' , 'linewidth' , 2 , ...
monday_indices(:,8) , 'b' , 'linewidth' , 2 , ...
monday_indices(:,9) , 'r' , 'linewidth' , 2 , ...
monday_indices(:,10) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'MONDAY' ) ;
legend( 'AUD' , 'HKD' , 'JPY' , 'NZD' , 'SGD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h2 = axes( 'position' , [ 0.36 , 0.54 , 0.30 , 0.43 ] ) ; plot( tuesday_indices(:,2) , 'k' , 'linewidth' , 2 , ...
tuesday_indices(:,7) , 'c' , 'linewidth' , 2 , ...
tuesday_indices(:,8) , 'b' , 'linewidth' , 2 , ...
tuesday_indices(:,9) , 'r' , 'linewidth' , 2 , ...
tuesday_indices(:,10) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'TUESDAY' ) ;
legend( 'AUD' , 'HKD' , 'JPY' , 'NZD' , 'SGD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h3 = axes( 'position' , [ 0.69 , 0.54 , 0.30 , 0.43 ] ) ; plot( wednesday_indices(:,2) , 'k' , 'linewidth' , 2 , ...
wednesday_indices(:,7) , 'c' , 'linewidth' , 2 , ...
wednesday_indices(:,8) , 'b' , 'linewidth' , 2 , ...
wednesday_indices(:,9) , 'r' , 'linewidth' , 2 , ...
wednesday_indices(:,10) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'WEDNESDAY' ) ;
legend( 'AUD' , 'HKD' , 'JPY' , 'NZD' , 'SGD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h4 = axes( 'position' , [ 0.03 , 0.04 , 0.30 , 0.43 ] ) ; plot( thursday_indices(:,2) , 'k' , 'linewidth' , 2 , ...
thursday_indices(:,7) , 'c' , 'linewidth' , 2 , ...
thursday_indices(:,8) , 'b' , 'linewidth' , 2 , ...
thursday_indices(:,9) , 'r' , 'linewidth' , 2 , ...
thursday_indices(:,10) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'THURSDAY' ) ;
legend( 'AUD' , 'HKD' , 'JPY' , 'NZD' , 'SGD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h5 = axes( 'position' , [ 0.36 , 0.04 , 0.30 , 0.43 ] ) ; plot( friday_indices(:,2) , 'k' , 'linewidth' , 2 , ...
friday_indices(:,7) , 'c' , 'linewidth' , 2 , ...
friday_indices(:,8) , 'b' , 'linewidth' , 2 , ...
friday_indices(:,9) , 'r' , 'linewidth' , 2 , ...
friday_indices(:,10) , 'g' , 'linewidth' , 2 ) ; xlim([0 21]) ; grid minor on ; title( 'FRIDAY' ) ;
legend( 'AUD' , 'HKD' , 'JPY' , 'NZD' , 'SGD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;

h6 = axes( 'position' , [ 0.69 , 0.04 , 0.30 , 0.43 ] ) ; plot( alldays_indices(:,2) , 'k' , 'linewidth' , 2 , ...
alldays_indices(:,7) , 'c' , 'linewidth' , 2 , ...
alldays_indices(:,8) , 'b' , 'linewidth' , 2 , ...
alldays_indices(:,9) , 'r' , 'linewidth' , 2 , ...
alldays_indices(:,10) , 'g' , 'linewidth' , 2 ) ; xlim([0 23]) ; grid minor on ; title( 'ALL DAYS COMBINED' ) ;
legend( 'AUD' , 'HKD' , 'JPY' , 'NZD' , 'SGD' , 'location' , 'north' , 'orientation' , 'horizontal' ) ;
vline( 7 , 'r' ) ; vline( 12 , 'g' ) ;
to conduct a quick visual analysis. This builds upon my recent work on fx pairs via oanda api and currency strength, and uses hourly data since June 2012.

This produces 24 hour seasonality charts of CAD, CHF, EUR, GBP and USD, i.e. the European and North American currencies.
The x-axis is in British Summer Time (BST) hours, the vertical red and green lines indicate 7:00am opens in London and New York respectively, all charts end at 17:00 New York (EST) time and the y-axis is hourly log returns. The individual currency seasonality lines are the cummulative cross-sectional means at BST 00, BST 01 ... etc. per weekday and all days combined (see subchart titles). BST Sunday evenings' returns prior to Monday trading are not included.

A similar chart for the Asian time zone currencies of AUD, HKD, JPY, NZD and SGD is also produced.
The function allows charts with a user selected data begin date to be plotted, but the illustrations above use all data available to me, i.e. hourly data since 2012.

It seems to me that, as indicated in my highlighting above, there is definite intraday forex seasonalilty in play. However, readers should be cautioned that the above is only a general tendency based on the last 9 years or so of hourly data. A more recent "data snapshot" of only data since the beginning of 2020 can tell a slightly different story:
look at GBP (red line) on Tuesdays and Wednesdays, for example. As always with stuff one reads online, even the extremely high quality stuff on this blog 😊, Caveat emptor.