#backtesting #pair #exit #market #strategy #enter #backtest

rusty_backtest

Super fast backtest of any enter/exit strategy in pure rust

3 releases

0.0.3 Sep 14, 2019
0.0.2 Sep 2, 2019
0.0.1 Sep 2, 2019

#203 in Finance

MIT/Apache

11KB
129 lines

Rusty Backtest

Backtesting made easy!

Rusty Backtest follows a simple concept. The enter - exit testing strategy.

This is where there is some set of logic returns a boolean (True/False) action to enter and to exit the market. This could be a buy-sell pair or a sell-buy pair.

A verbal example of a exit enter test:

enter the market when the RSI is above 70

exit the market if your up 30% or if its been 1 month or if the current price is 50% down.

You can chain logical test together on both the entry and exit points.

Enter/Exit Logic Breakdown

Entering market rules

In this case we passed the backtest an array 2, 7. Think of it as a table with 7 rows and 2 columns.

The first column is the price and is required for the backtest. It is the "market" prices that the backtest uses to track price.

The second column and onwards are custom data values that can be used to infor the enter exit logic.

In this case the second column is some calculated metric - lets say this is the DSI (david's special indicator). It could be the RSI or EMA or other well known technical indicator.

let mydata = vec![
    vec![1.0, 5.3, 0.5, 5.3, 1.0, 5.3, 1.0],
    vec![0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0],
];

Now that we have this information, we want to only enter the market when the DSI is above 0.5. Looking at the data above - we can see this only happends once (good indicator). Note: here we access the second column the "DSI" with market_info.data[1]

fn enter_market_function(market_info: EnterMarketInfo) -> bool {
    // if second row is above or equal to the limit we care about
    if market_info.data[1][market_info.index as usize] >= 0.5 {
        return true;
    }
    return false;
}

This function is run on every "row" of data and allows the user to set the strategy of their choice. If the function returns true and the portfolio has money then the backtest will enter the market. If the function returns false, then no action will be taken.

Exiting market rules

Now we have a way to enter the market. We need a way to exit it! Here we can set some logic to exit the market. Here we just want to exit if the trade is more then 2 days old. This is a silly strategy but you can chain any boolean statements together to make a much more complex - successful strategy.

fn exit_market_function(market_info: ExitMarketInfo) -> bool {
    // if trade in for certain time
    if market_info.index - market_info.index_in >= 2 {
        return true;
    }
    return false;
}

Full Example

We pull together the above enter and exit logic and run a full test. We can see that this test is very small - but also runs very fast 57 nano seconds.

eg. running this same program on about 1500 daily equity prices ran <2ms

Copy and run the following example using

cargo run --release
use std::time::Instant;

extern crate rusty_backtest;
use crate::rusty_backtest::backtest;
use crate::rusty_backtest::EnterMarketInfo;
use crate::rusty_backtest::ExitMarketInfo;
use crate::rusty_backtest::TradeInputResults;

fn enter_market_function(market_info: EnterMarketInfo) -> bool {
    // if second row indicates
    if market_info.data[1][market_info.index as usize] >= 0.5 {
        return true;
    }
    return false;
}

fn exit_market_function(market_info: ExitMarketInfo) -> bool {
    // if trade in for certain time
    if market_info.index - market_info.index_in >= 2 {
        return true;
    }
    return false;
}

fn main() {
    let _start = Instant::now();

    let mydata = vec![
        vec![1.0, 5.3, 0.5, 5.3, 1.0, 5.3, 1.0],
        vec![0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0],
    ];

	// backtest arguments
	// 	values
	// 	holding
	// 	default_amt
	// 	enter_market_function
	// 	exit_market_function
    let _returns = backtest(
        mydata,
        100,
        5,
        &enter_market_function,
        &exit_market_function,
    );

    let _out = TradeInputResults { returns: _returns };
    let duration = _start.elapsed();

    println!("{:#?}", _out);
    println!("{:#?}", duration);
}

// CONSOLE OUTPUT

//	 [1.0, 5.3, 0.5, 5.3, 1.0, 5.3, 1.0]
//	 TradeInputResults {
//	     returns: BacktestResults {
//	         calculated_returns: 0.5,
//	         tradesin: [],
//	         tradesout: [
//	             TradeActionOut {
//	                 index_in: 2,
//	                 price_in: 0.5,
//	                 amt: 5,
//	                 index_out: 4,
//	                 price_out: 1.0,
//	                 diff: 0.5,
//	             },
//	         ],
//	     },
//	 }
//	 57.715µs

Dependencies

~0.3–1MB
~21K SLoC