Expand platform strategy runtime helpers

This commit is contained in:
boris
2026-04-21 21:45:18 -07:00
parent 8b55fdabc7
commit 1ba3264990

View File

@@ -115,18 +115,34 @@ struct DayExpressionState {
signal_ma5: f64,
signal_ma10: f64,
signal_ma20: f64,
cash: f64,
market_value: f64,
total_equity: f64,
current_exposure: f64,
position_count: i64,
max_positions: i64,
refresh_rate: i64,
year: i64,
month: i64,
day_of_month: i64,
weekday: i64,
}
#[derive(Debug, Clone)]
struct StockExpressionState {
market_cap: f64,
free_float_cap: f64,
pe_ttm: f64,
turnover_ratio: f64,
effective_turnover_ratio: f64,
open: f64,
close: f64,
last: f64,
prev_close: f64,
upper_limit: f64,
lower_limit: f64,
price_tick: f64,
round_lot: i64,
paused: bool,
is_st: bool,
is_kcb: bool,
@@ -166,6 +182,23 @@ impl PlatformExprStrategy {
engine.register_fn("abs", |value: f64| value.abs());
engine.register_fn("min", |lhs: f64, rhs: f64| lhs.min(rhs));
engine.register_fn("max", |lhs: f64, rhs: f64| lhs.max(rhs));
engine.register_fn("sqrt", |value: f64| value.sqrt());
engine.register_fn("pow", |lhs: f64, rhs: f64| lhs.powf(rhs));
engine.register_fn("log", |value: f64| value.ln());
engine.register_fn("exp", |value: f64| value.exp());
engine.register_fn("clamp", |value: f64, low: f64, high: f64| value.clamp(low, high));
engine.register_fn("between", |value: f64, low: f64, high: f64| value >= low && value <= high);
engine.register_fn("nz", |value: f64, fallback: f64| if value.is_finite() { value } else { fallback });
engine.register_fn("safe_div", |lhs: f64, rhs: f64, fallback: f64| {
if rhs.abs() <= f64::EPSILON {
fallback
} else {
lhs / rhs
}
});
engine.register_fn("iff", |condition: bool, when_true: Dynamic, when_false: Dynamic| {
if condition { when_true } else { when_false }
});
Self { config, engine }
}
@@ -527,6 +560,10 @@ impl PlatformExprStrategy {
.data
.market_decision_close_moving_average(date, &self.config.signal_symbol, 20)
.unwrap_or(benchmark_ma10);
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;
let current_exposure = if total_equity > 0.0 { market_value / total_equity } else { 0.0 };
Ok(DayExpressionState {
signal_close,
@@ -539,6 +576,17 @@ impl PlatformExprStrategy {
signal_ma5: benchmark_ma5,
signal_ma10: benchmark_ma10,
signal_ma20: benchmark_ma20,
cash,
market_value,
total_equity,
current_exposure,
position_count: ctx.portfolio.positions().len() as i64,
max_positions: self.config.max_positions as i64,
refresh_rate: self.config.refresh_rate as i64,
year: date.year() as i64,
month: date.month() as i64,
day_of_month: date.day() as i64,
weekday: date.weekday().number_from_monday() as i64,
})
}
@@ -551,6 +599,7 @@ impl PlatformExprStrategy {
let market = ctx.data.require_market(date, symbol)?;
let factor = ctx.data.require_factor(date, symbol)?;
let candidate = ctx.data.require_candidate(date, symbol)?;
let instrument = ctx.data.instrument(symbol);
let stock_ma_short = ctx
.data
.market_decision_close_moving_average(date, symbol, self.config.stock_short_ma_days)
@@ -579,12 +628,17 @@ impl PlatformExprStrategy {
Ok(StockExpressionState {
market_cap: factor.market_cap_bn,
free_float_cap: factor.free_float_cap_bn,
pe_ttm: factor.pe_ttm,
turnover_ratio: factor.turnover_ratio.unwrap_or(0.0),
effective_turnover_ratio: factor.effective_turnover_ratio.unwrap_or(0.0),
open: market.day_open,
close: market.close,
last: market.last_price,
prev_close: market.prev_close,
upper_limit: market.upper_limit,
lower_limit: market.lower_limit,
price_tick: market.price_tick,
round_lot: instrument.map(|item| item.effective_round_lot()).unwrap_or(100) as i64,
paused: market.paused || candidate.is_paused,
is_st: candidate.is_st || self.special_name(ctx, symbol),
is_kcb: candidate.is_kcb,
@@ -619,9 +673,24 @@ impl PlatformExprStrategy {
scope.push("benchmark_ma20", day.benchmark_ma20);
scope.push("benchmark_ma_short", day.benchmark_ma_short);
scope.push("benchmark_ma_long", day.benchmark_ma_long);
scope.push("cash", day.cash);
scope.push("available_cash", day.cash);
scope.push("market_value", day.market_value);
scope.push("total_equity", day.total_equity);
scope.push("current_exposure", day.current_exposure);
scope.push("position_count", day.position_count);
scope.push("max_positions", day.max_positions);
scope.push("refresh_rate", day.refresh_rate);
scope.push("year", day.year);
scope.push("month", day.month);
scope.push("day_of_month", day.day_of_month);
scope.push("weekday", day.weekday);
if let Some(stock) = stock {
scope.push("market_cap", stock.market_cap);
scope.push("free_float_cap", stock.free_float_cap);
scope.push("pe_ttm", stock.pe_ttm);
scope.push("turnover_ratio", stock.turnover_ratio);
scope.push("effective_turnover_ratio", stock.effective_turnover_ratio);
scope.push("open", stock.open);
scope.push("close", stock.close);
scope.push("last", stock.last);
@@ -629,6 +698,8 @@ impl PlatformExprStrategy {
scope.push("prev_close", stock.prev_close);
scope.push("upper_limit", stock.upper_limit);
scope.push("lower_limit", stock.lower_limit);
scope.push("price_tick", stock.price_tick);
scope.push("round_lot", stock.round_lot);
scope.push("paused", stock.paused);
scope.push("is_st", stock.is_st);
scope.push("is_kcb", stock.is_kcb);