Pages

Thursday, 23 April 2020

Visualising Oanda's Orderbook

My earlier post of 26th March shows code to visualise the most recent instantaneous snapshot of Oanda's order book, realised as a horizontal bar chart superimposed over a price chart. Below is a screen shot of a different type of chart
designed to show the historical order book, which is similar to the proprietary Bookmap software. The background of the chart is a heatmap of the 20 order book levels above and below the order book price, with the lighter colours representing a higher percentage of the total order book order volume, the spheres are sized proportionally to the tick volume of the relevant OHLC bar and the blue and red lines represent the high and low of the bar respectively. The lighter colours nicely highlight the areas where orders are accumulated for potential support and resistance.

However, the above screenshot is actually a two dimensional view, from above, of a three dimensional surface plot, as can be seen in the short video below

The first 25 seconds or so shows panning along the two dimensional view, whilst the remainder of the video shows panning in 3 dimensions. I have whimsically named this a "Snake and Canyon" plot as it resembles watching a river flowing/snaking along a canyon/valley floor and bouncing off the cliffs/hills that are the support and resistance zones. The higher the peaks, the higher the percentage of resting orders, and thus a greater number of incoming market orders is needed to breakout out of the canyon/valley.

I created this so that I can visually look for patterns in the historical data, the reason being that my statistical search for useful features, using the Boruta package, has not shown any useful results yet. The Octave code to produce a "Snake and Canyon" plot is given below. Of course, use of this code presupposes that you have the data available for loading from csv files. Enjoy!
clear all ;
cd /home/dekalog/Documents/octave/oanda_data/20m ;

orderbook_snapshots_files = glob( '*_historical_orderbook_snapshots' ) ;
## {
##   [1,1] = aud_jpy_historical_orderbook_snapshots
##   [2,1] = aud_usd_historical_orderbook_snapshots
##   [3,1] = eur_aud_historical_orderbook_snapshots
##   [4,1] = eur_chf_historical_orderbook_snapshots
##   [5,1] = eur_gbp_historical_orderbook_snapshots
##   [6,1] = eur_jpy_historical_orderbook_snapshots
##   [7,1] = eur_usd_historical_orderbook_snapshots
##   [8,1] = gbp_chf_historical_orderbook_snapshots
##   [9,1] = gbp_jpy_historical_orderbook_snapshots
##   [10,1] = gbp_usd_historical_orderbook_snapshots
##   [11,1] = nzd_usd_historical_orderbook_snapshots
##   [12,1] = usd_cad_historical_orderbook_snapshots
##   [13,1] = usd_chf_historical_orderbook_snapshots
##   [14,1] = usd_jpy_historical_orderbook_snapshots
##   [15,1] = xag_usd_historical_orderbook_snapshots
##   [16,1] = xau_usd_historical_orderbook_snapshots
## }

file_no = input( 'Enter file no. from list, a number 1 to 16 inclusive. ' ) ;
filename = orderbook_snapshots_files{ file_no } ;
str_split = strsplit( filename , "_" ) ;
price_name = strjoin( str_split( 1 : 2 ) , "_" ) ;
ohlc_20m = dlmread( [ price_name , '_ohlc_20m' ] ) ; ## price data
orders = dlmread( [ price_name , '_historical_orderbook_snapshots' ] ) ; ## get latest orderbook levels

if ( file_no == 1 || file_no == 6 || file_no == 9 || file_no == 14 )
 bucket_size = 0.05 ;
elseif ( file_no == 16 )
 bucket_size = 0.5 ;
else
 bucket_size = 0.0005 ;
endif

order_price = orders( : , 6 ) ;
price_length = 250 ;
plot_price = order_price( end - price_length : end ) ;
rounded_price = round( plot_price ./ bucket_size ) .* bucket_size ;
x_length = ( 1 : 1 : ( price_length + 1 ) )' ;

y_max = max( rounded_price .+ ( 20 * bucket_size ) ) ;
y_min = min( rounded_price .- ( 20 * bucket_size ) ) ;
y = ( y_min : bucket_size : y_max )' ;

z = zeros( size( y , 1 ) , size( x_length , 1 ) ) ;
zz = orders( end - price_length : end , 7 : 47 ) .+ orders( end - price_length : end , 48 : 88 ) ;

for ii = 1 : size( z , 2 )
[ ~ , ix ] = min( abs( y .- plot_price( ii ) ) ) ;
z( ix - 20 : ix + 20 , ii ) = zz( ii , : ) ;
endfor

z_high = max( max( z ) ) ;
mid_y = ( ohlc_20m( end - price_length : end , 19 ) .+ ohlc_20m( end - price_length : end , 20 ) ) ./ 2 ;
vol = ohlc_20m( end - price_length : end , 22 ) ; vol = vol ./ max( vol ) ;

up_scatter_ix = find( ohlc_20m( end - price_length : end , 21 ) >= ohlc_20m( end - price_length : end , 18 ) ) ;
down_scatter_ix = find( ohlc_20m( end - price_length : end , 21 ) < ohlc_20m( end - price_length : end , 18 ) ) ;

figure(1) ;
colormap( 'cubehelix' ) ; surf( x_length , y , z , 'edgecolor' , 'none' , 'facecolor' , 'interp' , 'facealpha' , 0.75 ) ; view( 2 ) ; 
axis( [ 1 price_length y_min y_max 0 z_high ] ) ; grid off ;
hold on ;
scatter3( up_scatter_ix , mid_y( up_scatter_ix ) , ones( size( up_scatter_ix ) ) .* z_high ./ 2 , 1000.*vol( up_scatter_ix ) , 'c' , 'o' , 'filled' ) ;
scatter3( down_scatter_ix , mid_y( down_scatter_ix ) , ones( size( down_scatter_ix ) ) .* z_high ./ 2 , 1000.*vol( down_scatter_ix ) , 'm' , 'o' , 'filled' ) ;
line( 'xdata' , x_length , 'ydata' , ohlc_20m( end - price_length : end , 19 ) , 'zdata' , ones(size(x_length)).*z_high ./ 2 , 'color','b' , 'linewidth' , 5 ) ;
line( 'xdata',x_length ,'ydata', ohlc_20m( end - price_length : end , 20 ) ,'zdata', ones(size(x_length)).*z_high ./ 2 , 'color','r' , 'linewidth' , 5 ) ;
xlabel( 'Time' ) ; ylabel( 'Levels - Price' ) ; zlabel( 'OrderBook Percent' ) ; title( strjoin( str_split( 1 : 2 ) , "-" ) ) ;
hold off ;

2 comments:

  1. It sounds like avery interesting project but the visualisation you describe sounds over complicated if you want to visualise market depth and price points of interest why not use/modify an existing methodology that does a similar thing such as market profile theory you could even combine the historic elements of market profile and overlay the existing order book data onto in that way you track where resting orders are situated relative to key profile price points such as VPOC and CPOC etc just a thought

    ReplyDelete
  2. Hi Darren,
    Thanks for your comment. I shall take your suggestion under considerarion.

    ReplyDelete