Expose richer factor maps to platform expressions
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use chrono::{Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime};
|
||||
use rhai::{Dynamic, Engine, Scope};
|
||||
use rhai::{Dynamic, Engine, Map, Scope};
|
||||
|
||||
use crate::data::{DailyMarketSnapshot, EligibleUniverseSnapshot, PriceField};
|
||||
use crate::engine::BacktestError;
|
||||
@@ -114,9 +114,11 @@ struct DayExpressionState {
|
||||
benchmark_ma5: f64,
|
||||
benchmark_ma10: f64,
|
||||
benchmark_ma20: f64,
|
||||
benchmark_ma30: f64,
|
||||
signal_ma5: f64,
|
||||
signal_ma10: f64,
|
||||
signal_ma20: f64,
|
||||
signal_ma30: f64,
|
||||
cash: f64,
|
||||
market_value: f64,
|
||||
total_equity: f64,
|
||||
@@ -165,6 +167,7 @@ struct StockExpressionState {
|
||||
stock_ma5: f64,
|
||||
stock_ma10: f64,
|
||||
stock_ma20: f64,
|
||||
stock_ma30: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -575,6 +578,10 @@ impl PlatformExprStrategy {
|
||||
.data
|
||||
.market_decision_close_moving_average(date, &self.config.signal_symbol, 20)
|
||||
.unwrap_or(benchmark_ma10);
|
||||
let benchmark_ma30 = ctx
|
||||
.data
|
||||
.market_decision_close_moving_average(date, &self.config.signal_symbol, 30)
|
||||
.unwrap_or(benchmark_ma20);
|
||||
let cash = ctx.portfolio.cash();
|
||||
let market_value = ctx.portfolio.positions().values().map(|position| position.market_value()).sum::<f64>();
|
||||
let total_equity = cash + market_value;
|
||||
@@ -590,9 +597,11 @@ impl PlatformExprStrategy {
|
||||
benchmark_ma5,
|
||||
benchmark_ma10,
|
||||
benchmark_ma20,
|
||||
benchmark_ma30,
|
||||
signal_ma5: benchmark_ma5,
|
||||
signal_ma10: benchmark_ma10,
|
||||
signal_ma20: benchmark_ma20,
|
||||
signal_ma30: benchmark_ma30,
|
||||
cash,
|
||||
market_value,
|
||||
total_equity,
|
||||
@@ -646,6 +655,10 @@ impl PlatformExprStrategy {
|
||||
.data
|
||||
.market_decision_close_moving_average(date, symbol, 20)
|
||||
.unwrap_or(stock_ma_long);
|
||||
let stock_ma30 = ctx
|
||||
.data
|
||||
.market_decision_close_moving_average(date, symbol, 30)
|
||||
.unwrap_or(stock_ma20);
|
||||
|
||||
Ok(StockExpressionState {
|
||||
symbol: symbol.to_string(),
|
||||
@@ -676,6 +689,7 @@ impl PlatformExprStrategy {
|
||||
stock_ma5,
|
||||
stock_ma10,
|
||||
stock_ma20,
|
||||
stock_ma30,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -694,6 +708,7 @@ impl PlatformExprStrategy {
|
||||
scope.push("benchmark_ma5", day.benchmark_ma5);
|
||||
scope.push("benchmark_ma10", day.benchmark_ma10);
|
||||
scope.push("benchmark_ma20", day.benchmark_ma20);
|
||||
scope.push("benchmark_ma30", day.benchmark_ma30);
|
||||
scope.push("benchmark_ma_short", day.benchmark_ma_short);
|
||||
scope.push("benchmark_ma_long", day.benchmark_ma_long);
|
||||
scope.push("cash", day.cash);
|
||||
@@ -713,6 +728,37 @@ impl PlatformExprStrategy {
|
||||
scope.push("weekday", day.weekday);
|
||||
scope.push("is_month_start", day.is_month_start);
|
||||
scope.push("is_month_end", day.is_month_end);
|
||||
scope.push("signal_ma30", day.signal_ma30);
|
||||
let mut day_factors = Map::new();
|
||||
day_factors.insert("signal_close".into(), Dynamic::from(day.signal_close));
|
||||
day_factors.insert("benchmark_close".into(), Dynamic::from(day.benchmark_close));
|
||||
day_factors.insert("signal_ma5".into(), Dynamic::from(day.signal_ma5));
|
||||
day_factors.insert("signal_ma10".into(), Dynamic::from(day.signal_ma10));
|
||||
day_factors.insert("signal_ma20".into(), Dynamic::from(day.signal_ma20));
|
||||
day_factors.insert("signal_ma30".into(), Dynamic::from(day.signal_ma30));
|
||||
day_factors.insert("benchmark_ma5".into(), Dynamic::from(day.benchmark_ma5));
|
||||
day_factors.insert("benchmark_ma10".into(), Dynamic::from(day.benchmark_ma10));
|
||||
day_factors.insert("benchmark_ma20".into(), Dynamic::from(day.benchmark_ma20));
|
||||
day_factors.insert("benchmark_ma30".into(), Dynamic::from(day.benchmark_ma30));
|
||||
day_factors.insert("benchmark_ma_short".into(), Dynamic::from(day.benchmark_ma_short));
|
||||
day_factors.insert("benchmark_ma_long".into(), Dynamic::from(day.benchmark_ma_long));
|
||||
day_factors.insert("cash".into(), Dynamic::from(day.cash));
|
||||
day_factors.insert("market_value".into(), Dynamic::from(day.market_value));
|
||||
day_factors.insert("total_equity".into(), Dynamic::from(day.total_equity));
|
||||
day_factors.insert("current_exposure".into(), Dynamic::from(day.current_exposure));
|
||||
day_factors.insert("position_count".into(), Dynamic::from(day.position_count));
|
||||
day_factors.insert("max_positions".into(), Dynamic::from(day.max_positions));
|
||||
day_factors.insert("refresh_rate".into(), Dynamic::from(day.refresh_rate));
|
||||
day_factors.insert("year".into(), Dynamic::from(day.year));
|
||||
day_factors.insert("month".into(), Dynamic::from(day.month));
|
||||
day_factors.insert("quarter".into(), Dynamic::from(day.quarter));
|
||||
day_factors.insert("day_of_month".into(), Dynamic::from(day.day_of_month));
|
||||
day_factors.insert("day_of_year".into(), Dynamic::from(day.day_of_year));
|
||||
day_factors.insert("week_of_year".into(), Dynamic::from(day.week_of_year));
|
||||
day_factors.insert("weekday".into(), Dynamic::from(day.weekday));
|
||||
day_factors.insert("is_month_start".into(), Dynamic::from(day.is_month_start));
|
||||
day_factors.insert("is_month_end".into(), Dynamic::from(day.is_month_end));
|
||||
scope.push("day_factors", day_factors);
|
||||
if let Some(stock) = stock {
|
||||
scope.push("symbol", stock.symbol.clone());
|
||||
scope.push("market_cap", stock.market_cap);
|
||||
@@ -743,6 +789,46 @@ impl PlatformExprStrategy {
|
||||
scope.push("stock_ma5", stock.stock_ma5);
|
||||
scope.push("stock_ma10", stock.stock_ma10);
|
||||
scope.push("stock_ma20", stock.stock_ma20);
|
||||
scope.push("stock_ma30", stock.stock_ma30);
|
||||
scope.push("ma5", stock.stock_ma5);
|
||||
scope.push("ma10", stock.stock_ma10);
|
||||
scope.push("ma20", stock.stock_ma20);
|
||||
scope.push("ma30", stock.stock_ma30);
|
||||
let mut factors = Map::new();
|
||||
factors.insert("symbol".into(), Dynamic::from(stock.symbol.clone()));
|
||||
factors.insert("market_cap".into(), Dynamic::from(stock.market_cap));
|
||||
factors.insert("free_float_cap".into(), Dynamic::from(stock.free_float_cap));
|
||||
factors.insert("pe_ttm".into(), Dynamic::from(stock.pe_ttm));
|
||||
factors.insert("turnover_ratio".into(), Dynamic::from(stock.turnover_ratio));
|
||||
factors.insert(
|
||||
"effective_turnover_ratio".into(),
|
||||
Dynamic::from(stock.effective_turnover_ratio),
|
||||
);
|
||||
factors.insert("open".into(), Dynamic::from(stock.open));
|
||||
factors.insert("close".into(), Dynamic::from(stock.close));
|
||||
factors.insert("last_price".into(), Dynamic::from(stock.last));
|
||||
factors.insert("prev_close".into(), Dynamic::from(stock.prev_close));
|
||||
factors.insert("upper_limit".into(), Dynamic::from(stock.upper_limit));
|
||||
factors.insert("lower_limit".into(), Dynamic::from(stock.lower_limit));
|
||||
factors.insert("price_tick".into(), Dynamic::from(stock.price_tick));
|
||||
factors.insert("round_lot".into(), Dynamic::from(stock.round_lot));
|
||||
factors.insert("paused".into(), Dynamic::from(stock.paused));
|
||||
factors.insert("is_st".into(), Dynamic::from(stock.is_st));
|
||||
factors.insert("is_kcb".into(), Dynamic::from(stock.is_kcb));
|
||||
factors.insert("is_one_yuan".into(), Dynamic::from(stock.is_one_yuan));
|
||||
factors.insert("is_new_listing".into(), Dynamic::from(stock.is_new_listing));
|
||||
factors.insert("allow_buy".into(), Dynamic::from(stock.allow_buy));
|
||||
factors.insert("allow_sell".into(), Dynamic::from(stock.allow_sell));
|
||||
factors.insert("listed_days".into(), Dynamic::from(stock.listed_days));
|
||||
factors.insert("stock_ma5".into(), Dynamic::from(stock.stock_ma5));
|
||||
factors.insert("stock_ma10".into(), Dynamic::from(stock.stock_ma10));
|
||||
factors.insert("stock_ma20".into(), Dynamic::from(stock.stock_ma20));
|
||||
factors.insert("stock_ma30".into(), Dynamic::from(stock.stock_ma30));
|
||||
factors.insert("ma5".into(), Dynamic::from(stock.stock_ma5));
|
||||
factors.insert("ma10".into(), Dynamic::from(stock.stock_ma10));
|
||||
factors.insert("ma20".into(), Dynamic::from(stock.stock_ma20));
|
||||
factors.insert("ma30".into(), Dynamic::from(stock.stock_ma30));
|
||||
scope.push("factors", factors);
|
||||
}
|
||||
if let Some(position) = position {
|
||||
scope.push("avg_cost", position.avg_cost);
|
||||
@@ -750,6 +836,7 @@ impl PlatformExprStrategy {
|
||||
scope.push("holding_return", position.holding_return);
|
||||
scope.push("quantity", position.quantity);
|
||||
scope.push("sellable_qty", position.sellable_qty);
|
||||
scope.push("profit_pct", position.holding_return * 100.0);
|
||||
}
|
||||
scope
|
||||
}
|
||||
@@ -944,9 +1031,16 @@ impl PlatformExprStrategy {
|
||||
"price_tick" => Some(stock.price_tick),
|
||||
"round_lot" => Some(stock.round_lot as f64),
|
||||
"listed_days" => Some(stock.listed_days as f64),
|
||||
"stock_ma_short" | "stock_ma5" => Some(stock.stock_ma_short),
|
||||
"stock_ma_mid" | "stock_ma10" => Some(stock.stock_ma_mid),
|
||||
"stock_ma_long" | "stock_ma20" => Some(stock.stock_ma_long),
|
||||
"stock_ma_short" => Some(stock.stock_ma_short),
|
||||
"stock_ma5" => Some(stock.stock_ma5),
|
||||
"stock_ma_mid" => Some(stock.stock_ma_mid),
|
||||
"stock_ma10" => Some(stock.stock_ma10),
|
||||
"stock_ma_long" => Some(stock.stock_ma_long),
|
||||
"stock_ma20" => Some(stock.stock_ma20),
|
||||
"stock_ma30" | "ma30" => Some(stock.stock_ma30),
|
||||
"ma5" => Some(stock.stock_ma5),
|
||||
"ma10" => Some(stock.stock_ma10),
|
||||
"ma20" => Some(stock.stock_ma20),
|
||||
"allow_buy" => Some(if stock.allow_buy { 1.0 } else { 0.0 }),
|
||||
"allow_sell" => Some(if stock.allow_sell { 1.0 } else { 0.0 }),
|
||||
"paused" => Some(if stock.paused { 1.0 } else { 0.0 }),
|
||||
|
||||
Reference in New Issue
Block a user