将手工观察接入执行时钟并保留来源与账本语义

This commit is contained in:
boris
2026-09-14 11:41:15 +08:00
parent 93de28d369
commit 7f0c6a008a
10 changed files with 435 additions and 95 deletions
+26 -8
View File
@@ -413,7 +413,7 @@ pub struct AppliedManualFill {
/// One replay owns its immutable trace and progress. Advancing is atomic even
/// if a later receipt in the same step disagrees with the shadow account.
pub struct ManualReplayCursor {
replay: ManualExecutionReplay,
replay: std::sync::Arc<ManualExecutionReplay>,
indices: Vec<(usize, usize, usize)>,
cursor: usize,
clock: Option<DateTime<Utc>>,
@@ -448,6 +448,10 @@ pub struct ManualReplayApplication {
impl ManualReplayCursor {
pub fn new(replay: ManualExecutionReplay) -> Result<Self, String> {
Self::from_shared(std::sync::Arc::new(replay))
}
pub fn from_shared(replay: std::sync::Arc<ManualExecutionReplay>) -> Result<Self, String> {
replay.validate()?;
let mut indices = Vec::new();
for (a, action) in replay.actions.iter().enumerate() {
@@ -485,6 +489,27 @@ impl ManualReplayCursor {
portfolio: &mut PortfolioState,
data: &DataSet,
has_pending_orders: bool,
) -> Result<Vec<ManualReplayApplication>, String> {
let end = self.cursor
+ self.indices[self.cursor..].iter().take_while(|&&(a, o, f)| {
self.replay.actions[a].orders[o].fills[f].observed_at <= at
}).count();
self.advance_through(at, end, portfolio, data, has_pending_orders)
}
/// One receipt at a time lets callbacks observe the intermediate state
/// when multiple fills share a timestamp but have distinct durable sequences.
pub fn advance_next(
&mut self, portfolio: &mut PortfolioState, data: &DataSet, has_pending_orders: bool,
) -> Result<Option<ManualReplayApplication>, String> {
let Some(at) = self.next_observation_at() else { return Ok(None); };
let mut applications = self.advance_through(at, self.cursor + 1, portfolio, data, has_pending_orders)?;
Ok(applications.pop())
}
fn advance_through(
&mut self, at: DateTime<Utc>, end: usize, portfolio: &mut PortfolioState,
data: &DataSet, has_pending_orders: bool,
) -> Result<Vec<ManualReplayApplication>, String> {
if at > self.replay.observation_cutoff {
return Err("manual observation clock exceeds the frozen evidence cutoff".into());
@@ -492,13 +517,6 @@ impl ManualReplayCursor {
if self.clock.is_some_and(|clock| at < clock) {
return Err("manual observation clock moved backwards".into());
}
let end = self.cursor
+ self.indices[self.cursor..]
.iter()
.take_while(|&&(a, o, f)| {
self.replay.actions[a].orders[o].fills[f].observed_at <= at
})
.count();
if end == self.cursor {
self.clock = Some(at);
return Ok(vec![]);