#backtesting #exit #market #strategy #enter #backtest #logic

rusty_backtest

纯Rust实现的任何入场/出场策略的超快速回测

3个版本

0.0.3 2019年9月14日
0.0.2 2019年9月2日
0.0.1 2019年9月2日

#109金融

MIT/Apache

11KB
129

Rusty Backtest

回测变得简单!

Rusty Backtest遵循一个简单的概念。入场-出场测试策略。

这就是一组逻辑返回一个布尔值(True/False)动作来入场和出场市场的地方。这可能是一对买卖,或者是一对卖出-买入。

一个出口入场测试的口头例子

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.

您可以在入场点和出场点同时链接逻辑测试。

入场/出场逻辑分解

入场市场规则

在这种情况下,我们向回测传递了一个包含2, 7的数组。把它想象成一个有7行2列的表格。

第一列是价格,对于回测来说是必需的。它是回测用来跟踪价格的市场价格。

第二列及以后是自定义数据值,可以用来通知入场出场逻辑。

在这种情况下,第二列是某个计算指标——假设这是DSI(David的特殊指标)。它可能是RSI或EMA或其他知名的技术指标。

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],
];

现在我们有了这些信息,我们只想在DSI高于 0.5 时进入市场。查看上面的数据——我们可以看到这只会发生一次(好指标)。注意:在这里,我们通过 market_info.data[1] 访问第二列的“DSI”。

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;
}

这个函数在数据的每一行上运行,并允许用户设置他们选择的策略。如果函数返回true 并且投资组合有钱,则回测将进入市场。如果函数返回false,则不采取任何行动。

出场市场规则

现在我们有了进入市场的方法。我们需要一个退出市场的方法!在这里,我们可以设置一些逻辑来退出市场。这里我们只想在交易超过2天后退出。这是一个愚蠢的策略,但你可以将任何布尔语句链接起来,创建一个更复杂、更成功的策略。

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;
}

完整示例

我们将上述入场和出场逻辑组合起来并运行完整的测试。我们可以看到这个测试非常小——但运行速度也非常快 57 纳秒

例如,在约1500个日股价上运行此程序需要 <2ms

使用以下示例进行复制和运行

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

依赖项

~0.4–1MB
~22K SLoC