接入独立手工仓位时间线并保留原策略配置
This commit is contained in:
@@ -12,7 +12,7 @@ use crate::events::OrderSide;
|
||||
use crate::{DataSet, FixedMoney, PortfolioState};
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
pub const MANUAL_REPLAY_SCHEMA: &str = "fidc.observed-manual-executions/v2";
|
||||
pub const MANUAL_REPLAY_SCHEMA: &str = "fidc.observed-manual-executions/v3";
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||
@@ -24,6 +24,10 @@ pub struct ManualExecutionReplay {
|
||||
pub content_sha256: String,
|
||||
pub observation_cutoff: DateTime<Utc>,
|
||||
pub actions: Vec<ManualExecutionAction>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub position_exposure_events: Vec<crate::position_exposure::PositionExposureEvent>,
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub legacy_position_exposure_bps: BTreeMap<NaiveDate, i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
@@ -162,6 +166,19 @@ fn identifier(value: &str) -> Result<(), String> {
|
||||
}
|
||||
|
||||
impl ManualExecutionReplay {
|
||||
/// Market/indicator data is needed for securities whose observed fills
|
||||
/// change the portfolio. A rejected, never-filled order is not data demand.
|
||||
pub fn required_data_symbols(&self) -> Result<BTreeSet<String>, String> {
|
||||
self.validate()?;
|
||||
Ok(self
|
||||
.actions
|
||||
.iter()
|
||||
.flat_map(|action| &action.orders)
|
||||
.filter(|order| !order.fills.is_empty())
|
||||
.map(|order| order.symbol.clone())
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn observations(&self) -> Result<Vec<ManualFillObservation<'_>>, String> {
|
||||
self.validate()?;
|
||||
let mut observations = Vec::new();
|
||||
@@ -190,9 +207,30 @@ impl ManualExecutionReplay {
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
if self.schema != MANUAL_REPLAY_SCHEMA {
|
||||
if self.schema != MANUAL_REPLAY_SCHEMA
|
||||
&& self.schema != "fidc.observed-manual-executions/v2"
|
||||
{
|
||||
return Err("unsupported manual replay schema".into());
|
||||
}
|
||||
if self.schema == "fidc.observed-manual-executions/v2"
|
||||
&& (!self.position_exposure_events.is_empty()
|
||||
|| !self.legacy_position_exposure_bps.is_empty())
|
||||
{
|
||||
return Err("runtime configuration requires manual replay v3".into());
|
||||
}
|
||||
crate::position_exposure::PositionExposureTimeline::from_events(
|
||||
&self.position_exposure_events,
|
||||
)?;
|
||||
if self.position_exposure_events.iter().any(|event| event.effective_at > self.observation_cutoff) {
|
||||
return Err("observed runtime position event is after the evidence cutoff".into());
|
||||
}
|
||||
if self
|
||||
.legacy_position_exposure_bps
|
||||
.values()
|
||||
.any(|value| !(0..=10000).contains(value))
|
||||
{
|
||||
return Err("legacy manual exposure is outside 0..10000 bps".into());
|
||||
}
|
||||
identifier(&self.runtime_id)?;
|
||||
identifier(&self.account_id)?;
|
||||
if self.source_contract_sha256.len() != 64
|
||||
|
||||
Reference in New Issue
Block a user