diff --git a/crates/fidc-core/src/platform_expr_strategy.rs b/crates/fidc-core/src/platform_expr_strategy.rs index cf41d57..68ac50e 100644 --- a/crates/fidc-core/src/platform_expr_strategy.rs +++ b/crates/fidc-core/src/platform_expr_strategy.rs @@ -377,11 +377,16 @@ struct StockExpressionState { #[derive(Debug, Clone)] struct PositionExpressionState { + order_book_id: String, avg_cost: f64, + avg_price: f64, current_price: f64, + prev_close: f64, holding_return: f64, quantity: i64, sellable_qty: i64, + sellable: i64, + closable: i64, old_quantity: i64, bought_quantity: i64, sold_quantity: i64, @@ -391,6 +396,7 @@ struct PositionExpressionState { sold_value: f64, transaction_cost: f64, market_value: f64, + equity: f64, value_percent: f64, unrealized_pnl: f64, realized_pnl: f64, @@ -584,11 +590,17 @@ impl PlatformExprStrategy { "ma20", "ma30", "factors", + "order_book_id", "avg_cost", + "avg_price", "current_price", + "position_prev_close", + "prev_position_close", "holding_return", "quantity", "sellable_qty", + "sellable", + "closable", "old_quantity", "buy_quantity", "sell_quantity", @@ -600,6 +612,7 @@ impl PlatformExprStrategy { "sold_value", "transaction_cost", "position_market_value", + "equity", "value_percent", "unrealized_pnl", "realized_pnl", @@ -1795,11 +1808,17 @@ impl PlatformExprStrategy { } } if let Some(position) = position { + scope.push("order_book_id", position.order_book_id.clone()); scope.push("avg_cost", position.avg_cost); + scope.push("avg_price", position.avg_price); scope.push("current_price", position.current_price); + scope.push("position_prev_close", position.prev_close); + scope.push("prev_position_close", position.prev_close); scope.push("holding_return", position.holding_return); scope.push("quantity", position.quantity); scope.push("sellable_qty", position.sellable_qty); + scope.push("sellable", position.sellable); + scope.push("closable", position.closable); scope.push("old_quantity", position.old_quantity); scope.push("buy_quantity", position.bought_quantity); scope.push("sell_quantity", position.sold_quantity); @@ -1811,6 +1830,7 @@ impl PlatformExprStrategy { scope.push("sold_value", position.sold_value); scope.push("transaction_cost", position.transaction_cost); scope.push("position_market_value", position.market_value); + scope.push("equity", position.equity); scope.push("value_percent", position.value_percent); scope.push("unrealized_pnl", position.unrealized_pnl); scope.push("realized_pnl", position.realized_pnl); @@ -3506,11 +3526,16 @@ impl PlatformExprStrategy { 0.0 }; let position_state = PositionExpressionState { + order_book_id: position.symbol.clone(), avg_cost: position.average_cost, + avg_price: position.average_cost, current_price, + prev_close: stock.prev_close, holding_return, quantity: position.quantity as i64, sellable_qty: position.sellable_qty(date) as i64, + sellable: position.sellable_qty(date) as i64, + closable: position.sellable_qty(date) as i64, old_quantity: position.day_start_quantity() as i64, bought_quantity: position.bought_quantity() as i64, sold_quantity: position.sold_quantity() as i64, @@ -3520,6 +3545,7 @@ impl PlatformExprStrategy { sold_value: position.sold_value(), transaction_cost: position.transaction_cost(), market_value, + equity: market_value, value_percent, unrealized_pnl: position.unrealized_pnl(), realized_pnl: position.realized_pnl, @@ -5419,20 +5445,13 @@ mod tests { cfg.benchmark_short_ma_days = 1; cfg.benchmark_long_ma_days = 1; cfg.stop_loss_expr = concat!( - "old_quantity == 100 && buy_quantity == 100 && sell_quantity == 50", - " && bought_quantity == 100 && sold_quantity == 50", - " && buy_avg_price == 9.0 && sell_avg_price == 10.0", - " && bought_value == 900.0 && sold_value == 500.0", - " && transaction_cost == 2.0 && position_market_value > 0.0", - " && value_percent > 0.0 && unrealized_pnl > 0.0", - " && realized_pnl > 0.0 && pnl > 0.0", - " && day_trade_quantity_delta == 50 && trading_pnl > 90.0", - " && available_cash == cash && frozen_cash == 0.0", - " && total_value == total_equity && portfolio_value == total_equity", - " && starting_cash == 1000000.0 && unit_net_value > 1.0", - " && static_unit_net_value > 1.0 && daily_pnl > 290.0", - " && daily_returns > 0.0 && total_returns > 0.0", - " && cash_liabilities == 0.0" + "order_book_id == \"000001.SZ\" && avg_price == avg_cost", + " && sellable == sellable_qty && closable == sellable_qty", + " && equity == position_market_value", + " && position_prev_close == prev_position_close", + " && old_quantity == 100 && buy_quantity == 100", + " && sell_quantity == 50 && trading_pnl > 90.0", + " && daily_pnl > 290.0 && total_returns > 0.0" ) .to_string(); let mut strategy = PlatformExprStrategy::new(cfg); diff --git a/crates/fidc-core/src/strategy_ai.rs b/crates/fidc-core/src/strategy_ai.rs index c7d7f42..0bfa161 100644 --- a/crates/fidc-core/src/strategy_ai.rs +++ b/crates/fidc-core/src/strategy_ai.rs @@ -185,10 +185,10 @@ pub fn built_in_strategy_manual() -> StrategyAiManual { ManualField { name: "current_price".to_string(), field_type: "float".to_string(), detail: "当前盘中价格。".to_string() }, ManualField { name: "holding_return".to_string(), field_type: "float".to_string(), detail: "持仓收益率,小数。".to_string() }, ManualField { name: "profit_pct".to_string(), field_type: "float".to_string(), detail: "持仓收益率,百分比。".to_string() }, - ManualField { name: "quantity/sellable_qty".to_string(), field_type: "int".to_string(), detail: "持仓数量与可卖数量。".to_string() }, + ManualField { name: "order_book_id/quantity/sellable_qty/sellable/closable".to_string(), field_type: "string/int".to_string(), detail: "持仓代码、持仓数量与可卖数量;sellable/closable 是 RQAlpha StockPosition 常用别名。".to_string() }, ManualField { name: "old_quantity/buy_quantity/sell_quantity".to_string(), field_type: "int".to_string(), detail: "交易日开始时老仓数量、当日买入数量、当日卖出数量。buy_quantity/sell_quantity 也可写成 bought_quantity/sold_quantity。".to_string() }, ManualField { name: "buy_avg_price/sell_avg_price/bought_value/sold_value".to_string(), field_type: "float".to_string(), detail: "当日买入均价、卖出均价、买入成交额、卖出成交额。".to_string() }, - ManualField { name: "position_market_value/value_percent".to_string(), field_type: "float".to_string(), detail: "当前持仓市值,以及该持仓市值占账户总权益比例。".to_string() }, + ManualField { name: "avg_price/position_prev_close/position_market_value/equity/value_percent".to_string(), field_type: "float".to_string(), detail: "平均开仓价、持仓昨收、市值/权益,以及该持仓市值占账户总权益比例;avg_price/equity 对齐 RQAlpha 持仓对象别名。".to_string() }, ManualField { name: "unrealized_pnl/realized_pnl/pnl/transaction_cost".to_string(), field_type: "float".to_string(), detail: "未实现盈亏、累计已实现盈亏、总持仓盈亏和当日交易成本。".to_string() }, ManualField { name: "trading_pnl/position_pnl".to_string(), field_type: "float".to_string(), detail: "当日交易收益和昨仓持有收益,口径更接近 rqalpha StockPosition。".to_string() }, ManualField { name: "dividend_receivable".to_string(), field_type: "float".to_string(), detail: "当前 symbol 尚未到账的应收分红。".to_string() }, diff --git a/docs/rqalpha-gap-roadmap.md b/docs/rqalpha-gap-roadmap.md index 0f17981..93170bc 100644 --- a/docs/rqalpha-gap-roadmap.md +++ b/docs/rqalpha-gap-roadmap.md @@ -45,6 +45,8 @@ current alignment pass. - [x] `position_pnl` - [x] `dividend_receivable` - [x] richer position lifecycle fields exposed to strategy runtime +- [x] RQAlpha-style stock position aliases (`order_book_id`, `avg_price`, + `sellable`, `closable`, `equity`, `position_prev_close`) ### Phase 6: Strategy data API parity