Take data from your example:
txn:([] t: til 6; side:`Buy`Sell`Buy`Sell`Sell`Buy; qty:6 5 4 3 8 1; px: 10.0 11.0 10.0 11.0 9.0 8.0)
Best to maintain buys
and sells
transactions/fills separately in your database:
buys: select from txn where side=`Buy
sells: select from txn where side=`Sell
Functions we'll need [1]:
/ first-in first-out allocation of bid/buy and ask/sell fills
/ returns connectivity matrix of (b)id fills in rows and (a)sk fills in columns
fifo: {deltas each deltas sums[x] &\: sums[y]};
/ connectivity list from connectivity matrix
lm: {raze(til count x),''where each x};
/ realized profit & loss
rpnl: {[b;s]
t: l,'f ./: l:lm (f:fifo[exec qty from b;exec qty from s])>0;
pnl: (select bt:t, bqty:qty, bpx:px from b@t[;0]),'(select st:t, sqty:qty, spx:px from s@t[;1]),'([] qty: t[;2]);
select tstamp: bt|st, rpnl:qty*spx-bpx from pnl
}
Run:
q)rpnl[buys;sells]
tstamp rpnl
-----------
1 5
3 1
3 2
4 -2
5 1
According to my timings, should be ~ 2x faster than the next best solution, since it's nicely vectorized.
Footnotes:
fifo
function is a textbook example from Q for Mortals
. In your case, it looks like this:
q)fifo[exec qty from buys;exec qty from sells]
5 1 0
0 2 2
0 0 1
lm
function tells which buys and sell pairs were crossed (non-zero fills). More background here: [kdb+/q]: Convert adjacency matrix to adjacency list
q)lm fifo[exec qty from buys;exec qty from sells]>0
0 0
0 1
1 1
1 2
2 2
Cryptic first line of rpnl
is then combination of the two concepts above:
q)t: l,'f ./: l:lm (f:fifo[exec qty from buys;exec qty from sells])>0;
0 0 5
0 1 1
1 1 2
1 2 2
2 2 1