Align China A-share costs with rqalpha rules

This commit is contained in:
boris
2026-04-22 21:57:24 -07:00
parent 650e2e8319
commit aca8292c72
5 changed files with 111 additions and 43 deletions

View File

@@ -1,5 +1,9 @@
use chrono::NaiveDate;
use crate::events::OrderSide;
pub const STOCK_PIT_TAX_CHANGE_DATE: (i32, u32, u32) = (2023, 8, 28);
#[derive(Debug, Clone, Copy)]
pub struct TradingCost {
pub commission: f64,
@@ -13,13 +17,14 @@ impl TradingCost {
}
pub trait CostModel {
fn calculate(&self, side: OrderSide, gross_amount: f64) -> TradingCost;
fn calculate(&self, date: NaiveDate, side: OrderSide, gross_amount: f64) -> TradingCost;
}
#[derive(Debug, Clone, Copy)]
pub struct ChinaAShareCostModel {
pub commission_rate: f64,
pub stamp_tax_rate: f64,
pub stamp_tax_rate_before_change: f64,
pub stamp_tax_rate_after_change: f64,
pub minimum_commission: f64,
}
@@ -27,14 +32,45 @@ impl Default for ChinaAShareCostModel {
fn default() -> Self {
Self {
commission_rate: 0.0003,
stamp_tax_rate: 0.001,
stamp_tax_rate_before_change: 0.001,
stamp_tax_rate_after_change: 0.0005,
minimum_commission: 5.0,
}
}
}
impl ChinaAShareCostModel {
pub fn commission_for(&self, gross_amount: f64) -> f64 {
if gross_amount <= 0.0 {
return 0.0;
}
(gross_amount * self.commission_rate).max(self.minimum_commission)
}
pub fn stamp_tax_rate_for(&self, date: NaiveDate) -> f64 {
let change_date = NaiveDate::from_ymd_opt(
STOCK_PIT_TAX_CHANGE_DATE.0,
STOCK_PIT_TAX_CHANGE_DATE.1,
STOCK_PIT_TAX_CHANGE_DATE.2,
)
.expect("valid pit tax change date");
if date < change_date {
self.stamp_tax_rate_before_change
} else {
self.stamp_tax_rate_after_change
}
}
pub fn stamp_tax_for(&self, date: NaiveDate, side: OrderSide, gross_amount: f64) -> f64 {
if gross_amount <= 0.0 || side == OrderSide::Buy {
return 0.0;
}
gross_amount * self.stamp_tax_rate_for(date)
}
}
impl CostModel for ChinaAShareCostModel {
fn calculate(&self, side: OrderSide, gross_amount: f64) -> TradingCost {
fn calculate(&self, date: NaiveDate, side: OrderSide, gross_amount: f64) -> TradingCost {
if gross_amount <= 0.0 {
return TradingCost {
commission: 0.0,
@@ -42,11 +78,8 @@ impl CostModel for ChinaAShareCostModel {
};
}
let commission = (gross_amount * self.commission_rate).max(self.minimum_commission);
let stamp_tax = match side {
OrderSide::Buy => 0.0,
OrderSide::Sell => gross_amount * self.stamp_tax_rate,
};
let commission = self.commission_for(gross_amount);
let stamp_tax = self.stamp_tax_for(date, side, gross_amount);
TradingCost {
commission,