KX Community

Find answers, ask questions, and connect with our KX Community around the world.
KX Community Guidelines

Home Forums kdb+ Simple Backtester in q

  • Simple Backtester in q

    Posted by rgiu70 on October 31, 2024 at 8:55 am

    Good morning everyone, I’m trying to create a backtest engine for trading strategies that processes signals from tick datasets and verifies the performance position by position, according to stop loss and target levels.

    .Backtester.Engine:{[data] entry_time: data`ts_event;
    entry_prc: data`entry_price;
    tp: data`target;
    sl: data`stop;
    id: data`order_id;
    sig: data`signal;
    idx1: data`idx;
    dataset: ?[sig = 1;first select idx1, idx2:idx, id, entry_time, exit_time: ts_event, sig, entry_prc, stop:sl, target: price from final_6E where (ts_event > entry_time) & ((price > tp) | (price < sl));
    ?[sig = -1;first select idx1, idx2:idx, id, entry_time,exit_time:ts_event, sig, entry_prc, stop:sl, target: price from final_6E where (ts_event > entry_time) & ((price < tp) | (price > sl));0]];
    dataset: update result: ?[(sig = 1) & (target > entry_prc); 1;
    ?[(sig = -1) & (target < entry_prc); 1; -1]] from dataset;
    dataset: update pips_result: ?[sig = 1; target - entry_prc;
    ?[sig = -1; entry_prc - target; 0]] from dataset;
    dataset: update trade_duration: (exit_time.minute - entry_time.minute) from dataset;
    :dataset;
    };
    
    results: .Backtester.Engine each final_table;

     

    Specifically, one of the parts I’m managing in a totally inefficient way is the backtest function itself. With this function, the goal is to find the first point where the price reaches either the stop loss or target level, operation by operation, recording the results of the trade. The sig column represents (1 for long signals, -1 for short signals).

    The input table to the function is the one that filters all generated signals, while “final_6E” represents the complete tick-by-tick table, necessary to identify the exact point where events occur.

    How can I optimize this process, considering that I’m using idx (index number) to perform a loop where I search for the first useful point between stop and take profit?

    Thank you.

    rgiu70 replied 1 month ago 1 Member · 0 Replies
  • 0 Replies
  • jlucid

    Member
    November 14, 2024 at 11:37 am

    Without modifying the existing function, you can reduce the total compute time through parallelisation and the use of multiple threads. Start your process with the -s flag and use peach as shown

    .Backtester.Engine peach final_table

    I generated your two tables using random data and the threading scales pretty well.

    I would say that with queries like this, it would be very helpful for us if you could append sample csv files so that we can quickly load and test potential solutions. Always check that the files dont include client sensitive data before hand, and obscure column data if necessary.

    Also, when it comes to optimising functions like this which are critical to your backtesting. Start by making some unit tests with sample data, so that you can be confident that any performance improvements don’t break existing functionality. You could use qspec, k4unit, qunit

Log in to reply.