修正平台表达式回测口径
This commit is contained in:
@@ -208,8 +208,13 @@ pub struct PlatformExprStrategyConfig {
|
|||||||
pub retry_empty_rebalance: bool,
|
pub retry_empty_rebalance: bool,
|
||||||
pub calendar_rebalance_interval: bool,
|
pub calendar_rebalance_interval: bool,
|
||||||
pub aiquant_transaction_cost: bool,
|
pub aiquant_transaction_cost: bool,
|
||||||
|
pub commission_rate: Option<f64>,
|
||||||
|
pub minimum_commission: Option<f64>,
|
||||||
|
pub stamp_tax_rate_before_change: Option<f64>,
|
||||||
|
pub stamp_tax_rate_after_change: Option<f64>,
|
||||||
pub strict_value_budget: bool,
|
pub strict_value_budget: bool,
|
||||||
pub quote_quantity_limit: bool,
|
pub quote_quantity_limit: bool,
|
||||||
|
pub current_day_precomputed_factors: bool,
|
||||||
pub intraday_execution_time: Option<NaiveTime>,
|
pub intraday_execution_time: Option<NaiveTime>,
|
||||||
pub explicit_action_stage: PlatformExplicitActionStage,
|
pub explicit_action_stage: PlatformExplicitActionStage,
|
||||||
pub explicit_action_schedule: Option<PlatformRebalanceSchedule>,
|
pub explicit_action_schedule: Option<PlatformRebalanceSchedule>,
|
||||||
@@ -266,8 +271,13 @@ fn band_low(index_close) {
|
|||||||
retry_empty_rebalance: false,
|
retry_empty_rebalance: false,
|
||||||
calendar_rebalance_interval: false,
|
calendar_rebalance_interval: false,
|
||||||
aiquant_transaction_cost: false,
|
aiquant_transaction_cost: false,
|
||||||
|
commission_rate: None,
|
||||||
|
minimum_commission: None,
|
||||||
|
stamp_tax_rate_before_change: None,
|
||||||
|
stamp_tax_rate_after_change: None,
|
||||||
strict_value_budget: false,
|
strict_value_budget: false,
|
||||||
quote_quantity_limit: true,
|
quote_quantity_limit: true,
|
||||||
|
current_day_precomputed_factors: false,
|
||||||
intraday_execution_time: None,
|
intraday_execution_time: None,
|
||||||
explicit_action_stage: PlatformExplicitActionStage::OnDay,
|
explicit_action_stage: PlatformExplicitActionStage::OnDay,
|
||||||
explicit_action_schedule: None,
|
explicit_action_schedule: None,
|
||||||
@@ -879,11 +889,24 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn cost_model(&self) -> ChinaAShareCostModel {
|
fn cost_model(&self) -> ChinaAShareCostModel {
|
||||||
if self.config.aiquant_transaction_cost {
|
let mut model = if self.config.aiquant_transaction_cost {
|
||||||
ChinaAShareCostModel::aiquant_rqalpha_default()
|
ChinaAShareCostModel::aiquant_rqalpha_default()
|
||||||
} else {
|
} else {
|
||||||
ChinaAShareCostModel::default()
|
ChinaAShareCostModel::default()
|
||||||
|
};
|
||||||
|
if let Some(value) = self.config.commission_rate {
|
||||||
|
model.commission_rate = value;
|
||||||
}
|
}
|
||||||
|
if let Some(value) = self.config.minimum_commission {
|
||||||
|
model.minimum_commission = value;
|
||||||
|
}
|
||||||
|
if let Some(value) = self.config.stamp_tax_rate_before_change {
|
||||||
|
model.stamp_tax_rate_before_change = value;
|
||||||
|
}
|
||||||
|
if let Some(value) = self.config.stamp_tax_rate_after_change {
|
||||||
|
model.stamp_tax_rate_after_change = value;
|
||||||
|
}
|
||||||
|
model
|
||||||
}
|
}
|
||||||
|
|
||||||
fn marked_total_value(&self, ctx: &StrategyContext<'_>, date: NaiveDate) -> f64 {
|
fn marked_total_value(&self, ctx: &StrategyContext<'_>, date: NaiveDate) -> f64 {
|
||||||
@@ -4007,14 +4030,16 @@ impl PlatformExprStrategy {
|
|||||||
Ok(value.round().max(1.0) as usize)
|
Ok(value.round().max(1.0) as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selection_dates(&self, ctx: &StrategyContext<'_>) -> (NaiveDate, NaiveDate) {
|
fn selection_dates(&self, ctx: &StrategyContext<'_>) -> (NaiveDate, NaiveDate, NaiveDate) {
|
||||||
let decision_date = ctx.decision_date;
|
let decision_date = ctx.decision_date;
|
||||||
let factor_date = if self.config.aiquant_transaction_cost {
|
let previous_factor_date = ctx
|
||||||
|
.data
|
||||||
|
.previous_trading_date(decision_date, 1)
|
||||||
|
.unwrap_or(decision_date);
|
||||||
|
let stock_factor_date = if self.config.current_day_precomputed_factors {
|
||||||
decision_date
|
decision_date
|
||||||
} else {
|
} else {
|
||||||
ctx.data
|
previous_factor_date
|
||||||
.previous_trading_date(decision_date, 1)
|
|
||||||
.unwrap_or(decision_date)
|
|
||||||
};
|
};
|
||||||
let selection_date = if self.config.aiquant_transaction_cost
|
let selection_date = if self.config.aiquant_transaction_cost
|
||||||
&& self.config.intraday_execution_time.is_some()
|
&& self.config.intraday_execution_time.is_some()
|
||||||
@@ -4023,7 +4048,7 @@ impl PlatformExprStrategy {
|
|||||||
} else {
|
} else {
|
||||||
decision_date
|
decision_date
|
||||||
};
|
};
|
||||||
(selection_date, factor_date)
|
(selection_date, previous_factor_date, stock_factor_date)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buy_scale(
|
fn buy_scale(
|
||||||
@@ -5138,6 +5163,13 @@ impl PlatformExprStrategy {
|
|||||||
candidate: &EligibleUniverseSnapshot,
|
candidate: &EligibleUniverseSnapshot,
|
||||||
stock: &StockExpressionState,
|
stock: &StockExpressionState,
|
||||||
) -> f64 {
|
) -> f64 {
|
||||||
|
match self.config.market_cap_field.as_str() {
|
||||||
|
"market_cap" | "market_cap_bn" => return candidate.market_cap_bn,
|
||||||
|
"free_float_cap" | "free_float_market_cap" | "free_float_cap_bn" => {
|
||||||
|
return candidate.free_float_cap_bn;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
self.stock_numeric_field_value(candidate, stock, self.config.market_cap_field.as_str())
|
self.stock_numeric_field_value(candidate, stock, self.config.market_cap_field.as_str())
|
||||||
.unwrap_or_else(|| self.field_value(candidate))
|
.unwrap_or_else(|| self.field_value(candidate))
|
||||||
}
|
}
|
||||||
@@ -5236,18 +5268,19 @@ impl PlatformExprStrategy {
|
|||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
factor_date: NaiveDate,
|
universe_factor_date: NaiveDate,
|
||||||
|
stock_factor_date: NaiveDate,
|
||||||
day: &DayExpressionState,
|
day: &DayExpressionState,
|
||||||
band_low: f64,
|
band_low: f64,
|
||||||
band_high: f64,
|
band_high: f64,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
) -> Result<(Vec<String>, Vec<String>), BacktestError> {
|
) -> Result<(Vec<String>, Vec<String>), BacktestError> {
|
||||||
let universe = self.selectable_universe_on(ctx, date, factor_date);
|
let universe = self.selectable_universe_on(ctx, date, universe_factor_date);
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
let mut candidates = Vec::new();
|
let mut candidates = Vec::new();
|
||||||
for candidate in universe {
|
for candidate in universe {
|
||||||
let stock =
|
let stock =
|
||||||
self.stock_state_with_factor_date(ctx, date, factor_date, &candidate.symbol)?;
|
self.stock_state_with_factor_date(ctx, date, stock_factor_date, &candidate.symbol)?;
|
||||||
let field_value = self.selection_field_value(&candidate, &stock);
|
let field_value = self.selection_field_value(&candidate, &stock);
|
||||||
if !field_value.is_finite() {
|
if !field_value.is_finite() {
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
@@ -5341,19 +5374,20 @@ impl PlatformExprStrategy {
|
|||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
factor_date: NaiveDate,
|
universe_factor_date: NaiveDate,
|
||||||
|
stock_factor_date: NaiveDate,
|
||||||
day: &DayExpressionState,
|
day: &DayExpressionState,
|
||||||
band_low: f64,
|
band_low: f64,
|
||||||
band_high: f64,
|
band_high: f64,
|
||||||
selection_limit: usize,
|
selection_limit: usize,
|
||||||
) -> Result<(Vec<String>, Vec<String>, Vec<String>), BacktestError> {
|
) -> Result<(Vec<String>, Vec<String>, Vec<String>), BacktestError> {
|
||||||
let universe = self.selectable_universe_on(ctx, date, factor_date);
|
let universe = self.selectable_universe_on(ctx, date, universe_factor_date);
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
let mut candidates = Vec::new();
|
let mut candidates = Vec::new();
|
||||||
let apply_stock_filter = !self.stock_filter_uses_intraday_quote_fields();
|
let apply_stock_filter = !self.stock_filter_uses_intraday_quote_fields();
|
||||||
for candidate in universe {
|
for candidate in universe {
|
||||||
let stock =
|
let stock =
|
||||||
self.stock_state_with_factor_date(ctx, date, factor_date, &candidate.symbol)?;
|
self.stock_state_with_factor_date(ctx, date, stock_factor_date, &candidate.symbol)?;
|
||||||
let field_value = self.selection_field_value(&candidate, &stock);
|
let field_value = self.selection_field_value(&candidate, &stock);
|
||||||
if !field_value.is_finite() {
|
if !field_value.is_finite() {
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
@@ -5406,7 +5440,8 @@ impl PlatformExprStrategy {
|
|||||||
processed_symbols.push(symbol.clone());
|
processed_symbols.push(symbol.clone());
|
||||||
}
|
}
|
||||||
for (symbol, _) in &candidates[cursor..end] {
|
for (symbol, _) in &candidates[cursor..end] {
|
||||||
let stock = self.stock_state_with_factor_date(ctx, date, factor_date, symbol)?;
|
let stock =
|
||||||
|
self.stock_state_with_factor_date(ctx, date, stock_factor_date, symbol)?;
|
||||||
if let Some(reason) = self.buy_rejection_reason(ctx, date, symbol, &stock)? {
|
if let Some(reason) = self.buy_rejection_reason(ctx, date, symbol, &stock)? {
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
diagnostics.push(format!("{symbol} quote_plan rejected by {reason}"));
|
diagnostics.push(format!("{symbol} quote_plan rejected by {reason}"));
|
||||||
@@ -5453,7 +5488,7 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let day = self.day_state(ctx, ctx.decision_date)?;
|
let day = self.day_state(ctx, ctx.decision_date)?;
|
||||||
let (selection_date, factor_date) = self.selection_dates(ctx);
|
let (selection_date, universe_factor_date, stock_factor_date) = self.selection_dates(ctx);
|
||||||
let requires_intraday_selection_quotes = self.stock_filter_uses_intraday_quote_fields();
|
let requires_intraday_selection_quotes = self.stock_filter_uses_intraday_quote_fields();
|
||||||
let (band_low, band_high) = self.market_cap_band(ctx, &day)?;
|
let (band_low, band_high) = self.market_cap_band(ctx, &day)?;
|
||||||
let selection_limit = self
|
let selection_limit = self
|
||||||
@@ -5462,7 +5497,8 @@ impl PlatformExprStrategy {
|
|||||||
let (candidate_symbols, order_symbols, diagnostics) = self.select_quote_plan_symbols(
|
let (candidate_symbols, order_symbols, diagnostics) = self.select_quote_plan_symbols(
|
||||||
ctx,
|
ctx,
|
||||||
selection_date,
|
selection_date,
|
||||||
factor_date,
|
universe_factor_date,
|
||||||
|
stock_factor_date,
|
||||||
&day,
|
&day,
|
||||||
band_low,
|
band_low,
|
||||||
band_high,
|
band_high,
|
||||||
@@ -5472,7 +5508,7 @@ impl PlatformExprStrategy {
|
|||||||
execution_date: ctx.execution_date,
|
execution_date: ctx.execution_date,
|
||||||
decision_date: ctx.decision_date,
|
decision_date: ctx.decision_date,
|
||||||
selection_date,
|
selection_date,
|
||||||
factor_date,
|
factor_date: stock_factor_date,
|
||||||
requires_intraday_selection_quotes,
|
requires_intraday_selection_quotes,
|
||||||
band_low,
|
band_low,
|
||||||
band_high,
|
band_high,
|
||||||
@@ -5658,7 +5694,8 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let day = self.day_state(ctx, decision_date)?;
|
let day = self.day_state(ctx, decision_date)?;
|
||||||
let (selection_market_date, selection_factor_date) = self.selection_dates(ctx);
|
let (selection_market_date, selection_universe_factor_date, selection_factor_date) =
|
||||||
|
self.selection_dates(ctx);
|
||||||
let (explicit_action_intents, explicit_action_diagnostics) =
|
let (explicit_action_intents, explicit_action_diagnostics) =
|
||||||
if self.config.explicit_action_stage == PlatformExplicitActionStage::OnDay
|
if self.config.explicit_action_stage == PlatformExplicitActionStage::OnDay
|
||||||
&& self.explicit_actions_active(ctx.data.calendar(), execution_date)
|
&& self.explicit_actions_active(ctx.data.calendar(), execution_date)
|
||||||
@@ -5696,6 +5733,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let (stock_list, notes) = self.select_symbols(
|
let (stock_list, notes) = self.select_symbols(
|
||||||
ctx,
|
ctx,
|
||||||
selection_market_date,
|
selection_market_date,
|
||||||
|
selection_universe_factor_date,
|
||||||
selection_factor_date,
|
selection_factor_date,
|
||||||
&day,
|
&day,
|
||||||
band_low,
|
band_low,
|
||||||
@@ -6207,7 +6245,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
format!(
|
format!(
|
||||||
"selected={} periodic_rebalance={} exits={} projected_positions={} intents={} limit={} decision_date={} selection_market_date={} selection_factor_date={} execution_date={} budget_total={:.2} marked_total={:.2} day_total={:.2}",
|
"selected={} periodic_rebalance={} exits={} projected_positions={} intents={} limit={} decision_date={} selection_market_date={} selection_universe_factor_date={} selection_factor_date={} execution_date={} budget_total={:.2} marked_total={:.2} day_total={:.2}",
|
||||||
stock_list.len(),
|
stock_list.len(),
|
||||||
periodic_rebalance,
|
periodic_rebalance,
|
||||||
exit_symbols.len(),
|
exit_symbols.len(),
|
||||||
@@ -6216,6 +6254,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
selection_limit,
|
selection_limit,
|
||||||
decision_date,
|
decision_date,
|
||||||
selection_market_date,
|
selection_market_date,
|
||||||
|
selection_universe_factor_date,
|
||||||
selection_factor_date,
|
selection_factor_date,
|
||||||
execution_date,
|
execution_date,
|
||||||
aiquant_total_value,
|
aiquant_total_value,
|
||||||
@@ -6331,6 +6370,18 @@ mod tests {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn platform_expr_cost_model_uses_commission_override() {
|
||||||
|
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||||
|
cfg.aiquant_transaction_cost = true;
|
||||||
|
cfg.commission_rate = Some(0.0003);
|
||||||
|
cfg.minimum_commission = Some(5.0);
|
||||||
|
let strategy = PlatformExprStrategy::new(cfg);
|
||||||
|
|
||||||
|
assert!((strategy.buy_commission(100_000.0) - 30.0).abs() < 1e-9);
|
||||||
|
assert!((strategy.buy_commission(1_000.0) - 5.0).abs() < 1e-9);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn platform_expr_rewrites_prelude_assignment_ternary() {
|
fn platform_expr_rewrites_prelude_assignment_ternary() {
|
||||||
let prelude = "let cap_low = sig_close <= 0 ? 7 : max(5, min(cap_low_raw, 70));";
|
let prelude = "let cap_low = sig_close <= 0 ? 7 : max(5, min(cap_low_raw, 70));";
|
||||||
@@ -7404,7 +7455,7 @@ mod tests {
|
|||||||
|
|
||||||
let day = strategy.day_state(&ctx, date).expect("day state");
|
let day = strategy.day_state(&ctx, date).expect("day state");
|
||||||
let (selected, _) = strategy
|
let (selected, _) = strategy
|
||||||
.select_symbols(&ctx, date, date, &day, 10.0, 20.0, 1)
|
.select_symbols(&ctx, date, date, date, &day, 10.0, 20.0, 1)
|
||||||
.expect("selection");
|
.expect("selection");
|
||||||
assert!(selected.is_empty());
|
assert!(selected.is_empty());
|
||||||
}
|
}
|
||||||
@@ -7908,6 +7959,14 @@ mod tests {
|
|||||||
"{:?}",
|
"{:?}",
|
||||||
decision.diagnostics
|
decision.diagnostics
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
decision
|
||||||
|
.diagnostics
|
||||||
|
.iter()
|
||||||
|
.any(|item| item.contains("selection_factor_date=2023-11-10")),
|
||||||
|
"{:?}",
|
||||||
|
decision.diagnostics
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
decision
|
decision
|
||||||
.diagnostics
|
.diagnostics
|
||||||
|
|||||||
@@ -67,6 +67,14 @@ pub struct StrategyExecutionSpec {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub slippage_max_value: Option<f64>,
|
pub slippage_max_value: Option<f64>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub commission_rate: Option<f64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub minimum_commission: Option<f64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub stamp_tax_rate_before_change: Option<f64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub stamp_tax_rate_after_change: Option<f64>,
|
||||||
|
#[serde(default)]
|
||||||
pub strict_value_budget: Option<bool>,
|
pub strict_value_budget: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +116,14 @@ pub struct StrategyEngineConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub slippage_max_value: Option<f64>,
|
pub slippage_max_value: Option<f64>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub commission_rate: Option<f64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub minimum_commission: Option<f64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub stamp_tax_rate_before_change: Option<f64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub stamp_tax_rate_after_change: Option<f64>,
|
||||||
|
#[serde(default)]
|
||||||
pub strict_value_budget: Option<bool>,
|
pub strict_value_budget: Option<bool>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub dividend_reinvestment: Option<bool>,
|
pub dividend_reinvestment: Option<bool>,
|
||||||
@@ -343,6 +359,31 @@ pub fn platform_expr_config_from_value(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn valid_non_negative(value: Option<f64>) -> Option<f64> {
|
||||||
|
value.filter(|item| item.is_finite() && *item >= 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_cost_overrides(
|
||||||
|
cfg: &mut PlatformExprStrategyConfig,
|
||||||
|
commission_rate: Option<f64>,
|
||||||
|
minimum_commission: Option<f64>,
|
||||||
|
stamp_tax_rate_before_change: Option<f64>,
|
||||||
|
stamp_tax_rate_after_change: Option<f64>,
|
||||||
|
) {
|
||||||
|
if let Some(value) = valid_non_negative(commission_rate) {
|
||||||
|
cfg.commission_rate = Some(value);
|
||||||
|
}
|
||||||
|
if let Some(value) = valid_non_negative(minimum_commission) {
|
||||||
|
cfg.minimum_commission = Some(value);
|
||||||
|
}
|
||||||
|
if let Some(value) = valid_non_negative(stamp_tax_rate_before_change) {
|
||||||
|
cfg.stamp_tax_rate_before_change = Some(value);
|
||||||
|
}
|
||||||
|
if let Some(value) = valid_non_negative(stamp_tax_rate_after_change) {
|
||||||
|
cfg.stamp_tax_rate_after_change = Some(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn platform_expr_config_from_spec(
|
pub fn platform_expr_config_from_spec(
|
||||||
strategy_id: &str,
|
strategy_id: &str,
|
||||||
signal_symbol: &str,
|
signal_symbol: &str,
|
||||||
@@ -440,6 +481,13 @@ pub fn platform_expr_config_from_spec(
|
|||||||
{
|
{
|
||||||
cfg.benchmark_symbol = spec_benchmark_symbol.clone();
|
cfg.benchmark_symbol = spec_benchmark_symbol.clone();
|
||||||
}
|
}
|
||||||
|
apply_cost_overrides(
|
||||||
|
&mut cfg,
|
||||||
|
engine.commission_rate,
|
||||||
|
engine.minimum_commission,
|
||||||
|
engine.stamp_tax_rate_before_change,
|
||||||
|
engine.stamp_tax_rate_after_change,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(spec_signal_symbol) = spec
|
if let Some(spec_signal_symbol) = spec
|
||||||
@@ -741,6 +789,15 @@ pub fn platform_expr_config_from_spec(
|
|||||||
{
|
{
|
||||||
cfg.aiquant_transaction_cost = true;
|
cfg.aiquant_transaction_cost = true;
|
||||||
}
|
}
|
||||||
|
if let Some(execution) = spec.execution.as_ref() {
|
||||||
|
apply_cost_overrides(
|
||||||
|
&mut cfg,
|
||||||
|
execution.commission_rate,
|
||||||
|
execution.minimum_commission,
|
||||||
|
execution.stamp_tax_rate_before_change,
|
||||||
|
execution.stamp_tax_rate_after_change,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
cfg
|
cfg
|
||||||
}
|
}
|
||||||
@@ -1169,6 +1226,30 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_execution_cost_overrides_into_platform_config() {
|
||||||
|
let spec = serde_json::json!({
|
||||||
|
"execution": {
|
||||||
|
"compatibilityProfile": "aiquant_rqalpha",
|
||||||
|
"commissionRate": 0.0003,
|
||||||
|
"minimumCommission": 5.0,
|
||||||
|
"stampTaxRateBeforeChange": 0.0005,
|
||||||
|
"stampTaxRateAfterChange": 0.0005
|
||||||
|
},
|
||||||
|
"engineConfig": {
|
||||||
|
"commissionRate": 0.0008
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let cfg = platform_expr_config_from_value("", "", &spec).expect("config");
|
||||||
|
|
||||||
|
assert!(cfg.aiquant_transaction_cost);
|
||||||
|
assert_eq!(cfg.commission_rate, Some(0.0003));
|
||||||
|
assert_eq!(cfg.minimum_commission, Some(5.0));
|
||||||
|
assert_eq!(cfg.stamp_tax_rate_before_change, Some(0.0005));
|
||||||
|
assert_eq!(cfg.stamp_tax_rate_after_change, Some(0.0005));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_daily_schedule_time_for_aiquant_execution_quotes() {
|
fn parses_daily_schedule_time_for_aiquant_execution_quotes() {
|
||||||
let spec = serde_json::json!({
|
let spec = serde_json::json!({
|
||||||
|
|||||||
Reference in New Issue
Block a user