修复策略投影成交量约束
This commit is contained in:
@@ -1519,6 +1519,89 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn projected_market_fillable_quantity(
|
||||||
|
&self,
|
||||||
|
market: &DailyMarketSnapshot,
|
||||||
|
quote: Option<&crate::data::IntradayExecutionQuote>,
|
||||||
|
symbol: &str,
|
||||||
|
side: OrderSide,
|
||||||
|
requested_qty: u32,
|
||||||
|
round_lot: u32,
|
||||||
|
minimum_order_quantity: u32,
|
||||||
|
order_step_size: u32,
|
||||||
|
allow_odd_lot_sell: bool,
|
||||||
|
current_fill_quantity: u32,
|
||||||
|
execution_state: &ProjectedExecutionState,
|
||||||
|
) -> Option<u32> {
|
||||||
|
if requested_qty == 0 {
|
||||||
|
return Some(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let constraints = self.config.risk_config.trading_constraints;
|
||||||
|
let mut max_fill = requested_qty;
|
||||||
|
|
||||||
|
if self.config.quote_quantity_limit || constraints.liquidity_limit_enabled {
|
||||||
|
let top_level_liquidity = match (quote, side) {
|
||||||
|
(Some(quote), OrderSide::Buy) => quote
|
||||||
|
.ask1_volume
|
||||||
|
.saturating_mul(round_lot.max(1) as u64)
|
||||||
|
.min(u32::MAX as u64) as u32,
|
||||||
|
(Some(quote), OrderSide::Sell) => quote
|
||||||
|
.bid1_volume
|
||||||
|
.saturating_mul(round_lot.max(1) as u64)
|
||||||
|
.min(u32::MAX as u64) as u32,
|
||||||
|
(None, OrderSide::Buy) => market.liquidity_for_buy().min(u32::MAX as u64) as u32,
|
||||||
|
(None, OrderSide::Sell) => market.liquidity_for_sell().min(u32::MAX as u64) as u32,
|
||||||
|
};
|
||||||
|
if top_level_liquidity == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let liquidity_limited = if side == OrderSide::Sell && allow_odd_lot_sell {
|
||||||
|
top_level_liquidity
|
||||||
|
} else {
|
||||||
|
self.round_lot_quantity(
|
||||||
|
top_level_liquidity,
|
||||||
|
minimum_order_quantity,
|
||||||
|
order_step_size,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if liquidity_limited == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
max_fill = max_fill.min(liquidity_limited);
|
||||||
|
}
|
||||||
|
|
||||||
|
if constraints.volume_limit_enabled {
|
||||||
|
if market.minute_volume == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let consumed_turnover = execution_state
|
||||||
|
.intraday_turnover
|
||||||
|
.get(symbol)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(0)
|
||||||
|
.saturating_add(current_fill_quantity);
|
||||||
|
let raw_limit = ((market.minute_volume as f64) * constraints.volume_percent).floor()
|
||||||
|
as i64
|
||||||
|
- consumed_turnover as i64;
|
||||||
|
if raw_limit <= 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let volume_limited = if side == OrderSide::Sell && allow_odd_lot_sell {
|
||||||
|
raw_limit as u32
|
||||||
|
} else {
|
||||||
|
self.round_lot_quantity(raw_limit as u32, minimum_order_quantity, order_step_size)
|
||||||
|
};
|
||||||
|
if volume_limited == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
max_fill = max_fill.min(volume_limited);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(max_fill)
|
||||||
|
}
|
||||||
|
|
||||||
fn projected_select_execution_fill(
|
fn projected_select_execution_fill(
|
||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
@@ -1595,24 +1678,28 @@ impl PlatformExprStrategy {
|
|||||||
let Some(raw_quote_price) = self.projected_quote_raw_price(quote, side) else {
|
let Some(raw_quote_price) = self.projected_quote_raw_price(quote, side) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let available_qty = if self.config.quote_quantity_limit {
|
|
||||||
match side {
|
|
||||||
OrderSide::Buy => quote.ask1_volume,
|
|
||||||
OrderSide::Sell => quote.bid1_volume,
|
|
||||||
}
|
|
||||||
.saturating_mul(round_lot.max(1) as u64)
|
|
||||||
.min(u32::MAX as u64) as u32
|
|
||||||
} else {
|
|
||||||
requested_qty.saturating_sub(filled_qty)
|
|
||||||
};
|
|
||||||
if available_qty == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let remaining_qty = requested_qty.saturating_sub(filled_qty);
|
let remaining_qty = requested_qty.saturating_sub(filled_qty);
|
||||||
if remaining_qty == 0 {
|
if remaining_qty == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
let available_qty = self
|
||||||
|
.projected_market_fillable_quantity(
|
||||||
|
market,
|
||||||
|
Some(quote),
|
||||||
|
symbol,
|
||||||
|
side,
|
||||||
|
remaining_qty,
|
||||||
|
round_lot,
|
||||||
|
minimum_order_quantity,
|
||||||
|
order_step_size,
|
||||||
|
allow_odd_lot_sell,
|
||||||
|
filled_qty,
|
||||||
|
execution_state,
|
||||||
|
)
|
||||||
|
.unwrap_or(0);
|
||||||
|
if available_qty == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
let mut take_qty = remaining_qty.min(available_qty);
|
let mut take_qty = remaining_qty.min(available_qty);
|
||||||
if !(side == OrderSide::Sell && allow_odd_lot_sell && take_qty == remaining_qty) {
|
if !(side == OrderSide::Sell && allow_odd_lot_sell && take_qty == remaining_qty) {
|
||||||
take_qty =
|
take_qty =
|
||||||
@@ -1780,9 +1867,25 @@ impl PlatformExprStrategy {
|
|||||||
execution_time,
|
execution_time,
|
||||||
) && ctx.data.execution_quotes_on(date, symbol).is_empty()
|
) && ctx.data.execution_quotes_on(date, symbol).is_empty()
|
||||||
{
|
{
|
||||||
|
let fallback_quantity = self.projected_market_fillable_quantity(
|
||||||
|
market,
|
||||||
|
None,
|
||||||
|
symbol,
|
||||||
|
OrderSide::Sell,
|
||||||
|
quantity,
|
||||||
|
round_lot,
|
||||||
|
minimum_order_quantity,
|
||||||
|
order_step_size,
|
||||||
|
sellable_qty >= current_qty,
|
||||||
|
0,
|
||||||
|
execution_state,
|
||||||
|
)?;
|
||||||
|
if fallback_quantity == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
Some(ProjectedExecutionFill {
|
Some(ProjectedExecutionFill {
|
||||||
price: self.projected_execution_price(market, OrderSide::Sell),
|
price: self.projected_execution_price(market, OrderSide::Sell),
|
||||||
quantity,
|
quantity: fallback_quantity,
|
||||||
next_cursor: date.and_time(
|
next_cursor: date.and_time(
|
||||||
execution_time.unwrap_or_else(|| self.intraday_execution_start_time()),
|
execution_time.unwrap_or_else(|| self.intraday_execution_start_time()),
|
||||||
) + Duration::seconds(1),
|
) + Duration::seconds(1),
|
||||||
@@ -2144,9 +2247,25 @@ impl PlatformExprStrategy {
|
|||||||
None,
|
None,
|
||||||
) && ctx.data.execution_quotes_on(date, symbol).is_empty()
|
) && ctx.data.execution_quotes_on(date, symbol).is_empty()
|
||||||
{
|
{
|
||||||
|
let fallback_quantity = self.projected_market_fillable_quantity(
|
||||||
|
ctx.data.market(date, symbol)?,
|
||||||
|
None,
|
||||||
|
symbol,
|
||||||
|
OrderSide::Buy,
|
||||||
|
quantity,
|
||||||
|
round_lot,
|
||||||
|
minimum_order_quantity,
|
||||||
|
order_step_size,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
execution_state,
|
||||||
|
)?;
|
||||||
|
if fallback_quantity == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
Some(ProjectedExecutionFill {
|
Some(ProjectedExecutionFill {
|
||||||
price: sizing_price,
|
price: sizing_price,
|
||||||
quantity,
|
quantity: fallback_quantity,
|
||||||
next_cursor: date.and_time(self.intraday_execution_start_time())
|
next_cursor: date.and_time(self.intraday_execution_start_time())
|
||||||
+ Duration::seconds(1),
|
+ Duration::seconds(1),
|
||||||
})
|
})
|
||||||
@@ -9880,6 +9999,8 @@ mod tests {
|
|||||||
cfg.slippage_model = SlippageModel::PriceRatio(0.002);
|
cfg.slippage_model = SlippageModel::PriceRatio(0.002);
|
||||||
cfg.matching_type = MatchingType::MinuteLast;
|
cfg.matching_type = MatchingType::MinuteLast;
|
||||||
cfg.quote_quantity_limit = false;
|
cfg.quote_quantity_limit = false;
|
||||||
|
cfg.risk_config.trading_constraints.volume_limit_enabled = false;
|
||||||
|
cfg.risk_config.trading_constraints.liquidity_limit_enabled = false;
|
||||||
cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(10, 40, 0).expect("time"));
|
cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(10, 40, 0).expect("time"));
|
||||||
let strategy = PlatformExprStrategy::new(cfg);
|
let strategy = PlatformExprStrategy::new(cfg);
|
||||||
let quote = ctx
|
let quote = ctx
|
||||||
@@ -9910,6 +10031,92 @@ mod tests {
|
|||||||
assert!((position.average_cost - 5.12022).abs() < 1e-9);
|
assert!((position.average_cost - 5.12022).abs() < 1e-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn platform_projected_market_fill_caps_volume_limit() {
|
||||||
|
let date = d(2023, 5, 12);
|
||||||
|
let symbol = "603176.SH";
|
||||||
|
let market = DailyMarketSnapshot {
|
||||||
|
date,
|
||||||
|
symbol: symbol.to_string(),
|
||||||
|
timestamp: None,
|
||||||
|
day_open: 5.10,
|
||||||
|
open: 5.10,
|
||||||
|
high: 5.20,
|
||||||
|
low: 5.00,
|
||||||
|
close: 5.20,
|
||||||
|
last_price: 5.11,
|
||||||
|
bid1: 5.11,
|
||||||
|
ask1: 5.12,
|
||||||
|
prev_close: 5.00,
|
||||||
|
volume: 1_000_000,
|
||||||
|
minute_volume: 10_000,
|
||||||
|
bid1_volume: 10_000,
|
||||||
|
ask1_volume: 10_000,
|
||||||
|
trading_phase: Some("continuous".to_string()),
|
||||||
|
paused: false,
|
||||||
|
upper_limit: 5.50,
|
||||||
|
lower_limit: 4.50,
|
||||||
|
price_tick: 0.01,
|
||||||
|
};
|
||||||
|
let quote = IntradayExecutionQuote {
|
||||||
|
date,
|
||||||
|
symbol: symbol.to_string(),
|
||||||
|
timestamp: date.and_hms_opt(10, 40, 0).expect("timestamp"),
|
||||||
|
last_price: 5.11,
|
||||||
|
bid1: 5.11,
|
||||||
|
ask1: 5.12,
|
||||||
|
bid1_volume: 10_000,
|
||||||
|
ask1_volume: 10_000,
|
||||||
|
volume_delta: 10_000,
|
||||||
|
amount_delta: 51_100.0,
|
||||||
|
trading_phase: Some("continuous".to_string()),
|
||||||
|
};
|
||||||
|
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||||
|
cfg.quote_quantity_limit = false;
|
||||||
|
cfg.risk_config.trading_constraints.volume_limit_enabled = true;
|
||||||
|
cfg.risk_config.trading_constraints.liquidity_limit_enabled = false;
|
||||||
|
cfg.risk_config.trading_constraints.volume_percent = 0.25;
|
||||||
|
let strategy = PlatformExprStrategy::new(cfg);
|
||||||
|
let mut execution_state = super::ProjectedExecutionState::default();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
strategy.projected_market_fillable_quantity(
|
||||||
|
&market,
|
||||||
|
Some("e),
|
||||||
|
symbol,
|
||||||
|
OrderSide::Buy,
|
||||||
|
50_000,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
&execution_state,
|
||||||
|
),
|
||||||
|
Some(2_500)
|
||||||
|
);
|
||||||
|
|
||||||
|
execution_state
|
||||||
|
.intraday_turnover
|
||||||
|
.insert(symbol.to_string(), 2_400);
|
||||||
|
assert_eq!(
|
||||||
|
strategy.projected_market_fillable_quantity(
|
||||||
|
&market,
|
||||||
|
Some("e),
|
||||||
|
symbol,
|
||||||
|
OrderSide::Buy,
|
||||||
|
50_000,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
&execution_state,
|
||||||
|
),
|
||||||
|
Some(100)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn platform_aiquant_target_value_uses_scheduled_mark_for_delta() {
|
fn platform_aiquant_target_value_uses_scheduled_mark_for_delta() {
|
||||||
let prev_date = d(2023, 5, 11);
|
let prev_date = d(2023, 5, 11);
|
||||||
@@ -10482,6 +10689,8 @@ mod tests {
|
|||||||
cfg.signal_symbol = symbol.to_string();
|
cfg.signal_symbol = symbol.to_string();
|
||||||
cfg.stop_loss_expr.clear();
|
cfg.stop_loss_expr.clear();
|
||||||
cfg.take_profit_expr = "1.07".to_string();
|
cfg.take_profit_expr = "1.07".to_string();
|
||||||
|
cfg.risk_config.trading_constraints.volume_limit_enabled = false;
|
||||||
|
cfg.risk_config.trading_constraints.liquidity_limit_enabled = false;
|
||||||
let mut strategy = PlatformExprStrategy::new(cfg);
|
let mut strategy = PlatformExprStrategy::new(cfg);
|
||||||
|
|
||||||
let decision = strategy.on_day(&ctx).expect("platform decision");
|
let decision = strategy.on_day(&ctx).expect("platform decision");
|
||||||
@@ -10691,6 +10900,8 @@ mod tests {
|
|||||||
cfg.signal_symbol = symbol.to_string();
|
cfg.signal_symbol = symbol.to_string();
|
||||||
cfg.stop_loss_expr.clear();
|
cfg.stop_loss_expr.clear();
|
||||||
cfg.take_profit_expr = "1.07".to_string();
|
cfg.take_profit_expr = "1.07".to_string();
|
||||||
|
cfg.risk_config.trading_constraints.volume_limit_enabled = false;
|
||||||
|
cfg.risk_config.trading_constraints.liquidity_limit_enabled = false;
|
||||||
let mut strategy = PlatformExprStrategy::new(cfg);
|
let mut strategy = PlatformExprStrategy::new(cfg);
|
||||||
|
|
||||||
let first_decision = strategy.on_day(&first_ctx).expect("first decision");
|
let first_decision = strategy.on_day(&first_ctx).expect("first decision");
|
||||||
@@ -10983,6 +11194,8 @@ mod tests {
|
|||||||
cfg.delayed_limit_open_exit_enabled = true;
|
cfg.delayed_limit_open_exit_enabled = true;
|
||||||
cfg.delayed_limit_open_exit_time = Some(NaiveTime::from_hms_opt(9, 31, 0).unwrap());
|
cfg.delayed_limit_open_exit_time = Some(NaiveTime::from_hms_opt(9, 31, 0).unwrap());
|
||||||
cfg.skip_month_day_ranges = vec![(Some(2024), 1, 15, 31)];
|
cfg.skip_month_day_ranges = vec![(Some(2024), 1, 15, 31)];
|
||||||
|
cfg.risk_config.trading_constraints.volume_limit_enabled = false;
|
||||||
|
cfg.risk_config.trading_constraints.liquidity_limit_enabled = false;
|
||||||
let mut strategy = PlatformExprStrategy::new(cfg);
|
let mut strategy = PlatformExprStrategy::new(cfg);
|
||||||
strategy
|
strategy
|
||||||
.pending_highlimit_holdings
|
.pending_highlimit_holdings
|
||||||
@@ -14864,6 +15077,8 @@ mod tests {
|
|||||||
cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(10, 40, 0).unwrap());
|
cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(10, 40, 0).unwrap());
|
||||||
cfg.commission_rate = Some(0.0003);
|
cfg.commission_rate = Some(0.0003);
|
||||||
cfg.minimum_commission = Some(5.0);
|
cfg.minimum_commission = Some(5.0);
|
||||||
|
cfg.risk_config.trading_constraints.volume_limit_enabled = false;
|
||||||
|
cfg.risk_config.trading_constraints.liquidity_limit_enabled = false;
|
||||||
let strategy = PlatformExprStrategy::new(cfg);
|
let strategy = PlatformExprStrategy::new(cfg);
|
||||||
let mut projected = portfolio.clone();
|
let mut projected = portfolio.clone();
|
||||||
let mut execution_state = super::ProjectedExecutionState::default();
|
let mut execution_state = super::ProjectedExecutionState::default();
|
||||||
@@ -17217,6 +17432,9 @@ mod tests {
|
|||||||
.to_string();
|
.to_string();
|
||||||
cfg.exposure_expr = "signal_close > 0 ? 1.0 : 0.5".to_string();
|
cfg.exposure_expr = "signal_close > 0 ? 1.0 : 0.5".to_string();
|
||||||
cfg.buy_scale_expr = "touched_upper_limit ? 1.0 : 0.5".to_string();
|
cfg.buy_scale_expr = "touched_upper_limit ? 1.0 : 0.5".to_string();
|
||||||
|
cfg.quote_quantity_limit = false;
|
||||||
|
cfg.risk_config.trading_constraints.volume_limit_enabled = false;
|
||||||
|
cfg.risk_config.trading_constraints.liquidity_limit_enabled = false;
|
||||||
cfg.rank_expr = concat!(
|
cfg.rank_expr = concat!(
|
||||||
"(close / rolling_mean(\"close\", 2)) * 0.5",
|
"(close / rolling_mean(\"close\", 2)) * 0.5",
|
||||||
" + pct_change(\"close\", 1) * boost"
|
" + pct_change(\"close\", 1) * boost"
|
||||||
@@ -20291,6 +20509,8 @@ mod tests {
|
|||||||
cfg.aiquant_transaction_cost = true;
|
cfg.aiquant_transaction_cost = true;
|
||||||
cfg.strict_value_budget = true;
|
cfg.strict_value_budget = true;
|
||||||
cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(10, 18, 0).unwrap());
|
cfg.intraday_execution_time = Some(NaiveTime::from_hms_opt(10, 18, 0).unwrap());
|
||||||
|
cfg.risk_config.trading_constraints.volume_limit_enabled = false;
|
||||||
|
cfg.risk_config.trading_constraints.liquidity_limit_enabled = false;
|
||||||
let mut strategy = PlatformExprStrategy::new(cfg);
|
let mut strategy = PlatformExprStrategy::new(cfg);
|
||||||
strategy.rebalance_day_counter = 20;
|
strategy.rebalance_day_counter = 20;
|
||||||
|
|
||||||
|
|||||||
@@ -2034,7 +2034,6 @@ impl OmniMicroCapStrategy {
|
|||||||
Some(fill.quantity)
|
Some(fill.quantity)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn projected_market_fillable_quantity(
|
fn projected_market_fillable_quantity(
|
||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
@@ -2046,18 +2045,19 @@ impl OmniMicroCapStrategy {
|
|||||||
minimum_order_quantity: u32,
|
minimum_order_quantity: u32,
|
||||||
order_step_size: u32,
|
order_step_size: u32,
|
||||||
allow_odd_lot_sell: bool,
|
allow_odd_lot_sell: bool,
|
||||||
|
current_fill_quantity: u32,
|
||||||
execution_state: &ProjectedExecutionState,
|
execution_state: &ProjectedExecutionState,
|
||||||
) -> Option<u32> {
|
) -> Option<u32> {
|
||||||
if requested_qty == 0 {
|
if requested_qty == 0 {
|
||||||
return Some(0);
|
return Some(0);
|
||||||
}
|
}
|
||||||
let snapshot = ctx.data.market(date, symbol)?;
|
let snapshot = ctx.data.market(date, symbol)?;
|
||||||
if snapshot.minute_volume == 0 {
|
let constraints = self.config.risk_config.trading_constraints;
|
||||||
|
if constraints.volume_limit_enabled && snapshot.minute_volume == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut max_fill = requested_qty;
|
let mut max_fill = requested_qty;
|
||||||
let constraints = self.config.risk_config.trading_constraints;
|
|
||||||
if constraints.liquidity_limit_enabled {
|
if constraints.liquidity_limit_enabled {
|
||||||
let top_level_liquidity = match side {
|
let top_level_liquidity = match side {
|
||||||
OrderSide::Buy => snapshot.liquidity_for_buy(),
|
OrderSide::Buy => snapshot.liquidity_for_buy(),
|
||||||
@@ -2079,7 +2079,12 @@ impl OmniMicroCapStrategy {
|
|||||||
max_fill = max_fill.min(liquidity_limited);
|
max_fill = max_fill.min(liquidity_limited);
|
||||||
}
|
}
|
||||||
|
|
||||||
let consumed_turnover = *execution_state.intraday_turnover.get(symbol).unwrap_or(&0);
|
let consumed_turnover = execution_state
|
||||||
|
.intraday_turnover
|
||||||
|
.get(symbol)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(0)
|
||||||
|
.saturating_add(current_fill_quantity);
|
||||||
if constraints.volume_limit_enabled {
|
if constraints.volume_limit_enabled {
|
||||||
let raw_limit = ((snapshot.minute_volume as f64) * constraints.volume_percent).floor()
|
let raw_limit = ((snapshot.minute_volume as f64) * constraints.volume_percent).floor()
|
||||||
as i64
|
as i64
|
||||||
@@ -2129,6 +2134,23 @@ impl OmniMicroCapStrategy {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let requested_qty = self.projected_market_fillable_quantity(
|
||||||
|
ctx,
|
||||||
|
date,
|
||||||
|
symbol,
|
||||||
|
side,
|
||||||
|
requested_qty,
|
||||||
|
round_lot,
|
||||||
|
minimum_order_quantity,
|
||||||
|
order_step_size,
|
||||||
|
allow_odd_lot_sell,
|
||||||
|
0,
|
||||||
|
execution_state,
|
||||||
|
)?;
|
||||||
|
if requested_qty == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(market) = ctx.data.market(date, symbol) {
|
if let Some(market) = ctx.data.market(date, symbol) {
|
||||||
let execution_price = self.projected_execution_price(market, side);
|
let execution_price = self.projected_execution_price(market, side);
|
||||||
if execution_price.is_finite() && execution_price > 0.0 {
|
if execution_price.is_finite() && execution_price > 0.0 {
|
||||||
@@ -2222,7 +2244,25 @@ impl OmniMicroCapStrategy {
|
|||||||
if remaining_qty == 0 {
|
if remaining_qty == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let mut take_qty = remaining_qty.min(available_qty);
|
let market_fillable_qty = self
|
||||||
|
.projected_market_fillable_quantity(
|
||||||
|
ctx,
|
||||||
|
date,
|
||||||
|
symbol,
|
||||||
|
side,
|
||||||
|
remaining_qty,
|
||||||
|
round_lot,
|
||||||
|
minimum_order_quantity,
|
||||||
|
order_step_size,
|
||||||
|
allow_odd_lot_sell,
|
||||||
|
filled_qty,
|
||||||
|
execution_state,
|
||||||
|
)
|
||||||
|
.unwrap_or(0);
|
||||||
|
if market_fillable_qty == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let mut take_qty = remaining_qty.min(available_qty).min(market_fillable_qty);
|
||||||
if !(side == OrderSide::Sell && allow_odd_lot_sell && take_qty == remaining_qty) {
|
if !(side == OrderSide::Sell && allow_odd_lot_sell && take_qty == remaining_qty) {
|
||||||
take_qty =
|
take_qty =
|
||||||
self.round_lot_quantity(take_qty, minimum_order_quantity, order_step_size);
|
self.round_lot_quantity(take_qty, minimum_order_quantity, order_step_size);
|
||||||
|
|||||||
Reference in New Issue
Block a user