Skip to content

ThetaPrime v0.35.15

Choose a tag to compare

@dsharma01 dsharma01 released this 26 Aug 18:58
· 9 commits to main since this release

Download ThetaPrime-0.35.15-setup.exe

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=ACTIVE with 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.json now writes via temp-file + os.replace instead of truncating in place, so a crash mid-write can no longer wipe every stored credential.
  • Fixed a stuck AWAITING_REENTRY status left over from a day run_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_entry legs 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, sets position_dte (not just long_dte), and fetches a fresh VIX reading when a delta_stop/iv_stop is in scope — these exits previously never fired on long legs.
  • Black-Scholes formulas no longer raise ZeroDivisionError on vix=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) to request.get_json(silent=True) or {}.
  • Concurrent safety/broker/backup config saves are now lock-guarded against a lost-update race.
  • _settle_close_order now 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 with AttributeError.
  • Scheduled backup pruning now rejects keep <= 0 instead 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 .py strategies.
  • GammaTarget/DeltaOtm selectors now raise a clean error when min_otm_pct > max_otm_pct empties 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-Auth header 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 validates user/password up front instead of a bare KeyError on a partial config.
  • Sim logging handler cleanup now only removes handlers the sim itself added, instead of clearing whatever's present.

[0.35.14] — 2026-08-26

Fixed

  • Loss-limit breach no longer force-downgrades an unrelated strategy's real exits to paper — the arm gate now exempts monitor-mode runs, and long_exit was added to the daily loss-limit check's event types (composer diagonal/covered-call terminal closes were never enforced against it). Reentry now also respects STRATEGY_ENTRIES_BLOCKED, closing a gap where a disarmed strategy's monitor tick could still place a brand-new real reentry order mid-breach.
  • Rejected-leg reconciliation tightened from >= units to == units so a differently-sized unrelated position can no longer be mistaken for a rejected leg's fill.
  • Breeze weekly-option symbol parsing fixed for Oct/Nov/Dec expiries — the single-digit-month regex mis-parsed them as a different month/day; now tries both month widths and disambiguates by closest-to-today (mirrors an existing Dhan adapter fix for the same collision).
  • Notifier: fixed a dedup-prune KeyError race (now locked), a LIB V1 entry alert silently dropping its Target/SL line, and icon selection crashing on non-numeric P&L values.
  • Dhan adapter hardening: session-dead state is now tracked per token at the class level (previously per-adapter-instance, so it never actually engaged since app.py builds a fresh adapter per request) — ltp()/quote() now raise once a session is dead instead of returning an empty price dict; the scrip-master loader is now lock-guarded against concurrent double-downloads.