Capturing orders in the multi-exchange order book can be structured like so:
q)init:{B::(1#`)!enlist x!count[x]#enlist `s#(enlist 0Np)!enlist (0#0Ni)!(0#0Ni)} / [e(x)changes]:initialized global order (B)ook
q)add0:{.[`B;-1 _ x;:;last x]} / [orders:(`sym;`ex;tstam(p);px(i);sz(i))]:(add)ed to (B)ook
q)add:{ .[`B;-1 _ x;+;last x]} / [orders:(`sym;`ex;tstam(p);px(i);sz(i))]:(add)ed to (B)ook
q)state::(+/'')B / state view of the order book, rolling up all the events; `state is a view as per https://code.kx.com/q/learn/views/
We can add orders individually:
q)ti:2024.05.07D14 / timestamp
q)init `binance`bybit / initialize order book B
q)B[`btc;`binance;ti;62i]:5i / order to buy 5 BTC on Binance for 62 gets added as bid (no matching here)
q)B[`eth;`binance;ti;3000i]:6i
q)B[`btc;`bybit;ti;62i]:-2i / sell order gets added as ask (no matching)
q)B[`eth;`bybit;ti;3000i]:1i
q)B
| binance bybit
| -----------------------------------------------------------------------------------------------------------------------------------------
| `s#(`s#,0Np)!,(`int$())!`int$() `s#(`s#,0Np)!,(`int$())!`int$()
btc| `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,62i)!,5i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,62i)!,-2i)
eth| `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,6i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,1i)
q)state
| binance bybit
| -----------------------------------
| (`int$())!`int$() (`int$())!`int$()
btc| (,62i)!,5i (,62i)!,-2i
eth| (,3000i)!,6i (,3000i)!,1i
We can amend vectors of orders in bulk like so: https://code.kx.com/q/ref/amend/#cross-sections
q)ti:2024.05.07D15
q)add0 (`btc;`binance;ti; 62 65i; 4 -1i);
q)state
| binance bybit
| -----------------------------------
| (`int$())!`int$() (`int$())!`int$()
btc| 62 65i!9 -1i (,62i)!,-2i
eth| (,3000i)!,6i (,3000i)!,1i
q)B
| binance bybit
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| `s#(`s#,0Np)!,(`int$())!`int$() `s#(`s#,0Np)!,(`int$())!`int$()
btc| `s#0N 2024.05.07D14:00:00.000000000 2024.05.07D15:00:00.000000000!((`int$())!`int$();(,62i)!,5i;62 65i!4 -1i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,62i)!,-2i)
eth| `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,6i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,1i)
However, if two orders arrive at identical time and price, only the last in the sequence gets captured with this .[;;:;]
assign form, previous values overwritten:
q)ti:2024.05.07D16
q)add0 (`btc;`binance;ti; 62 62i; 10 20i);
q)B
| binance bybit
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| `s#(`s#,0Np)!,(`int$())!`int$() `s#(`s#,0Np)!,(`int$())!`int$()
btc| `s#0N 2024.05.07D14:00:00.000000000 2024.05.07D15:00:00.000000000 2024.05.07D16:00:00.000000000!((`int$())!`int$();(,62i)!,5i;62 65i!4 -1i;(,62i)!,20i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,62i)!,-2i)
eth| `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,6i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,1i)
q)state
| binance bybit
| -----------------------------------
| (`int$())!`int$() (`int$())!`int$()
btc| 62 65i!29 -1i (,62i)!,-2i
eth| (,3000i)!,6i (,3000i)!,1i
So we introduce .[;;+;]
rather than .[;;:;]
:
q)add (`btc;`binance;ti; 62 62i; 10 20i);
q)B
| binance bybit
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| `s#(`s#,0Np)!,(`int$())!`int$() `s#(`s#,0Np)!,(`int$())!`int$()
btc| `s#0N 2024.05.07D14:00:00.000000000 2024.05.07D15:00:00.000000000 2024.05.07D16:00:00.000000000!((`int$())!`int$();(,62i)!,5i;62 65i!4 -1i;(,62i)!,50i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,62i)!,-2i)
eth| `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,6i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,1i)
q)state
| binance bybit
| -----------------------------------
| (`int$())!`int$() (`int$())!`int$()
btc| 62 65i!59 -1i (,62i)!,-2i
eth| (,3000i)!,6i (,3000i)!,1i
The problem is that it only works with the pre-existing keys, not when new keys would have to be created with this amend:
q)add (`btc;`binance;2024.05.07D15; 63 64i; 100 -300i);
q)B
| binance bybit
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| `s#(`s#,0Np)!,(`int$())!`int$() `s#(`s#,0Np)!,(`int$())!`int$()
btc| `s#0N 2024.05.07D14:00:00.000000000 2024.05.07D15:00:00.000000000 2024.05.07D16:00:00.000000000!((`int$())!`int$();(,62i)!,5i;62 65 63 64i!4 -1 100 -300i;(,62i)!,50i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,62i)!,-2i)
eth| `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,6i) `s#0N 2024.05.07D14:00:00.000000000!((`int$())!`int$();(,3000i)!,1i)
q)state
| binance bybit
| ----------------------------------------------
| (`int$())!`int$() (`int$())!`int$()
btc| 62 65 63 64i!59 -1 100 -300i (,62i)!,-2i
eth| (,3000i)!,6i (,3000i)!,1i
q)ti:2024.05.07D17
q)add (`btc;`binance;ti; 63 64i; 100 -300i);
'length
[1] add:{ .[`B;-1 _ x;+;last x]}
^
q))
Is there an elegant solution to maintaining order book where two orders can have identical timestamp and where it’s ok to capture those two orders as sum of sizes at that level?