支持按持仓成交均价止盈止损
This commit is contained in:
@@ -575,6 +575,7 @@ pub enum PlatformExplicitActionStage {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PlatformStopTakeReferencePriceMode {
|
||||
PositionCostBasis,
|
||||
PositionAverageEntryPrice,
|
||||
SignalDayPostAdjustedClose,
|
||||
}
|
||||
|
||||
@@ -11674,6 +11675,7 @@ impl PlatformExprStrategy {
|
||||
entry_avg_price
|
||||
}
|
||||
}
|
||||
PlatformStopTakeReferencePriceMode::PositionAverageEntryPrice => entry_avg_price,
|
||||
PlatformStopTakeReferencePriceMode::SignalDayPostAdjustedClose => {
|
||||
let entry_date = self.position_entry_dates.get(symbol).copied().ok_or_else(|| {
|
||||
BacktestError::Execution(format!(
|
||||
@@ -19112,6 +19114,60 @@ mod tests {
|
||||
.expect("quoted stop check"),
|
||||
(true, false),
|
||||
);
|
||||
|
||||
let mut borderline_portfolio = PortfolioState::new(1_000_000.0);
|
||||
let borderline_position = borderline_portfolio.position_mut(symbol);
|
||||
borderline_position.buy(prev_date, 9_300, 25.495);
|
||||
borderline_position.average_cost = 25.51;
|
||||
let borderline_context = StrategyContext {
|
||||
execution_date: date,
|
||||
decision_date: date,
|
||||
decision_index: 40,
|
||||
data: &with_quote,
|
||||
portfolio: &borderline_portfolio,
|
||||
futures_account: None,
|
||||
open_orders: &[],
|
||||
dynamic_universe: None,
|
||||
subscriptions: &subscriptions,
|
||||
process_events: &[],
|
||||
active_process_event: None,
|
||||
active_datetime: None,
|
||||
order_events: &[],
|
||||
fills: &[],
|
||||
};
|
||||
let mut average_price_cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
average_price_cfg.rotation_enabled = false;
|
||||
average_price_cfg.matching_type = MatchingType::CurrentBarClose;
|
||||
average_price_cfg.intraday_execution_time =
|
||||
Some(NaiveTime::from_hms_opt(9, 30, 0).expect("time"));
|
||||
average_price_cfg.signal_symbol = symbol.to_string();
|
||||
average_price_cfg.stop_loss_expr = "holding_return <= -0.08".to_string();
|
||||
average_price_cfg.take_profit_expr.clear();
|
||||
average_price_cfg.stop_take_reference_price_mode =
|
||||
PlatformStopTakeReferencePriceMode::PositionAverageEntryPrice;
|
||||
let average_price_strategy = PlatformExprStrategy::new(average_price_cfg.clone());
|
||||
let borderline_day = average_price_strategy
|
||||
.day_state(&borderline_context, date)
|
||||
.expect("borderline day state");
|
||||
assert_eq!(
|
||||
average_price_strategy
|
||||
.stop_take_action(&borderline_context, date, date, &borderline_day, symbol,)
|
||||
.expect("average entry price stop check"),
|
||||
(false, false),
|
||||
);
|
||||
|
||||
average_price_cfg.stop_take_reference_price_mode =
|
||||
PlatformStopTakeReferencePriceMode::PositionCostBasis;
|
||||
let cost_basis_strategy = PlatformExprStrategy::new(average_price_cfg);
|
||||
let borderline_day = cost_basis_strategy
|
||||
.day_state(&borderline_context, date)
|
||||
.expect("cost basis day state");
|
||||
assert_eq!(
|
||||
cost_basis_strategy
|
||||
.stop_take_action(&borderline_context, date, date, &borderline_day, symbol,)
|
||||
.expect("cost basis stop check"),
|
||||
(true, false),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1259,6 +1259,9 @@ fn parse_stop_take_reference_price_mode(
|
||||
"position_cost_basis" | "position_cost" | "execution_cost_basis" => {
|
||||
Ok(PlatformStopTakeReferencePriceMode::PositionCostBasis)
|
||||
}
|
||||
"position_average_entry_price" | "position_avg_price" | "average_entry_price" => {
|
||||
Ok(PlatformStopTakeReferencePriceMode::PositionAverageEntryPrice)
|
||||
}
|
||||
"signal_day_post_adjusted_close"
|
||||
| "signal_post_adjusted_close"
|
||||
| "model_signal_post_adjusted_close" => {
|
||||
@@ -4497,6 +4500,24 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_position_average_entry_stop_take_reference_price_mode() {
|
||||
let spec = serde_json::json!({
|
||||
"runtimeExpressions": {
|
||||
"risk": {
|
||||
"stopTakeReferencePriceMode": "position_average_entry_price"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let cfg = platform_expr_config_from_value("", "", &spec).expect("config");
|
||||
|
||||
assert_eq!(
|
||||
cfg.stop_take_reference_price_mode,
|
||||
PlatformStopTakeReferencePriceMode::PositionAverageEntryPrice
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_unknown_stop_take_reference_price_mode() {
|
||||
let spec = serde_json::json!({
|
||||
|
||||
Reference in New Issue
Block a user