Compare simulated stock opportunities with actual-trade records
Comparing a simulated stock signal with a journal entry requires evidence that both refer to the same opportunity. A nearby timestamp or a shared ticker is not enough: different rule versions, directions and observation scopes can produce unrelated events. Establish the match first, then describe timing, quantity and price differences without assuming why the human acted differently.
This guide compares four synthetic opportunities with four synthetic execution records under one fixed A/B USD basket and version v1. It accesses no user account or broker export. Opportunity matching is an external research method, not a verified CoTrading import feature, live replication channel or estimate of what an unobserved trade would have earned.
Define which two event layers are being compared
One side is an eligible simulated opportunity with a stable identity, security, direction, decision boundary, requested quantity and reference price. The other is a supplied execution record with its own identity and an explicit opportunity reference. A signal is not automatically a completed virtual fill, and an actual execution is not proof that a simulated rule produced the same request.
The fixture’s actual-trade records are invented. In real research, obtain an authorized read-only export and preserve its source fields and coverage boundaries. Do not infer missing execution rows from the final holdings. A position can result from several fills, a transfer or a corporate action, and reconstructing those events is a separate accounting problem.
The SEC fees bulletin recommends checking statements and trade confirmations. Such records can support execution evidence, including costs. Our matching example compares prices and quantities only; it does not calculate net realized profit or replace a fee-aware journal reconciliation. Keep the provenance and cost treatment beside any downstream performance report.
Clear coverage and identity before judging deviations
Freeze the universe and rule version that governed each opportunity. Our comparison scope is USD and securities A/B. An execution in C is outside coverage, even if it occurred close to a simulated event in B. Do not report it as a human failure to follow that event. The strategy never established an observable C opportunity in this experiment.
Match canonical security identity and currency rather than company-name text. A version mismatch also blocks comparison: v2 can express a genuinely different hypothesis or start after another cutoff. The example preserves identity_or_version_mismatch instead of using the closest available v1 event to manufacture a match. Unknown or blank identity fields are rejected before pairing.
The fixture assumes the source already supplies explicit opportunity references. If a real export lacks them, record a separate proposed-matching policy and its ambiguous candidates. Do not silently apply this demonstration’s confident labels to inferred references. An analyst’s manually assigned link is evidence to review, not an intrinsic property of two independent records.
Declare time units and the matching window
The example uses integer sequence times and accepts an execution from zero through three units after the opportunity. These units are synthetic ordering positions, not minutes or exchange sessions. An execution before the opportunity fails. A real implementation must define its time basis before choosing a tolerance and must not widen the window until an attractive pair happens to fit.
The Python datetime documentation explains why timezone-aware and naive times differ. Real timestamp preprocessing should resolve offsets and daylight-saving behavior before mapping records to a common comparison timeline. The current function receives prepared integers, so it does not claim to implement that calendar or timestamp-adapter layer itself.
Preserve the original timestamps alongside normalized values. A delayed confirmation timestamp is not necessarily the execution time, and a signal-generation timestamp may follow the underlying observation boundary. If the source cannot distinguish them, classify timing as unresolved instead of interpreting an arbitrary export field as a precise measure of reaction speed.
Require an unambiguous relationship
Group execution rows by their explicit opportunity reference. No referenced execution produces no_actual_execution. More than one produces ambiguous_actual_group, because this small contract does not aggregate partial fills. Exactly one proceeds through security/currency/version, direction and timing checks. The order prevents a quantity difference on an unrelated event from being presented as a meaningful execution deviation.
Duplicate execution identities are rejected globally before grouping. This differs from multiple unique fills referencing one opportunity: the former can double-count the same source row, while the latter may be legitimate partial execution needing another contract. Preserve both situations distinctly, rather than dropping duplicates until the remaining group happens to contain exactly one row.
A completed match reports signed quantity difference, time lag and raw execution-price minus reference-price difference. It does not label that raw price difference favorable across both buy and sell directions. Nor does it infer the trader’s intention, order type or available liquidity. Those explanations require evidence beyond the matching fields supplied here.
Read the main opportunity ledger
S1 requests buying three A at sequence time ten with reference price 100. E1 explicitly references S1, buys two A under v1 USD at time twelve and price 101. It matches, with quantity difference minus one, lag two and raw price difference plus one. Those differences describe the records without asserting that the absent third share would have been profitable.
S2 requests selling A at time twenty. Its referenced E2 is a buy at twenty-one, so the result is direction_mismatch. S3 requests buying B at thirty but has no referenced execution, yielding no_actual_execution. S4 requests selling B at forty, while its E4 is tagged v2 rather than v1, yielding identity_or_version_mismatch.
E3 buys C and references an outside opportunity. It is retained as coverage_gap, not attached to S3 merely because their times are close. Across the main case there is one matched opportunity, three other opportunity outcomes and one uncovered execution row. A summary must preserve these different denominators rather than claiming four signals produced five trades or treating every row as one failed match.
Challenge a match through another mechanism
Move E1 from time twelve to fourteen without changing its identity, direction or quantity. Its four-unit lag exceeds the frozen window and becomes timing_mismatch. This is a timing-policy failure, distinct from S2’s opposite direction and S4’s version difference. Each classification has a specific field-level basis that another reviewer can reproduce.
Next add a unique E5 that also references S1. The group becomes ambiguous_actual_group rather than keeping the first row and ignoring the second. Adding an exact duplicate E1 identity instead raises an input error. These tests prevent apparently convenient one-to-one matching from concealing repeated records or unimplemented partial-fill aggregation.
Run and hand off the comparison
Download the complete example, save R643.py and run python R643.py with Python 3.12 or later. It uses only the standard library and synthetic records. Assertions verify every main status, the three S1 differences, the late-event counterexample, multiple-fill ambiguity and duplicate-identity rejection. No account credentials or live trading channel is used.
Deliver the matched-opportunity ledger, unmatched event lists, coverage evidence, version references and timing convention. A discrepancy is a question for review, not automatic proof of a behavioral mistake or counterfactual lost profit. If a mapping is corrected, preserve the previous result and rerun under a new recorded interpretation. Future rule observation should continue collecting all planned opportunities, not just the records that are easiest to match.
At acceptance, reconcile each opportunity’s outcome and verify that every supplied execution identity remains represented. The two row counts need not match, but hidden deletion and repeated allocation of one execution must not occur. Any extension allowing splits or merged opportunities needs a separately declared quantity-allocation and fee-ownership policy.
Label a revision as a source-export correction, rule-version change or matching-interpretation change. These are different operations. A changed rule needs a new opportunity set, not a new version label pasted onto old signals. Preserve row-level differences so improved data completeness is not misreported as better execution skill.
FAQ
Can the nearest timestamp establish a match?
Not under this contract. The example requires an explicit reference and then verifies identity, direction and timing.
Does no_actual_execution prove the person missed a profitable trade?
No. It only describes the supplied records. Missing history, costs, liquidity and the opportunity’s future outcome require separate evidence.