Add target-shares parity and rqalpha roadmap

This commit is contained in:
boris
2026-04-23 05:49:17 -07:00
parent 2857f72d84
commit f805a4b26d
6 changed files with 526 additions and 1 deletions

View File

@@ -576,6 +576,42 @@ where
commission_state,
report,
),
OrderIntent::TargetShares {
symbol,
target_quantity,
reason,
} => self.process_target_shares(
date,
portfolio,
data,
symbol,
*target_quantity,
reason,
intraday_turnover,
execution_cursors,
global_execution_cursor,
commission_state,
report,
),
OrderIntent::LimitTargetShares {
symbol,
target_quantity,
limit_price,
reason,
} => self.process_limit_target_shares(
date,
portfolio,
data,
symbol,
*target_quantity,
*limit_price,
reason,
intraday_turnover,
execution_cursors,
global_execution_cursor,
commission_state,
report,
),
OrderIntent::TargetValue {
symbol,
target_value,
@@ -2153,6 +2189,101 @@ where
Ok(())
}
fn process_target_shares(
&self,
date: NaiveDate,
portfolio: &mut PortfolioState,
data: &DataSet,
symbol: &str,
target_quantity: i32,
reason: &str,
intraday_turnover: &mut BTreeMap<String, u32>,
execution_cursors: &mut BTreeMap<String, NaiveDateTime>,
global_execution_cursor: &mut Option<NaiveDateTime>,
commission_state: &mut BTreeMap<u64, f64>,
report: &mut BrokerExecutionReport,
) -> Result<(), BacktestError> {
let current_qty = portfolio
.position(symbol)
.map(|pos| pos.quantity)
.unwrap_or(0);
let target_qty = target_quantity.max(0) as u32;
let minimum_order_quantity = self.minimum_order_quantity(data, symbol);
let order_step_size = self.order_step_size(data, symbol);
if current_qty > target_qty {
let raw_sell_qty = current_qty - target_qty;
let sell_qty = if target_qty == 0 {
current_qty
} else {
self.round_buy_quantity(raw_sell_qty, minimum_order_quantity, order_step_size)
.min(current_qty)
};
if sell_qty > 0 {
self.process_sell(
date,
portfolio,
data,
symbol,
sell_qty,
self.reserve_order_id(),
reason,
intraday_turnover,
execution_cursors,
global_execution_cursor,
commission_state,
None,
false,
true,
report,
)?;
}
} else if target_qty > current_qty {
let buy_qty = self.round_buy_quantity(
target_qty - current_qty,
minimum_order_quantity,
order_step_size,
);
if buy_qty > 0 {
self.process_buy(
date,
portfolio,
data,
symbol,
buy_qty,
self.reserve_order_id(),
reason,
intraday_turnover,
execution_cursors,
global_execution_cursor,
commission_state,
None,
None,
false,
true,
report,
)?;
}
} else {
report.order_events.push(OrderEvent {
date,
order_id: None,
symbol: symbol.to_string(),
side: if current_qty > 0 {
OrderSide::Sell
} else {
OrderSide::Buy
},
requested_quantity: 0,
filled_quantity: 0,
status: OrderStatus::Filled,
reason: format!("{reason}: already at target shares"),
});
}
Ok(())
}
fn process_limit_target_value(
&self,
date: NaiveDate,
@@ -2228,6 +2359,87 @@ where
Ok(())
}
fn process_limit_target_shares(
&self,
date: NaiveDate,
portfolio: &mut PortfolioState,
data: &DataSet,
symbol: &str,
target_quantity: i32,
limit_price: f64,
reason: &str,
intraday_turnover: &mut BTreeMap<String, u32>,
execution_cursors: &mut BTreeMap<String, NaiveDateTime>,
global_execution_cursor: &mut Option<NaiveDateTime>,
commission_state: &mut BTreeMap<u64, f64>,
report: &mut BrokerExecutionReport,
) -> Result<(), BacktestError> {
let current_qty = portfolio
.position(symbol)
.map(|pos| pos.quantity)
.unwrap_or(0);
let target_qty = target_quantity.max(0) as u32;
let minimum_order_quantity = self.minimum_order_quantity(data, symbol);
let order_step_size = self.order_step_size(data, symbol);
if current_qty > target_qty {
let raw_sell_qty = current_qty - target_qty;
let sell_qty = if target_qty == 0 {
current_qty
} else {
self.round_buy_quantity(raw_sell_qty, minimum_order_quantity, order_step_size)
.min(current_qty)
};
if sell_qty > 0 {
self.process_sell(
date,
portfolio,
data,
symbol,
sell_qty,
self.reserve_order_id(),
reason,
intraday_turnover,
execution_cursors,
global_execution_cursor,
commission_state,
Some(limit_price),
true,
true,
report,
)?;
}
} else if target_qty > current_qty {
let buy_qty = self.round_buy_quantity(
target_qty - current_qty,
minimum_order_quantity,
order_step_size,
);
if buy_qty > 0 {
self.process_buy(
date,
portfolio,
data,
symbol,
buy_qty,
self.reserve_order_id(),
reason,
intraday_turnover,
execution_cursors,
global_execution_cursor,
commission_state,
None,
Some(limit_price),
true,
true,
report,
)?;
}
}
Ok(())
}
fn process_target_percent(
&self,
date: NaiveDate,