Add target-shares parity and rqalpha roadmap
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user