feat(stock-pool): unify target execution, durable intent state and ETF rules
This commit is contained in:
@@ -5,6 +5,7 @@ use chrono::NaiveDate;
|
||||
use crate::events::OrderSide;
|
||||
use crate::fixed_point::{FixedChinaAShareCostModel, FixedMoney, FixedTradingCost};
|
||||
use crate::risk_control::TradingConstraintConfig;
|
||||
use crate::Instrument;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TradingCost {
|
||||
@@ -35,6 +36,17 @@ impl TradingCost {
|
||||
pub trait CostModel {
|
||||
fn calculate(&self, date: NaiveDate, side: OrderSide, gross_amount: f64) -> TradingCost;
|
||||
|
||||
fn calculate_for_instrument(&self, date: NaiveDate, side: OrderSide, gross_amount: f64, _instrument: Option<&Instrument>) -> TradingCost {
|
||||
self.calculate(date, side, gross_amount)
|
||||
}
|
||||
|
||||
fn calculate_with_order_state_for_instrument(
|
||||
&self, date: NaiveDate, side: OrderSide, gross_amount: f64,
|
||||
order_id: Option<u64>, commission_state: &mut BTreeMap<u64,f64>, _instrument: Option<&Instrument>,
|
||||
) -> TradingCost {
|
||||
self.calculate_with_order_state(date, side, gross_amount, order_id, commission_state)
|
||||
}
|
||||
|
||||
fn calculate_with_order_state(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
@@ -215,6 +227,27 @@ impl ChinaAShareCostModel {
|
||||
}
|
||||
|
||||
impl CostModel for ChinaAShareCostModel {
|
||||
fn calculate_for_instrument(&self, date: NaiveDate, side: OrderSide, gross_amount: f64, instrument: Option<&Instrument>) -> TradingCost {
|
||||
let mut cost = self.calculate(date, side, gross_amount);
|
||||
if instrument.is_some_and(Instrument::is_exchange_traded_fund) {
|
||||
cost.stamp_tax = 0.0;
|
||||
cost.transfer_fee = 0.0;
|
||||
}
|
||||
cost
|
||||
}
|
||||
|
||||
fn calculate_with_order_state_for_instrument(
|
||||
&self, date: NaiveDate, side: OrderSide, gross_amount: f64,
|
||||
order_id: Option<u64>, commission_state: &mut BTreeMap<u64,f64>, instrument: Option<&Instrument>,
|
||||
) -> TradingCost {
|
||||
let mut cost = self.calculate_with_order_state(date, side, gross_amount, order_id, commission_state);
|
||||
if instrument.is_some_and(Instrument::is_exchange_traded_fund) {
|
||||
cost.stamp_tax = 0.0;
|
||||
cost.transfer_fee = 0.0;
|
||||
}
|
||||
cost
|
||||
}
|
||||
|
||||
fn calculate(&self, date: NaiveDate, side: OrderSide, gross_amount: f64) -> TradingCost {
|
||||
if gross_amount <= 0.0 {
|
||||
return TradingCost {
|
||||
@@ -273,6 +306,25 @@ impl CostModel for ChinaAShareCostModel {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fund_fees_use_admitted_instrument_type_and_share_the_order_commission_budget() {
|
||||
let day=NaiveDate::from_ymd_opt(2026,9,11).unwrap();
|
||||
let model=ChinaAShareCostModel::from_trading_constraints(TradingConstraintConfig{commission_rate:0.0003,minimum_commission:5.,transfer_fee_rate:0.00001,..Default::default()});
|
||||
let mut instrument=Instrument{symbol:"510300.SH".into(),name:"fixture".into(),board:"ETF".into(),round_lot:100,listed_at:Some(day),delisted_at:None,status:"active".into()};
|
||||
for side in [OrderSide::Buy,OrderSide::Sell] {
|
||||
let cost=model.calculate_for_instrument(day,side,10_000.,Some(&instrument));
|
||||
assert_eq!(cost.commission,5.);assert_eq!(cost.stamp_tax,0.);assert_eq!(cost.transfer_fee,0.);
|
||||
let mut state=BTreeMap::new();
|
||||
let one=model.calculate_with_order_state_for_instrument(day,side,1_000.,Some(1),&mut state,Some(&instrument));
|
||||
let two=model.calculate_with_order_state_for_instrument(day,side,9_000.,Some(1),&mut state,Some(&instrument));
|
||||
assert_eq!(one.total()+two.total(),cost.total());
|
||||
}
|
||||
instrument.board="SH".into();
|
||||
let stock=model.calculate_for_instrument(day,OrderSide::Sell,10_000.,Some(&instrument));
|
||||
assert_eq!(stock.stamp_tax,5.);assert_eq!(stock.transfer_fee,0.1);
|
||||
assert_eq!(stock.total(),model.calculate(day,OrderSide::Sell,10_000.).total());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_quantizes_fees_to_micro_yuan() {
|
||||
let model = ChinaAShareCostModel::default();
|
||||
|
||||
Reference in New Issue
Block a user