ThetaPrime v0.35.16
Download ThetaPrime-0.35.16-setup.exe
Fixed
Batches 10-11 of the 2026-08-25 systematic bug hunt (findings #45-57), closing out all 58 findings. Full suite: 1232 passed (up from 1220).
_run_short_reentrynow reuses the parked long legs' actualunitsfor re-entered short legs instead of recomputingsizing.qty()fresh, which could land a different lot count undercapital_pctsizing days after entry — leaving the diagonal naked on mismatched lots.scanner.pyOI/price baselines no longer treat a genuine 0 (fresh-listed strike's previous-day OI, or a flat net_change) the same as missing/undefined — both now show their correct "Flat"/0 state instead of falling back to a misleading label.storage.pydecrypt/re-encrypt of pending DB files now attempts every file before raising, instead of one bad file blocking every later file in the list from being (de/re)crypted.app.py_leg_token_cachenow resets on a date change instead of growing unbounded for the life of the waitress process.- Rejected-leg reconciliation now matches the position-count change since a snapshot taken right before order placement, instead of raw broker quantity, so it can no longer mistake a coincidentally-same-sized pre-existing position for a genuine new fill.
storage.is_token_fresh()is now memoized per-request instead of doing 2 keyring reads twice per page load./api/marginsnow fetches all 3 brokers' margins concurrently instead of a slow broker blocking the others.- Strategy list + archived list are now computed from one directory scan instead of two.
run_monitornow batches oneltp()call per tick across all held legs instead of a separate round-trip per leg group.- Close-and-verify logic shared between
_close_legs/_close_for_reentry, and the OTM strike-scan shared acrossPremiumTarget/GammaTarget/DeltaOtm, are now single implementations (pure refactor, no behavior change). publish-release.ps1release notes now scope to the current version's changelog section instead of the whole file, avoiding GitHub's 125000-char release-body cap as CHANGELOG.md grows.
[0.35.15] — 2026-08-26
Fixed
Batches 3-9 of the 2026-08-25 systematic bug hunt (findings #6-44), plus the 2026-08-26 orphaned-fill incident. Full suite: 1220 passed.
- KiteAdapter now tracks dead-session state at the class level (mirrors the fix already shipped for Breeze/Dhan) — a fresh adapter per request previously meant it never armed.
- Dhan contract validation now clears the class-level dead-token flag between checks, not just the instance flag, avoiding a repeat of the 2026-07-19 incident.
convert_to_limit(Kite/Breeze/Dhan) no longer re-places a marketable LIMIT on top of an order whose cancel wasn't confirmed CANCELLED/REJECTED, which could double a leg.- Strategy engine now aborts cleanly when a structure builds zero legs, instead of leaving
status=ACTIVEwith no orders placed. - Fixed an unpadded-hour bug ("9:45" vs "09:45") that silently broke entry-time filters, EOD exits, and the fullday-sim/scheduler "latest exit time" calc; spec save-time validation now rejects unpadded hours.
- Fullday-sim orphaned-thread handling now waits for the orphaned thread to finish before restoring env / releasing its lock, so a second concurrent sim can't corrupt the first's date/hour/minute env vars; the lock is also released on exception instead of deadlocking.
- One strategy's malformed active-trade state no longer crashes reconciliation for every other strategy.
.secrets.jsonnow writes via temp-file +os.replaceinstead of truncating in place, so a crash mid-write can no longer wipe every stored credential.- Fixed a stuck
AWAITING_REENTRYstatus left over from a dayrun_eod()never ran, which had been silently skipping the next day's entry. - Fill verification no longer treats a genuine 0.0 average fill price as "no fill" and skips backfilling it.
- Orphaned-fill incident (2026-08-26): subprocess timeout raised to 240s (was shorter than Breeze's own worst-case fill-poll budget, guaranteeing a mid-poll kill); a killed run now gets a proper run-history row and crash notification instead of vanishing; entry legs are checkpointed to disk after each successful placement, not only on failure. Reconciliation now reads
_partial_entrylegs and a diagonal's held long legs on the ACTIVE branch; the periodic reconcile safety net now runs whenever real trading is configured, not only once a leg is already believed open; the UI modal no longer hides held long legs after a successful short re-entry. - Close orders get the same mid-poll checkpoint as entries, across all 7 close call sites (close, close-long, close-for-reentry, adjustment rolls, retry-unconfirmed, rollback).
lot_size()now raises for a genuinely unrecognized underlying instead of silently defaulting to NIFTY's lot size.- Long-leg monitor context (diagonal hedges) now carries
entry_vix, setsposition_dte(not justlong_dte), and fetches a fresh VIX reading when adelta_stop/iv_stopis in scope — these exits previously never fired on long legs. - Black-Scholes formulas no longer raise
ZeroDivisionErroronvix=0; return the correct zero-vol limit instead. - Margin estimate no longer treats a genuine 0.0
wing_pts(same-strike wing) as undefined risk. - Nine JSON-body routes switched from
request.json or {}(raises on missing/non-JSON Content-Type) torequest.get_json(silent=True) or {}. - Concurrent safety/broker/backup config saves are now lock-guarded against a lost-update race.
_settle_close_ordernow catches broker exceptions instead of letting one dead session abort the whole retry/roll loop for every other unconfirmed leg.- AI
ask()now raises a clean error on a tool-calls-only reply (content: null) instead of crashing callers withAttributeError. - Scheduled backup pruning now rejects
keep <= 0instead of deleting every backup including the one just made. - Strategy archiving now derives the state-file path the same way app.py does, fixing a false "no open position" check for legacy
.pystrategies. GammaTarget/DeltaOtmselectors now raise a clean error whenmin_otm_pct > max_otm_pctempties the scan range.- License revocation check now stamps "checked" even on a failed network call, so a sustained outage doesn't force the blocking 5s check on every page load.
- Cloning a strategy now also checks for an existing display/param sidecar file, not just the strategy file itself.
multi_sim.extend()no longer widens the session's date range before the corresponding batch of days has actually run.- Local-credential-store writes (
storage.py) are now lock-guarded against concurrent read-modify-write races, including the trial-start write-once guard. - Revocation state and pending-request storage now go through the OS keyring first, local file only as fallback.
- Fixed a SQLite connection leak in the app-events table helpers.
- The
X-MCP-Authheader now shares/login's brute-force lockout, instead of being uncapped. - MCP server error responses now surface app.py's actual error message instead of a generic HTTP status line.
- First-run data-encryption setup no longer raises unguarded on a keyring/disk failure.
sync_backup_job()no longer takes down the entire scheduler when a saved backup time is out of range.- Run IDs are now unique to sub-second resolution, preventing two same-second runs from overwriting each other's tracking entry.
_send_email()now validatesuser/passwordup front instead of a bareKeyErroron a partial config.- Sim logging handler cleanup now only removes handlers the sim itself added, instead of clearing whatever's present.