fix: record the positions a terminated run leaves open - #181
Merged
mohammadp1001 merged 1 commit intoAug 13, 2026
Merged
Conversation
mohammadp1001
force-pushed
the
feat/report-run-cost
branch
from
August 13, 2026 22:18
bf3ceb3 to
1b995b2
Compare
The 2026-08-13 run was killed at 17:21 UTC holding 16 shares of XLE whose exit window had already expired. Nothing closed it, nothing warned, and nothing recorded it - the fact existed only at the broker until someone thought to look. The run before it died the same way and only escaped because it happened to be flat. There was no signal handling at all, so SIGTERM killed the process outright and run()'s finally block never executed. Termination now raises RunTerminatedError, which lets the shutdown path run. A terminated run does not trade on its way out - closing positions on a signal would realise them at whatever price the reaper happened to pick. It also does not drain, because draining waits candle by candle and a process being killed does not have that time. Instead it exits fast and says exactly what it is leaving behind, on stderr and as POSITIONS_ABANDONED telemetry: ticker, size, entry, stop, and window deadline - enough to act on by hand. Previous handlers are restored on the way out so embedding the scheduler does not permanently redirect the host process's signals. This cannot help against a hard kill. SIGKILL and TerminateProcess are not deliverable to any handler, and startup reconciliation (#177) remains the only net for that case. What it covers is every orderly stop: Ctrl-C, a supervisor's SIGTERM, a container shutdown.
mohammadp1001
force-pushed
the
fix/record-positions-left-open-on-shutdown
branch
from
August 13, 2026 22:20
a19b3d8 to
eb79c86
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
run-4was killed at 17:21 UTC on 2026-08-13 holding 16 shares of XLE whoseexit window had already expired at 17:22:38. Nothing closed it, nothing warned,
and nothing recorded it. The fact existed only at the broker until someone thought
to go and look.
run-3died exactly the same way and only escaped because it happened to be flatat the time. Two for two.
The cause is simple: there was no signal handling at all. SIGTERM killed the
process outright, so
run()'sfinallyblock - which already contains_drain_open_positions- never executed.The fix
Termination now raises
RunTerminatedError, which lets the shutdown path run atall. What that path does was a deliberate choice between two options:
realise them at whatever price the reaper happened to pick.
being killed does not have that time.
So it exits fast and says exactly what it is leaving behind - on stderr and as
POSITIONS_ABANDONEDtelemetry:Ticker, size, entry, stop, and window deadline - enough to act on by hand, which
is precisely what was missing today.
Previous handlers are restored on the way out, so embedding or importing the
scheduler does not permanently redirect the host process's signals.
What this does not fix
A hard kill. SIGKILL and Windows
TerminateProcessare not deliverable toany handler, here or anywhere else, so if that is what reaped the last two runs
this changes nothing for them. Startup reconciliation (#177) remains the only net
for that case.
What it does cover is every orderly stop: Ctrl-C, a supervisor's SIGTERM, a
container shutdown. The root cause of the reaping itself is still unknown and is
recorded in the handoff as an open question.
Verification
ruff check alphoryn/ tests/cleanname list rather than relying on SIGBREAK's absence, so coverage holds on both
Windows and Linux
GOOGLE_APPLICATION_CREDENTIALS=/nonexistent/adc.json python -m pytest -q