kdb+

Obtaining non-overlapping trades in kdb


I've a table t as

q)trades: ([]
    signalTime: 2024.01.05 2024.01.12 2024.01.19 2024.01.25 2024.02.02 2024.02.09 2024.02.16 2024.02.23 2024.03.01 2024.03.07;
    exitTime: 2024.01.25 2024.02.01 2024.02.08 2024.02.15 2024.02.22 2024.02.29 2024.03.07 2024.03.14 2024.03.21 2024.03.28
 );
signalTime exitTime
----------------------
2024.01.05 2024.01.25
2024.01.12 2024.02.01
2024.01.19 2024.02.08
2024.01.25 2024.02.15
2024.02.02 2024.02.22
2024.02.09 2024.02.29
2024.02.16 2024.03.07
2024.02.23 2024.03.14
2024.03.01 2024.03.21
2024.03.07 2024.03.28

I would like to only keep trades whose intervening dates (between signalTime and exitTime) do not overlap with those of any other trades (rows). Alternatively, there should be only one trade during any given date. The expected output should be

signalTime exitTime
----------------------
2024.01.05 2024.01.25
2024.02.02 2024.02.22
2024.02.23 2024.03.14

While I'm able to conceive a solution using a do loop, how do i achieve this in more efficient, idiomatic q?

Here's my feeble attempt at the solution using a do loop though it's not quite correct.

removeOverlappingTrades:{[t]
    n: count t;
    accept: n#0b;          
    accept[0]: 1b;         
    lastExitTime: t[`exitTime][0]; 
    do[n-1; {[t;lastExitTime;accept;x]
        i: x+1;  // Current index
        if[t[`signalTime][i] >= lastExitTime;
           accept[i]: 1b;
           lastExitTime: t[`exitTime][i];
          ];
    }[t;lastExitTime;accept] each til n-1];
    t where accept
 };

Solution

  • // Add a dummy row to ensure the first row gets selected
    // Add a boolean `b` column to capture results
    q)r:update b:0b from ([] signalTime:enlist -0Wd;exitTime:enlist -0Wd),trades
    q)r
    signalTime exitTime   b
    -----------------------
    -0W        -0W        0
    2024.01.05 2024.01.25 0
    2024.01.12 2024.02.01 0
    2024.01.19 2024.02.08 0
    2024.01.25 2024.02.15 0
    2024.02.02 2024.02.22 0
    2024.02.09 2024.02.29 0
    2024.02.16 2024.03.07 0
    2024.02.23 2024.03.14 0
    2024.03.01 2024.03.21 0
    2024.03.07 2024.03.28 0
    
    //scan through the table
    //capturing when a new trade has no overlap with the prevailing
    q)r:{[x;y] $[y[`signalTime]>x[`exitTime];
                [y[`b]:1b;y]; //New window opens, keep y with 1b set
                [x[`b]:0b;x]] //No new window, keep x with 0b set
     } scan r
    q)r
    signalTime exitTime   b
    -----------------------
    -0W        -0W        0
    2024.01.05 2024.01.25 1
    2024.01.05 2024.01.25 0
    2024.01.05 2024.01.25 0
    2024.01.05 2024.01.25 0
    2024.02.02 2024.02.22 1
    2024.02.02 2024.02.22 0
    2024.02.02 2024.02.22 0
    2024.02.23 2024.03.14 1
    2024.02.23 2024.03.14 0
    2024.02.23 2024.03.14 0
    
    //Filter only when new windows opened
    q)select from r where b
    signalTime exitTime   b
    -----------------------
    2024.01.05 2024.01.25 1
    2024.02.02 2024.02.22 1
    2024.02.23 2024.03.14 1