diff --git a/src/core/quote-outcomes.mjs b/src/core/quote-outcomes.mjs index 191d338..7ab409f 100644 --- a/src/core/quote-outcomes.mjs +++ b/src/core/quote-outcomes.mjs @@ -37,9 +37,7 @@ export function deriveQuoteOutcomeRecords({ inventorySnapshots, activeAssetIds, }); - const latestInventoryAt = inventoryDeltas.length - ? inventoryDeltas[inventoryDeltas.length - 1].observed_at - : latestSnapshotTimestamp(inventorySnapshots); + const latestInventoryAt = latestSnapshotTimestamp(inventorySnapshots); const candidatesByMovement = new Map(); const candidatesByQuote = new Map(); diff --git a/test/quote-outcomes.test.mjs b/test/quote-outcomes.test.mjs index a73c47b..73a4dab 100644 --- a/test/quote-outcomes.test.mjs +++ b/test/quote-outcomes.test.mjs @@ -130,6 +130,34 @@ test('submitted quote without settlement becomes not filled only after deadline assert.equal(outcome.attributed_inventory_delta, null); }); +test('older unrelated inventory movement does not block later no-fill expiry', () => { + const [outcome] = deriveQuoteOutcomeRecords({ + submissions: [submittedResult('quote-after-old-move', '2026-04-02T18:20:00.000Z')], + commands: [exactOutCommand('quote-after-old-move')], + inventorySnapshots: [ + inventorySnapshot('2026-04-02T18:13:00.000Z', { + [BTC.assetId]: '0', + [EURE.assetId]: '100000000000000000000', + }), + inventorySnapshot('2026-04-02T18:13:33.000Z', { + [BTC.assetId]: '1', + [EURE.assetId]: '99999999999999999999', + }), + inventorySnapshot('2026-04-02T18:22:00.000Z', { + [BTC.assetId]: '1', + [EURE.assetId]: '99999999999999999999', + }), + ], + btcAsset: BTC, + eureAsset: EURE, + now: '2026-04-02T18:22:00.000Z', + }); + + assert.equal(outcome.outcome_status, 'not_filled'); + assert.equal(outcome.outcome_reason, 'deadline_elapsed_without_settlement'); + assert.equal(outcome.payload.evidence.latest_inventory_observed_at, '2026-04-02T18:22:00.000Z'); +}); + test('ambiguous inventory movement is not counted as completed settlement', () => { const outcomes = deriveQuoteOutcomeRecords({ submissions: [