chore: 更新 fidc-backtest-engine - 2026-05-22

This commit is contained in:
boris
2026-05-22 17:22:33 +08:00
parent 7dbd66b467
commit 3499d4aa74
8 changed files with 526 additions and 125 deletions
+18 -33
View File
@@ -2,6 +2,7 @@ use chrono::NaiveDate;
use crate::data::{CandidateEligibility, DailyMarketSnapshot, PriceField};
use crate::portfolio::Position;
use crate::risk_control::ChinaAShareRiskControl;
#[derive(Debug, Clone)]
pub struct RuleCheck {
@@ -47,20 +48,6 @@ pub trait EquityRuleHooks {
#[derive(Debug, Clone, Default)]
pub struct ChinaEquityRuleHooks;
impl ChinaEquityRuleHooks {
fn at_upper_limit(snapshot: &DailyMarketSnapshot, price_field: PriceField) -> bool {
snapshot.is_at_upper_limit_price(snapshot.buy_price(price_field))
}
fn at_lower_limit(snapshot: &DailyMarketSnapshot, price_field: PriceField) -> bool {
let check_price = match price_field {
PriceField::Last => snapshot.price(PriceField::Last),
_ => snapshot.sell_price(price_field),
};
snapshot.is_at_lower_limit_price(check_price)
}
}
impl EquityRuleHooks for ChinaEquityRuleHooks {
fn can_buy(
&self,
@@ -69,14 +56,14 @@ impl EquityRuleHooks for ChinaEquityRuleHooks {
candidate: &CandidateEligibility,
price_field: PriceField,
) -> RuleCheck {
if snapshot.paused || candidate.is_paused {
return RuleCheck::reject("paused");
}
if !candidate.allow_buy {
return RuleCheck::reject("buy disabled by eligibility flags");
}
if Self::at_upper_limit(snapshot, price_field) {
return RuleCheck::reject("open at or above upper limit");
if let Some(reason) = ChinaAShareRiskControl::buy_rejection_reason(
_execution_date,
candidate,
snapshot,
None,
ChinaAShareRiskControl::buy_check_price(snapshot, price_field),
) {
return RuleCheck::reject(reason);
}
RuleCheck::allow()
@@ -90,17 +77,15 @@ impl EquityRuleHooks for ChinaEquityRuleHooks {
position: &Position,
price_field: PriceField,
) -> RuleCheck {
if snapshot.paused || candidate.is_paused {
return RuleCheck::reject("paused");
}
if !candidate.allow_sell {
return RuleCheck::reject("sell disabled by eligibility flags");
}
if Self::at_lower_limit(snapshot, price_field) {
return RuleCheck::reject("open at or below lower limit");
}
if position.sellable_qty(execution_date) == 0 {
return RuleCheck::reject("t+1 sellable quantity is zero");
if let Some(reason) = ChinaAShareRiskControl::sell_rejection_reason(
execution_date,
candidate,
snapshot,
None,
Some(position),
ChinaAShareRiskControl::sell_check_price(snapshot, price_field),
) {
return RuleCheck::reject(reason);
}
RuleCheck::allow()