Fix MultimarketPower: bar-0 forward-fill guard and thread-safe trade buffers - #173
Conversation
|
Thanks for the update. The direction makes sense, but we cannot merge this version yet. |
|
Thanks for the detailed review - you were right about the handoff window. Pushed a follow-up commit that closes it: trade handlers now check the flag inside the lock (buffer-or-live is atomic), and after the historical replay a drain loop repeatedly swaps out the buffers under the lock and replays each batch, flipping _bigTradesIsReceived inside the lock only once both buffers are empty. The finally-block clear is gone, so trades arriving during the transition are always replayed rather than dropped. Zero-history responses now also drain instead of returning early |
|
The drain loop addresses the previously reported handoff issue, but another update-loss window remains. |
…ng realtime The previous version snapshotted _ticks/_trades under the lock, but _bigTradesIsReceived was only set after CalculateHistory returned. Trades arriving between the snapshot and the flag flip were appended to the original buffers and then discarded by the finally-block clear. Close the handoff window: - Trade handlers now check the flag inside the lock: a trade is either buffered under the lock or processed as realtime, never dropped. - CalculateHistory no longer clears the buffers in finally. After the historical replay it calls DrainBufferedData, which repeatedly swaps out the buffers under the lock and replays each batch, and flips _bigTradesIsReceived inside the lock only once both buffers are empty, so no trade can be buffered after the flip. - The flag becomes volatile (it is still read lock-free in OnCalculate) and is reset inside the lock in OnFinishRecalculate. - Zero-history responses now also drain buffered live trades instead of returning early.
…tches Buffered updates used to overwrite the last buffered trade. When an update arrived after DrainBufferedData had detached the batch, the buffer was empty and the update was dropped, so the trade was replayed with an outdated volume. Buffer new trades and updates as ordered events and replay each one through CalculateTrade with its own update flag. An update for a trade that is not the last processed one (for example, a trade already contained in the history response) is skipped as before, so its volume is not counted twice. _lastTrade is reset together with the accumulated deltas.
df49174 to
3d7d0f9
Compare
|
Thanks for the detailed analysis. Updated:
Test: the repository has no test project, so I verified it with a small console harness that compiles the actual |
Two small robustness fixes for
MultiMarketPower, in separate commits.1. Guard
OnCalculateforward-fill against bar 0OnCalculatecopies the previous bar's value into a zero bar:vds[bar] = vds[bar - 1]. The guard only checkedbar != CurrentBar - 1, so on a chart with a single bar (CurrentBar == 1,bar == 0) it readsvds[-1].Added
|| bar == 0to the early return.2. Synchronize the realtime trade buffers
_ticks/_tradesare appended from the market-data callbacks (OnNewTrade,OnCumulativeTrade,OnUpdateCumulativeTrade) whileOnFinishRecalculateclears them andCalculateHistoryiterates them, with no synchronization - even though a_lockerfield is already declared. This races on reload under live data (the existingcatch (NullReferenceException) //on reset exception ignoredinCalculateHistoryis a symptom). All buffer access is now wrapped inlock (_locker), and the replay iterates a snapshot taken under the lock instead of the live list.No behavior change in the steady state; only removes the race and the bar-0 edge case.