Releases: dsharma01/ThetaPrime-Dist
Release list
ThetaPrime v0.35.24
Download ThetaPrime-0.35.24-setup.exe
Fixed
Bug hunt area 8 — frontend (templates/static), 14 findings.
index.html: dashboard card no longer drops_long_activeP&L; two leg-symbol XSS gaps (state card, Mark Exited form) escaped; January weekly-expiry date-parsing collision in_symbolExpiry()fixed.composer.js:chipLabel()XSS on an untrusted spec.json block type escaped; draft-restore no longer loses its dirty/cleansavedSnapshotbaseline.multi_sim_results.html:renderRow()/renderSessionMeta()unescaped fields fixed;stopSim()now surfaces failure feedback instead of failing silently.performance.html:JSON.stringify-in-onclickapostrophe breakout fixed across 8 sites;eventBadge()event type escaped.option_scanner.html: underlyings/expiries escaped into<option>values.docs.html: DOMPurify added aroundmarked.parse()output (defense-in-depth — the/docs/<path>route is already locked todocs/*.mdvia regex).reports.html: falsy-zero strikes check fixed; dead row-highlight code fixed (wrong operator precedence + missingclassList.add).
Validated archive/CODE_REVIEW_TEMPLATES_STATIC_2026-08-28.md — 6/8 confirmed and folded into the above, 2 stale-loading-UI findings deferred as refactor-scoped.
Bug hunt area 9 — performance.py, 2 findings.
add_event()/today_pnl(): trade dates were derived from the machine's local clock instead of IST, so a host not set to IST could attribute an exit to the wrong trading day. Now uses a fixed IST offset, matchingholidays.py's_ISTpattern.get_summary(): a strategy/mode bucket was created for any matching row before checkingREALIZED_EVENT_TYPES, so a strategy with only entry/monitor/skip events rendered as a phantom "0 trades" summary card. Bucket creation is now gated on a realized exit event.- Also hardened
today_pnl()/get_summary()against malformed JSON rows so a damaged row is skipped instead of 500ing the dashboard.
Validated archive/CODE_REVIEW_PERFORMANCE_2026-08-28.md, 2/2 confirmed and fixed.
Bug hunt area 10 — runner.py/engine.py/reconciliation.py, 3 findings.
engine.py_close_and_record(): a partial/unconfirmed close was booked as a finalized full-quantity exit with no way to later correct the historical_closedrecord. Now taggedunconfirmed_legs, and a new_correct_closed_record()helper lets_retry_unconfirmed_close()'s per-leg true-up patch that record directly._execute_long_roll()(and sibling_execute_roll(), found in passing): a roll's replacement leg was dropped entirely when its fill never confirmed, leaving it untracked by reconciliation. Now kept taggedunconfirmed/order_idsoreconciliation.py's existing fold-in picks it up automatically.runner.py: the TRADE_EVENT parsing loop aborted on the first malformed line, silently dropping every later valid event. Moved to per-line try/except, log-and-continue.
Validated archive/CODE_REVIEW_RUNNER_ENGINE_RECONCILIATION_2026-08-28.md, 3/3 confirmed and fixed. reconciliation.py itself had no additional defect.
[0.35.23] — 2026-08-28
Fixed
Bug hunt area 4 — notifications/AI/MCP, 5 findings.
notifier.pysend_notification(): the dedup cache no longer suppresses a retry after an all-channels-failed outage — the placeholder is popped if nothing actually sent, instead of blocking the same alert for the rest of the 10-minute dedup window.notifier.pyformat_trade_event(): malformed/legacy numeric fields (rs,total_rs, per-leg entry/exit) no longer raiseValueErrorand drop the whole notification — added_safe_float()alongside the existing_sign/_is_negativefallback.notifier.pydesktop channel:get_desktop_alerts()and_send_desktop()now share one lock, fixing aRuntimeError: deque mutated during iterationunder concurrent traffic.mcp_server.py:THETAPRIME_MCP_PASSWORDis no longer sent over plaintexthttp://to a non-loopback host — a non-loopbackTHETAPRIME_URLnow requireshttps://or the server refuses to start.ai.pyask(): the bring-your-ownbase_url(custom OpenAI-compatible endpoint) now rejects non-http(s) schemes and the cloud metadata IP, closing an SSRF path that could forward the stored AI key.
Bug hunt area 5 — strategy components & composer, 2 findings.
strategies/components/exits.py:PremiumPctTargetandStopLossPctPremium's duplicated net-premium calculation deduplicated into a shared_net_premium(ctx)helper.strategies/spec.pyestimate_min_capital(): a covered-call holdings-lookup failure (broker offline, rate limit) now logs a warning instead of silently falling back tocovered_qty=0, which looked identical to "no shares actually held" and quietly inflated the margin estimate.
Bug hunt area 6 — simulation & backtesting, 6 findings.
sim_engine.py/strategies/nifty_strangle_live.py: a user-requested sim stop no longer still runsrun_eod()and books P&L for the aborted day; custom.pystrategies (nifty_strangle_live.py,lib_v1_trade.py,Lib_v1_1_lot.py) can now actually be interrupted via a newsim_engine.should_stop().multi_sim.py: the declared-but-unused_multi_sim_lockis now acquired instart()/extend(), closing a race where two concurrent requests could launch overlapping sessions against the same strategy's carry-forward state file.multi_sim._run_extend(): a stop landing right as the prepend batch finishes no longer skipsrecompute_cumulative(), which left every pre-existing day'scumulative_ptsstale.sim_sessions.compute_summary(): a same-day-opened, still-open position at EOD no longer vanishes from every summary bucket (was excluded from trades, no_trades, and errors alike).sim_engine.py: a non-positiveSTRATEGY_MONITOR_INTERVALnow raisesValueErrorup front instead of spinning the tick loop forever.broker/__init__.pyget_broker(): singleton check/rebuild/assign is now guarded by a lock (found while fixing the sim-engine reset call), via a newbroker.reset_singleton().
Bug hunt area 7 — app.py routes, 11 findings.
square_off(): a close order that failed to place or didn't confirm no longer marks the positionCLOSED— unconfirmed legs stay tracked as open undermanual_controlinstead of silently dropping real exposure from monitoring/reconciliation.square_off():SHORT_CLOSEDfull-open-leg derivation now reads_long_active['legs']instead of the never-pruned top-levellegsdict, matchingmanual_exit's existing fix for the same bug class.square_off()/edit_trade_state(): three falsy-zero bugs fixed (ltp_map.get(...) or entry_price,ref_price or None,average_price or entry_price) — a genuine0.0LTP/reference/average-price is no longer treated as missing.- Daily loss limit can no longer be silently disabled by a
NaN/inf/-infvalue —_parse_optional_float()now rejects non-finite input. - Auth/OAuth callback errors (Kite, Breeze, Dhan — 8 call sites) now route through
_safe_error_message()instead of leaking raw exception text into flash/redirect messages. - Scheduled-backup time validation now range-checks hour/minute instead of accepting shape-only garbage like
99:99. /api/desktop_alerts's loopback auth bypass now checks proxy headers like its sibling dev-only bypass, closing a same-host-proxy P&L/alert content leak.trade_reset()now takes the same.closelockevery other live-state route holds, preventing an interleaved read-then-delete from dropping a just-written position.multi_sim.start()/extend()now return-1(not0) when blocked by an already-active session, so the UI reports "already running" instead of a misleading "no trading days found"./ai/prompt/reportsno longer 500s on an unhandledAttributeErrorwhenreports_data()hits its own exception branch.
ThetaPrime v0.35.18
Download ThetaPrime-0.35.18-setup.exe
Added
- India VIX ticker in the navbar, mirroring the existing Nifty ticker (
/api/vix, Kite→Breeze→Dhan with NSE fallback, 3s cache, 5s poll).
Fixed
Adversarial bug hunt round — 10 findings (RCE, races, key/secret leaks, auth bypass).
- Path traversal via Windows backslashes (not blocked by Flask's
<name>converter) could reach arbitrary file execution on/run_strategyet al. (runner.py) and arbitrary file read/write on ~10 trade-state routes (app.py); closed. manual_entryno longer trusts an unverified phantom position — now requiresbroker.positions()confirmation before saving, matchingedit_trade_state'sconfirm_activepattern.- Legacy
.pystrategies (lib_v1_trade,Lib_v1_1_lot,niftyStrgl_AI2,nifty_strangle_live) gain the same.entrylockhardening the spec engine already had, closing a double-entry race on a double/run_strategyPOST. manual_entry/manual_exit/edit_trade_state/close_long_legsnow take the same.closelockthe automated close andsquare_offalready honor, closing a race against an in-flight close.- Backup zip no longer bundles the data-at-rest encryption key material in plaintext (redacted, not excluded — credentials-in-backup stays documented/intentional); restore no longer clobbers a live
.secrets.jsonwith the redacted copy. - Dev-loopback auth bypass no longer trusts
remote_addrwhen a forwarding header is present (a reverse proxy/tunnel on the same host used to make every request look like127.0.0.1). - Open-redirect guard now strips tab/CR/LF before checking, matching browser behavior before navigating.
notifier.pyno longer logs the Telegram bot token / webhook secret embedded in arequestsexception's URL.change-password/recovery-code-regeneratenow share/login's brute-force lockout.
[0.35.17] — 2026-08-27
Fixed
/api/desktop_alertsis now exempt from the prod login gate.- Webhook notification config was missing its "Send Test" button; added.
- "Close Long Legs" button label corrected to "@ EOD" (was stale "@ 15:20").
ThetaPrime v0.35.17
Download ThetaPrime-0.35.17-setup.exe
Fixed
/api/desktop_alertsis now exempt from the prod login gate.- Webhook notification config was missing its "Send Test" button; added.
- "Close Long Legs" button label corrected to "@ EOD" (was stale "@ 15:20").
[0.35.16] — 2026-08-27
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.
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.
ThetaPrime v0.35.15
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=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.
[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_exitwas added to the daily loss-limit check's event types (composer diagonal/covered-call terminal closes were never enforced against it). Reentry now also respectsSTRATEGY_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
>= unitsto== unitsso 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
KeyErrorrace (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.
ThetaPrime v0.35.13
Download ThetaPrime-0.35.13-setup.exe
Changelog
All notable changes to ThetaPrime are documented here.
Format follows Keep a Changelog.
Versioning follows Semantic Versioning.
[Unreleased]
[0.35.13] — 2026-08-25
Fixed
- Breeze no longer fails an entire session over a single contract's "not authorized to trade ..." rejection — the session-expiry detector was matching any "authoriz..." substring, which also caught this contract-scoped refusal. It also now re-selects around a contract the broker quotes live but won't let you trade, instead of parking the strategy in
PARTIAL_ENTRY_FAILEDwith no position to reconcile (capped at 3 skips/day, cleared next trading day). convert_to_limitno longer re-prices a stuck Breeze order worse than the one it cancels — breeze_connect already rewritesorder_type="market"into a LIMIT at ±10% of LTP before sending it, so the old "rescue" was cancelling an aggressive order and replacing it with a less aggressive one, losing queue position for nothing. Conversion still runs for SELL-to-close, where the point is an intrinsic-value floor, not fill speed.- Session-dead state is now tracked per session token instead of per adapter instance — app.py builds a new adapter per request, so a dead session previously never armed the fail-fast path and re-fired its alert on every poll.
- A filled Breeze order is now recognized as terminal (
EXECUTED→COMPLETE); previouslypoll_fillburned all retries on an unmapped status string. ltp()/quote()raise once the session is dead instead of silently returning a partial price dict that monitors kept trading on.- Order-book lookup failures during a Breeze retry window now raise instead of returning "not found", which previously caused a duplicate live leg to be placed.
- Fixed Breeze order-datetime parsing to accept the actual
"DD-Mon-YYYY HH:MM"shape ICICI sends (no seconds) — the recency guard was silently never applying.
[0.35.12] — 2026-08-25
Fixed
- Security: two license bypasses that needed no reverse-engineering — both were reachable on a shipped install without touching
app.exe, so the Nuitka compile never protected against either. (1)APP_ENV=dev, or a git branch created beside the binary, resolved a compiled build to dev, and dev mode reports a full Pro licence unconditionally; a compiled build now always resolves to prod and honors no overrides. (2) The trial start date lived in a local JSON file while the license key already used the OS keyring, so deleting that one file bought another 15-day trial, repeatable forever; it now lives in the keyring too, migrating existing installs without restarting their clock. The setter is additionally write-once, so a strategy file can't roll the trial window forward on every scheduled run. - Privacy: the notification module carried a developer's WhatsApp number and a
localhostBaileys URL as hardcoded constants, used whenever no webhook was configured — including from the test suite, which delivered real messages about phantom trades to that number. Both constants are gone: the webhook channel is now off until a URL is saved, matching how email and Telegram already behave.
Added
- Notifications settings take an optional Recipient for the custom webhook, sent as
toin the POST body only when set — WhatsApp bridges like Baileys need an addressee, Slack and similar endpoints reject the extra key. Documented in the setup guide.
[0.35.11] — 2026-08-25
Added
- A Dhan Data API entitlement failure (expired/missing Data API subscription — including Dhan's misleading
(810) ClientId is invalidvariant) is now surfaced in the UI: an amber site-wide banner with Dhan's raw error text, a detailed warning in the Auth page's Dhan card, and a new "How the app tells you" section in the Dhan broker docs. The warning clears automatically once Dhan accepts a data call again (or on a new token). - New user docs: order placement lifecycle and failure-recovery guide (docs/order-lifecycle.md).
[0.35.10] — 2026-08-25
Fixed
- A Dhan Data API entitlement failure (e.g. the Data API subscription lapsing, surfaced as error 810 "ClientId is invalid") was retried with a full HTTP round-trip on every LTP poll (~every 5s) with no alert — 12 days of this starved the server's worker threads and made the UI unresponsive (2026-08-25 incident). After one such failure, Dhan data-path calls now fail fast without HTTP for 10 minutes (a re-login retries immediately; Trading API calls are unaffected), and an alert/notification fires the first time it trips.
- Every per-request Dhan adapter build re-parsed the ~27MB scrip-master CSV; the parsed map is now a module-level singleton refreshed daily, and the per-request "session ready" log line is demoted to debug.
[0.35.9] — 2026-08-25
Fixed
- Critical, shipped in 0.35.7/0.35.8: the unconfirmed-close retry re-placed blind — a close order whose status was unreadable (or whose partial fill the accounting missed) could be closed a second time, opening the opposite position. The retry now settles the original order's fate first: filled → adopt the real fill, still resting → cancel and re-read, provably dead → re-place, partial → flag for manual, unreadable → wait. Capped at 10 attempts with a one-time alert.
- Critical, shipped in 0.35.7/0.35.8: the intrinsic-value clamp on close-order LIMIT pricing applied to both sides — but intrinsic is a floor on a sale, not a ceiling on a buyback (an ATM short leg's intrinsic is ~0), so a stuck diagonal SC/SP buyback was re-placed as an unfillable LIMIT at ~0 and stranded open. The bound now applies to SELL-to-close only.
poll_filllost a converted MARKET order's partial fill: a close that filled 30/65 on MARKET and 35/65 on the converted LIMIT read as falsely unconfirmed (35≠65) with the MARKET portion's price dropped. Fill quantity and value now carry across the conversion with a weighted-average price.convert_to_limit(all three brokers) placed its marketable LIMIT even when the original order had COMPLETED in the race window between the last poll and the cancel — doubling the position. The order is re-read after the cancel and kept if it filled.- A diagonal's same-tick short and long closes could both leave unconfirmed legs; the second stash overwrote the first. Stashes now merge.
- An unconfirmed close resolving after the day boundary wiped
total_pnldown to just the correction delta (the day reset zeroes_pnl_pts, which the true-up mirrored).total_pnlnow adjusts from its own prior value. _execute_rolldropped an unconfirmed close entirely (state wasn't even saved) — the next tick blindly re-closed the same leg. The pending order id is now saved and settled before any new order, and realized pts book from the real fill.- Manual square-off passed no reference price, so Kite's "no trades in this instrument" MARKET rejection had no LIMIT fallback — the button errored on exactly the illiquid legs that need it, and its conversion path had no intrinsic bound (the 2026-08-24 LP fill was reproducible through the UI). Both fixed.
- A monitor tick mid-close and a simultaneous manual square-off could both place close orders — a per-strategy close lock (shared by both paths, 10-minute stale-steal) now serializes them. The entry lock got the same stale-steal, so a crashed entry no longer wedges the day.
- Dhan's rejected-MARKET LIMIT retry used the raw reference price; it now prices off live depth like Kite's.
- Token-freshness reminder gating read the wall clock instead of the caller's, making its tests red/green with the time of day; Breeze margins and license-tools tests were stale from earlier refactors. All repaired — full suite green (1107 passed).
[0.35.8] — 2026-08-25
Fixed
BreezeAdapter.margins()readunallocated_balance(unsegmented cash outside any allocation, confirmed near-empty live), not the F&O segment this adapter actually trades. Now usesallocated_fno/block_by_trade_fno.strategy_manager.clone_strategy()didn't patchdisplay_nameinside.spec.jsonfiles — Composer reads it from the spec itself, so a cloned strategy's name reverted when reopened for editing (#443).
[0.35.7] — 2026-08-24
Fixed
- A virtually-untraded deep ITM leg's best bid/ask could itself be mispriced — confirmed live 2026-08-24: NIFTY26AUG25950PE (1 DTE) had its MARKET close rejected ("no trades in this instrument") and the depth-based LIMIT fallback crossed into a thin, mispriced bid ~80pts below the leg's own intrinsic value.
_marketable_limit_pricenow clamps toward intrinsic (a SELL never goes below it, a BUY never above), threaded throughplace_order/convert_to_limitacross Kite/Breeze/Dhan adapters andstrategies/engine.py's close/retry path. Unconfirmed closes now stash the full leg so the retry can re-place it the same way a fresh close would.
[0.35.6] — 2026-08-24
Fixed
- Desktop shell (
installer/shell.py) opened its WebView2 window in private mode by default, so broker OAuth login never saved passwords/autofill/cookies across launches. Now uses a persistent per-user profile under%LOCALAPPDATA%.
[0.35.5] — 2026-08-23
Fixed
setup.issnever declared 64-bit install mode, so Inno Setup defaulted to 32-bit and installed intoProgram Files (x86)despite ThetaPrime being a 64-bit app. AddedArchitecturesAllowed/ArchitecturesInstallIn64BitModex64, plus detection and a migration prompt for a pre-fix (x86) install.
Changed
- Install docs (
docs/installation.md, `docs/dev/installati...
ThetaPrime v0.35.10
Download ThetaPrime-0.35.10-setup.exe
Changelog
All notable changes to ThetaPrime are documented here.
Format follows Keep a Changelog.
Versioning follows Semantic Versioning.
[Unreleased]
[0.35.10] — 2026-08-25
Fixed
- A Dhan Data API entitlement failure (e.g. the Data API subscription lapsing, surfaced as error 810 "ClientId is invalid") was retried with a full HTTP round-trip on every LTP poll (~every 5s) with no alert — 12 days of this starved the server's worker threads and made the UI unresponsive (2026-08-25 incident). After one such failure, Dhan data-path calls now fail fast without HTTP for 10 minutes (a re-login retries immediately; Trading API calls are unaffected), and an alert/notification fires the first time it trips.
- Every per-request Dhan adapter build re-parsed the ~27MB scrip-master CSV; the parsed map is now a module-level singleton refreshed daily, and the per-request "session ready" log line is demoted to debug.
[0.35.9] — 2026-08-25
Fixed
- Critical, shipped in 0.35.7/0.35.8: the unconfirmed-close retry re-placed blind — a close order whose status was unreadable (or whose partial fill the accounting missed) could be closed a second time, opening the opposite position. The retry now settles the original order's fate first: filled → adopt the real fill, still resting → cancel and re-read, provably dead → re-place, partial → flag for manual, unreadable → wait. Capped at 10 attempts with a one-time alert.
- Critical, shipped in 0.35.7/0.35.8: the intrinsic-value clamp on close-order LIMIT pricing applied to both sides — but intrinsic is a floor on a sale, not a ceiling on a buyback (an ATM short leg's intrinsic is ~0), so a stuck diagonal SC/SP buyback was re-placed as an unfillable LIMIT at ~0 and stranded open. The bound now applies to SELL-to-close only.
poll_filllost a converted MARKET order's partial fill: a close that filled 30/65 on MARKET and 35/65 on the converted LIMIT read as falsely unconfirmed (35≠65) with the MARKET portion's price dropped. Fill quantity and value now carry across the conversion with a weighted-average price.convert_to_limit(all three brokers) placed its marketable LIMIT even when the original order had COMPLETED in the race window between the last poll and the cancel — doubling the position. The order is re-read after the cancel and kept if it filled.- A diagonal's same-tick short and long closes could both leave unconfirmed legs; the second stash overwrote the first. Stashes now merge.
- An unconfirmed close resolving after the day boundary wiped
total_pnldown to just the correction delta (the day reset zeroes_pnl_pts, which the true-up mirrored).total_pnlnow adjusts from its own prior value. _execute_rolldropped an unconfirmed close entirely (state wasn't even saved) — the next tick blindly re-closed the same leg. The pending order id is now saved and settled before any new order, and realized pts book from the real fill.- Manual square-off passed no reference price, so Kite's "no trades in this instrument" MARKET rejection had no LIMIT fallback — the button errored on exactly the illiquid legs that need it, and its conversion path had no intrinsic bound (the 2026-08-24 LP fill was reproducible through the UI). Both fixed.
- A monitor tick mid-close and a simultaneous manual square-off could both place close orders — a per-strategy close lock (shared by both paths, 10-minute stale-steal) now serializes them. The entry lock got the same stale-steal, so a crashed entry no longer wedges the day.
- Dhan's rejected-MARKET LIMIT retry used the raw reference price; it now prices off live depth like Kite's.
- Token-freshness reminder gating read the wall clock instead of the caller's, making its tests red/green with the time of day; Breeze margins and license-tools tests were stale from earlier refactors. All repaired — full suite green (1107 passed).
[0.35.8] — 2026-08-25
Fixed
BreezeAdapter.margins()readunallocated_balance(unsegmented cash outside any allocation, confirmed near-empty live), not the F&O segment this adapter actually trades. Now usesallocated_fno/block_by_trade_fno.strategy_manager.clone_strategy()didn't patchdisplay_nameinside.spec.jsonfiles — Composer reads it from the spec itself, so a cloned strategy's name reverted when reopened for editing (#443).
[0.35.7] — 2026-08-24
Fixed
- A virtually-untraded deep ITM leg's best bid/ask could itself be mispriced — confirmed live 2026-08-24: NIFTY26AUG25950PE (1 DTE) had its MARKET close rejected ("no trades in this instrument") and the depth-based LIMIT fallback crossed into a thin, mispriced bid ~80pts below the leg's own intrinsic value.
_marketable_limit_pricenow clamps toward intrinsic (a SELL never goes below it, a BUY never above), threaded throughplace_order/convert_to_limitacross Kite/Breeze/Dhan adapters andstrategies/engine.py's close/retry path. Unconfirmed closes now stash the full leg so the retry can re-place it the same way a fresh close would.
[0.35.6] — 2026-08-24
Fixed
- Desktop shell (
installer/shell.py) opened its WebView2 window in private mode by default, so broker OAuth login never saved passwords/autofill/cookies across launches. Now uses a persistent per-user profile under%LOCALAPPDATA%.
[0.35.5] — 2026-08-23
Fixed
setup.issnever declared 64-bit install mode, so Inno Setup defaulted to 32-bit and installed intoProgram Files (x86)despite ThetaPrime being a 64-bit app. AddedArchitecturesAllowed/ArchitecturesInstallIn64BitModex64, plus detection and a migration prompt for a pre-fix (x86) install.
Changed
- Install docs (
docs/installation.md,docs/dev/installation.md) now describe broker-agnostic prerequisites (Kite/Breeze/Dhan) instead of Kite-only, and point at the correct app log path (C:\ProgramData\ThetaPrime\logs\thetaprime.log). - Dashboard's no-token banner now reads "Broker session expired" instead of Kite-specific wording.
[0.35.4] — 2026-08-23
Changed
- Install docs (
docs/installation.md,docs/setup.md,docs/security.md,docs/dev/installation.md) rewritten for the pywebview app-window/tray-icon flow, replacing stale browser-tab instructions; documents the license request flow and the NSSM service-registration failure mode. tools/create_coupon.py,tools/generate_license.py,tools/process_license_requests.pynow share atools/_wrangler.pyd1_execute()helper instead of each callingsubprocess.run()directly.
Added
installer/build_installation_pdf.pyregenerates the installation guide PDF.
[0.35.3] — 2026-08-22
Fixed
- Installer upgrades could fail with
DeleteFile failed; code 5 (Access is denied)on a bundled.pydfile —ThetaPrimeShell.exehides to the tray on window close rather than exiting, so it's almost always still running (with its own DLLs loaded) during an upgrade, and the installer only ever stopped the NSSM service.setup.issnow alsotaskkill /F /IM ThetaPrimeShell.exe(best-effort) before file replacement.
[0.35.2] — 2026-08-22
Fixed
- Installer's icon-cache refresh used
ie4uinit.exe -ClearIconCache, which isn't reliably present on all Windows builds (CreateProcess failed; code 2on a real install). Replaced with a directSHChangeNotify(SHCNE_ASSOCCHANGED, ...)call toshell32.dllinsetup.iss's[Code]section — no external binary to go missing.
[0.35.1] — 2026-08-22
Fixed
- Desktop shell (
ThetaPrimeShell.exe) showed a stale/incorrect icon after install — icon is now embedded into the exe at Nuitka compile time instead of referenced separately by the shortcut, and the installer clears Explorer's icon cache on upgrade so a changed embedded icon isn't hidden behind a stale cached one. shell.pynow passes the window icon to pywebview explicitly instead of relying on it being extracted from the running exe.
[0.35.0] — 2026-08-22
Added
- Desktop shell (
installer/shell.py): pywebview native window wrapping the dashboard, pystray tray icon, and win11toast toast notifications, replacing the old "open dashboard in browser" flow. notifier.pydesktop channel: queues alerts for the shell to poll viaGET /api/desktop_alerts, since the notifier can run inside the Session-0 NSSM service where UI can't be shown directly.- Installer now silently installs the WebView2 runtime if missing, and launches
ThetaPrimeShell.exefrom the Start Menu / post-install instead ofopen_dashboard.bat.
[0.34.2] — 2026-08-22
Added
docs/option-scanner.md— scan modes, results columns, Flow (OI Analysis) buildup categorization, and the Ask AI handoff, cross-linked fromdocs/strategy-usage.md.
Fixed
- Option Scanner page description said "Instruments & spot always from Kite" — stale since the broker-parity work made every field (instruments/spot/OI/LTPs) come from whichever broker is selected.
- 4 markdown tables in
docs/strategy-composer.md's regime-skip filter section were rendering as raw pipe-delimited text instead of tables (missing blank line before the table, same bug class as the list-gluing fix in v0.32.13).
Changed
marketing/features.md's Option Scanner entry updated for Flow (OI Analysis), multi-broker support, and Ask AI — was still describing only the original premium%/IV/delta scan.README.mdrewritten — was still describing the pre-multi-broker prototype (wrong product name, in-memory token storage, a PyInstaller build script that no l...
ThetaPrime v0.35.8
Download ThetaPrime-0.35.8-setup.exe
Changelog
All notable changes to ThetaPrime are documented here.
Format follows Keep a Changelog.
Versioning follows Semantic Versioning.
[Unreleased]
[0.35.8] — 2026-08-25
Fixed
BreezeAdapter.margins()readunallocated_balance(unsegmented cash outside any allocation, confirmed near-empty live), not the F&O segment this adapter actually trades. Now usesallocated_fno/block_by_trade_fno.strategy_manager.clone_strategy()didn't patchdisplay_nameinside.spec.jsonfiles — Composer reads it from the spec itself, so a cloned strategy's name reverted when reopened for editing (#443).
[0.35.7] — 2026-08-24
Fixed
- A virtually-untraded deep ITM leg's best bid/ask could itself be mispriced — confirmed live 2026-08-24: NIFTY26AUG25950PE (1 DTE) had its MARKET close rejected ("no trades in this instrument") and the depth-based LIMIT fallback crossed into a thin, mispriced bid ~80pts below the leg's own intrinsic value.
_marketable_limit_pricenow clamps toward intrinsic (a SELL never goes below it, a BUY never above), threaded throughplace_order/convert_to_limitacross Kite/Breeze/Dhan adapters andstrategies/engine.py's close/retry path. Unconfirmed closes now stash the full leg so the retry can re-place it the same way a fresh close would.
[0.35.6] — 2026-08-24
Fixed
- Desktop shell (
installer/shell.py) opened its WebView2 window in private mode by default, so broker OAuth login never saved passwords/autofill/cookies across launches. Now uses a persistent per-user profile under%LOCALAPPDATA%.
[0.35.5] — 2026-08-23
Fixed
setup.issnever declared 64-bit install mode, so Inno Setup defaulted to 32-bit and installed intoProgram Files (x86)despite ThetaPrime being a 64-bit app. AddedArchitecturesAllowed/ArchitecturesInstallIn64BitModex64, plus detection and a migration prompt for a pre-fix (x86) install.
Changed
- Install docs (
docs/installation.md,docs/dev/installation.md) now describe broker-agnostic prerequisites (Kite/Breeze/Dhan) instead of Kite-only, and point at the correct app log path (C:\ProgramData\ThetaPrime\logs\thetaprime.log). - Dashboard's no-token banner now reads "Broker session expired" instead of Kite-specific wording.
[0.35.4] — 2026-08-23
Changed
- Install docs (
docs/installation.md,docs/setup.md,docs/security.md,docs/dev/installation.md) rewritten for the pywebview app-window/tray-icon flow, replacing stale browser-tab instructions; documents the license request flow and the NSSM service-registration failure mode. tools/create_coupon.py,tools/generate_license.py,tools/process_license_requests.pynow share atools/_wrangler.pyd1_execute()helper instead of each callingsubprocess.run()directly.
Added
installer/build_installation_pdf.pyregenerates the installation guide PDF.
[0.35.3] — 2026-08-22
Fixed
- Installer upgrades could fail with
DeleteFile failed; code 5 (Access is denied)on a bundled.pydfile —ThetaPrimeShell.exehides to the tray on window close rather than exiting, so it's almost always still running (with its own DLLs loaded) during an upgrade, and the installer only ever stopped the NSSM service.setup.issnow alsotaskkill /F /IM ThetaPrimeShell.exe(best-effort) before file replacement.
[0.35.2] — 2026-08-22
Fixed
- Installer's icon-cache refresh used
ie4uinit.exe -ClearIconCache, which isn't reliably present on all Windows builds (CreateProcess failed; code 2on a real install). Replaced with a directSHChangeNotify(SHCNE_ASSOCCHANGED, ...)call toshell32.dllinsetup.iss's[Code]section — no external binary to go missing.
[0.35.1] — 2026-08-22
Fixed
- Desktop shell (
ThetaPrimeShell.exe) showed a stale/incorrect icon after install — icon is now embedded into the exe at Nuitka compile time instead of referenced separately by the shortcut, and the installer clears Explorer's icon cache on upgrade so a changed embedded icon isn't hidden behind a stale cached one. shell.pynow passes the window icon to pywebview explicitly instead of relying on it being extracted from the running exe.
[0.35.0] — 2026-08-22
Added
- Desktop shell (
installer/shell.py): pywebview native window wrapping the dashboard, pystray tray icon, and win11toast toast notifications, replacing the old "open dashboard in browser" flow. notifier.pydesktop channel: queues alerts for the shell to poll viaGET /api/desktop_alerts, since the notifier can run inside the Session-0 NSSM service where UI can't be shown directly.- Installer now silently installs the WebView2 runtime if missing, and launches
ThetaPrimeShell.exefrom the Start Menu / post-install instead ofopen_dashboard.bat.
[0.34.2] — 2026-08-22
Added
docs/option-scanner.md— scan modes, results columns, Flow (OI Analysis) buildup categorization, and the Ask AI handoff, cross-linked fromdocs/strategy-usage.md.
Fixed
- Option Scanner page description said "Instruments & spot always from Kite" — stale since the broker-parity work made every field (instruments/spot/OI/LTPs) come from whichever broker is selected.
- 4 markdown tables in
docs/strategy-composer.md's regime-skip filter section were rendering as raw pipe-delimited text instead of tables (missing blank line before the table, same bug class as the list-gluing fix in v0.32.13).
Changed
marketing/features.md's Option Scanner entry updated for Flow (OI Analysis), multi-broker support, and Ask AI — was still describing only the original premium%/IV/delta scan.README.mdrewritten — was still describing the pre-multi-broker prototype (wrong product name, in-memory token storage, a PyInstaller build script that no longer exists).
[0.34.1] — 2026-08-22
Added
tests/test_broker_conformance.py— structural conformance suite that checks every broker adapter (Kite, Breeze, Dhan) implements the fullBrokerClientcontract and constants.docs/brokers/adding-a-new-broker.md— checklist for wiring in a 4th broker adapter.
Changed
- Broker adapters (Kite/Breeze/Dhan) and
scanner.py/app.pybrought into parity per the new conformance checklist.
[0.34.0] — 2026-08-21
Added
- Option Scanner: OI-buildup / unusual-volume / PCR flow analysis (Long/Short Buildup, Short Covering, Long Unwinding) per strike, plus an OI column on the plain scan. Previous-day OI baseline is fetched once per symbol per day and cached to
oi_baseline.json. - Option Scanner results have an "Ask AI" handoff — reviews the scan CSV for notable strikes, mispricings, or unusual activity via the configured AI provider.
option_scannerfeature flag now defaults on.- New
strategies/reliance_cash_secured_putstrategy (spec + generated .py).
Changed
- AI block-catalog prompt trimmed to type/param essentials and compacted to non-pretty-printed JSON — was hitting Groq's free-tier 8000 TPM limit and leaving no room for the auto-correction retry.
- API error responses no longer leak raw exception text/paths to the browser — routed through a shared handler that logs the full exception server-side with a correlation id and returns a short generic message instead.
quote()now returns open interest (oi) across all three broker adapters (Kite passthrough, Breeze/Dhan unverified live).
Fixed
- Live-candle instrument-token lookups are now cached per symbol instead of re-resolved on every call.
[0.33.2] — 2026-08-19
Added
- Trade journal CSV (
lib_v1_paper_trade_journal.csv) is now encrypted at rest and in backups, same path asrun_history.db/performance.db(issue #370). - Cheap RSS/thread-count snapshot logged every reconcile cycle, piggybacked on the existing broker round-trip — no documented CPU/RAM footprint for the always-on service previously (issue #375).
- Installer's Finished page now checks whether the NSSM Windows service actually registered instead of assuming success, and reports clearly if it didn't.
Changed
docs/setup.mddocuments the broker read-call retry/backoff behavior (2 retries, 0.5s/1.5s delay, 5 consecutive failures raise a "broker degraded" alert).
[0.33.1] — 2026-08-19
Added
- Build with AI in the Strategy Composer is now an interactive in-app chat when an AI provider is configured (Settings → AI): every reply is validated server-side before it reaches the UI, with one bounded auto-correction retry on an invalid spec. Falls back to the original copy/paste handoff when no provider is configured. Bring-your-own-key via any OpenAI-compatible endpoint (Groq default, Ollama for local/no-key use, OpenAI, or custom).
Fixed
- "Build with AI" occasionally producing an invalid spec with no feedback — replies are now run through the composer's own validator instead of trusted as-is.
- The AI prompt's block catalog dump cut from ~7960 to ~2960 tokens (dropped UI-only description/label text, compacted JSON) — was causing 413s and leaving no headroom for a retry within free-tier per-minute token limits.
[0.33.0] — 2026-08-19
Added
- Manual takeover:
square_off()accepts an optionallegs=[...]param to close a single position and leave the rest running (real order, not just a record). A strategy that's had a leg closed this way setsmanual_control— autopilot (entries, exits, adjustments, reentry) leaves it alone until every leg is closed, then resumes on its own. Strategy card and trade-...
ThetaPrime v0.35.7
Download ThetaPrime-0.35.7-setup.exe
Changelog
All notable changes to ThetaPrime are documented here.
Format follows Keep a Changelog.
Versioning follows Semantic Versioning.
[Unreleased]
[0.35.7] — 2026-08-24
Fixed
- A virtually-untraded deep ITM leg's best bid/ask could itself be mispriced — confirmed live 2026-08-24: NIFTY26AUG25950PE (1 DTE) had its MARKET close rejected ("no trades in this instrument") and the depth-based LIMIT fallback crossed into a thin, mispriced bid ~80pts below the leg's own intrinsic value.
_marketable_limit_pricenow clamps toward intrinsic (a SELL never goes below it, a BUY never above), threaded throughplace_order/convert_to_limitacross Kite/Breeze/Dhan adapters andstrategies/engine.py's close/retry path. Unconfirmed closes now stash the full leg so the retry can re-place it the same way a fresh close would.
[0.35.6] — 2026-08-24
Fixed
- Desktop shell (
installer/shell.py) opened its WebView2 window in private mode by default, so broker OAuth login never saved passwords/autofill/cookies across launches. Now uses a persistent per-user profile under%LOCALAPPDATA%.
[0.35.5] — 2026-08-23
Fixed
setup.issnever declared 64-bit install mode, so Inno Setup defaulted to 32-bit and installed intoProgram Files (x86)despite ThetaPrime being a 64-bit app. AddedArchitecturesAllowed/ArchitecturesInstallIn64BitModex64, plus detection and a migration prompt for a pre-fix (x86) install.
Changed
- Install docs (
docs/installation.md,docs/dev/installation.md) now describe broker-agnostic prerequisites (Kite/Breeze/Dhan) instead of Kite-only, and point at the correct app log path (C:\ProgramData\ThetaPrime\logs\thetaprime.log). - Dashboard's no-token banner now reads "Broker session expired" instead of Kite-specific wording.
[0.35.4] — 2026-08-23
Changed
- Install docs (
docs/installation.md,docs/setup.md,docs/security.md,docs/dev/installation.md) rewritten for the pywebview app-window/tray-icon flow, replacing stale browser-tab instructions; documents the license request flow and the NSSM service-registration failure mode. tools/create_coupon.py,tools/generate_license.py,tools/process_license_requests.pynow share atools/_wrangler.pyd1_execute()helper instead of each callingsubprocess.run()directly.
Added
installer/build_installation_pdf.pyregenerates the installation guide PDF.
[0.35.3] — 2026-08-22
Fixed
- Installer upgrades could fail with
DeleteFile failed; code 5 (Access is denied)on a bundled.pydfile —ThetaPrimeShell.exehides to the tray on window close rather than exiting, so it's almost always still running (with its own DLLs loaded) during an upgrade, and the installer only ever stopped the NSSM service.setup.issnow alsotaskkill /F /IM ThetaPrimeShell.exe(best-effort) before file replacement.
[0.35.2] — 2026-08-22
Fixed
- Installer's icon-cache refresh used
ie4uinit.exe -ClearIconCache, which isn't reliably present on all Windows builds (CreateProcess failed; code 2on a real install). Replaced with a directSHChangeNotify(SHCNE_ASSOCCHANGED, ...)call toshell32.dllinsetup.iss's[Code]section — no external binary to go missing.
[0.35.1] — 2026-08-22
Fixed
- Desktop shell (
ThetaPrimeShell.exe) showed a stale/incorrect icon after install — icon is now embedded into the exe at Nuitka compile time instead of referenced separately by the shortcut, and the installer clears Explorer's icon cache on upgrade so a changed embedded icon isn't hidden behind a stale cached one. shell.pynow passes the window icon to pywebview explicitly instead of relying on it being extracted from the running exe.
[0.35.0] — 2026-08-22
Added
- Desktop shell (
installer/shell.py): pywebview native window wrapping the dashboard, pystray tray icon, and win11toast toast notifications, replacing the old "open dashboard in browser" flow. notifier.pydesktop channel: queues alerts for the shell to poll viaGET /api/desktop_alerts, since the notifier can run inside the Session-0 NSSM service where UI can't be shown directly.- Installer now silently installs the WebView2 runtime if missing, and launches
ThetaPrimeShell.exefrom the Start Menu / post-install instead ofopen_dashboard.bat.
[0.34.2] — 2026-08-22
Added
docs/option-scanner.md— scan modes, results columns, Flow (OI Analysis) buildup categorization, and the Ask AI handoff, cross-linked fromdocs/strategy-usage.md.
Fixed
- Option Scanner page description said "Instruments & spot always from Kite" — stale since the broker-parity work made every field (instruments/spot/OI/LTPs) come from whichever broker is selected.
- 4 markdown tables in
docs/strategy-composer.md's regime-skip filter section were rendering as raw pipe-delimited text instead of tables (missing blank line before the table, same bug class as the list-gluing fix in v0.32.13).
Changed
marketing/features.md's Option Scanner entry updated for Flow (OI Analysis), multi-broker support, and Ask AI — was still describing only the original premium%/IV/delta scan.README.mdrewritten — was still describing the pre-multi-broker prototype (wrong product name, in-memory token storage, a PyInstaller build script that no longer exists).
[0.34.1] — 2026-08-22
Added
tests/test_broker_conformance.py— structural conformance suite that checks every broker adapter (Kite, Breeze, Dhan) implements the fullBrokerClientcontract and constants.docs/brokers/adding-a-new-broker.md— checklist for wiring in a 4th broker adapter.
Changed
- Broker adapters (Kite/Breeze/Dhan) and
scanner.py/app.pybrought into parity per the new conformance checklist.
[0.34.0] — 2026-08-21
Added
- Option Scanner: OI-buildup / unusual-volume / PCR flow analysis (Long/Short Buildup, Short Covering, Long Unwinding) per strike, plus an OI column on the plain scan. Previous-day OI baseline is fetched once per symbol per day and cached to
oi_baseline.json. - Option Scanner results have an "Ask AI" handoff — reviews the scan CSV for notable strikes, mispricings, or unusual activity via the configured AI provider.
option_scannerfeature flag now defaults on.- New
strategies/reliance_cash_secured_putstrategy (spec + generated .py).
Changed
- AI block-catalog prompt trimmed to type/param essentials and compacted to non-pretty-printed JSON — was hitting Groq's free-tier 8000 TPM limit and leaving no room for the auto-correction retry.
- API error responses no longer leak raw exception text/paths to the browser — routed through a shared handler that logs the full exception server-side with a correlation id and returns a short generic message instead.
quote()now returns open interest (oi) across all three broker adapters (Kite passthrough, Breeze/Dhan unverified live).
Fixed
- Live-candle instrument-token lookups are now cached per symbol instead of re-resolved on every call.
[0.33.2] — 2026-08-19
Added
- Trade journal CSV (
lib_v1_paper_trade_journal.csv) is now encrypted at rest and in backups, same path asrun_history.db/performance.db(issue #370). - Cheap RSS/thread-count snapshot logged every reconcile cycle, piggybacked on the existing broker round-trip — no documented CPU/RAM footprint for the always-on service previously (issue #375).
- Installer's Finished page now checks whether the NSSM Windows service actually registered instead of assuming success, and reports clearly if it didn't.
Changed
docs/setup.mddocuments the broker read-call retry/backoff behavior (2 retries, 0.5s/1.5s delay, 5 consecutive failures raise a "broker degraded" alert).
[0.33.1] — 2026-08-19
Added
- Build with AI in the Strategy Composer is now an interactive in-app chat when an AI provider is configured (Settings → AI): every reply is validated server-side before it reaches the UI, with one bounded auto-correction retry on an invalid spec. Falls back to the original copy/paste handoff when no provider is configured. Bring-your-own-key via any OpenAI-compatible endpoint (Groq default, Ollama for local/no-key use, OpenAI, or custom).
Fixed
- "Build with AI" occasionally producing an invalid spec with no feedback — replies are now run through the composer's own validator instead of trusted as-is.
- The AI prompt's block catalog dump cut from ~7960 to ~2960 tokens (dropped UI-only description/label text, compacted JSON) — was causing 413s and leaving no headroom for a retry within free-tier per-minute token limits.
[0.33.0] — 2026-08-19
Added
- Manual takeover:
square_off()accepts an optionallegs=[...]param to close a single position and leave the rest running (real order, not just a record). A strategy that's had a leg closed this way setsmanual_control— autopilot (entries, exits, adjustments, reentry) leaves it alone until every leg is closed, then resumes on its own. Strategy card and trade-status modal show a ✋ MANUAL badge; the manual-exit form gets a per-leg "Close Now" button.
Fixed
- Token-freshness nudges and broker session-dead notifications (Kite/Breeze/Dhan) no longer fire outside trading hours — gated to market hours ± 2h via new
holidays.in_reminder_window().
[0.32.17] — 2026-08-18
Added
- "External Data Sources" card on the Data Management page: last-updated status for the cached NFO instrument snapshot, NSE holiday cale...
ThetaPrime v0.35.4
Download ThetaPrime-0.35.4-setup.exe
Changelog
All notable changes to ThetaPrime are documented here.
Format follows Keep a Changelog.
Versioning follows Semantic Versioning.
[Unreleased]
[0.35.4] — 2026-08-23
Changed
- Install docs (
docs/installation.md,docs/setup.md,docs/security.md,docs/dev/installation.md) rewritten for the pywebview app-window/tray-icon flow, replacing stale browser-tab instructions; documents the license request flow and the NSSM service-registration failure mode. tools/create_coupon.py,tools/generate_license.py,tools/process_license_requests.pynow share atools/_wrangler.pyd1_execute()helper instead of each callingsubprocess.run()directly.
Added
installer/build_installation_pdf.pyregenerates the installation guide PDF.
[0.35.3] — 2026-08-22
Fixed
- Installer upgrades could fail with
DeleteFile failed; code 5 (Access is denied)on a bundled.pydfile —ThetaPrimeShell.exehides to the tray on window close rather than exiting, so it's almost always still running (with its own DLLs loaded) during an upgrade, and the installer only ever stopped the NSSM service.setup.issnow alsotaskkill /F /IM ThetaPrimeShell.exe(best-effort) before file replacement.
[0.35.2] — 2026-08-22
Fixed
- Installer's icon-cache refresh used
ie4uinit.exe -ClearIconCache, which isn't reliably present on all Windows builds (CreateProcess failed; code 2on a real install). Replaced with a directSHChangeNotify(SHCNE_ASSOCCHANGED, ...)call toshell32.dllinsetup.iss's[Code]section — no external binary to go missing.
[0.35.1] — 2026-08-22
Fixed
- Desktop shell (
ThetaPrimeShell.exe) showed a stale/incorrect icon after install — icon is now embedded into the exe at Nuitka compile time instead of referenced separately by the shortcut, and the installer clears Explorer's icon cache on upgrade so a changed embedded icon isn't hidden behind a stale cached one. shell.pynow passes the window icon to pywebview explicitly instead of relying on it being extracted from the running exe.
[0.35.0] — 2026-08-22
Added
- Desktop shell (
installer/shell.py): pywebview native window wrapping the dashboard, pystray tray icon, and win11toast toast notifications, replacing the old "open dashboard in browser" flow. notifier.pydesktop channel: queues alerts for the shell to poll viaGET /api/desktop_alerts, since the notifier can run inside the Session-0 NSSM service where UI can't be shown directly.- Installer now silently installs the WebView2 runtime if missing, and launches
ThetaPrimeShell.exefrom the Start Menu / post-install instead ofopen_dashboard.bat.
[0.34.2] — 2026-08-22
Added
docs/option-scanner.md— scan modes, results columns, Flow (OI Analysis) buildup categorization, and the Ask AI handoff, cross-linked fromdocs/strategy-usage.md.
Fixed
- Option Scanner page description said "Instruments & spot always from Kite" — stale since the broker-parity work made every field (instruments/spot/OI/LTPs) come from whichever broker is selected.
- 4 markdown tables in
docs/strategy-composer.md's regime-skip filter section were rendering as raw pipe-delimited text instead of tables (missing blank line before the table, same bug class as the list-gluing fix in v0.32.13).
Changed
marketing/features.md's Option Scanner entry updated for Flow (OI Analysis), multi-broker support, and Ask AI — was still describing only the original premium%/IV/delta scan.README.mdrewritten — was still describing the pre-multi-broker prototype (wrong product name, in-memory token storage, a PyInstaller build script that no longer exists).
[0.34.1] — 2026-08-22
Added
tests/test_broker_conformance.py— structural conformance suite that checks every broker adapter (Kite, Breeze, Dhan) implements the fullBrokerClientcontract and constants.docs/brokers/adding-a-new-broker.md— checklist for wiring in a 4th broker adapter.
Changed
- Broker adapters (Kite/Breeze/Dhan) and
scanner.py/app.pybrought into parity per the new conformance checklist.
[0.34.0] — 2026-08-21
Added
- Option Scanner: OI-buildup / unusual-volume / PCR flow analysis (Long/Short Buildup, Short Covering, Long Unwinding) per strike, plus an OI column on the plain scan. Previous-day OI baseline is fetched once per symbol per day and cached to
oi_baseline.json. - Option Scanner results have an "Ask AI" handoff — reviews the scan CSV for notable strikes, mispricings, or unusual activity via the configured AI provider.
option_scannerfeature flag now defaults on.- New
strategies/reliance_cash_secured_putstrategy (spec + generated .py).
Changed
- AI block-catalog prompt trimmed to type/param essentials and compacted to non-pretty-printed JSON — was hitting Groq's free-tier 8000 TPM limit and leaving no room for the auto-correction retry.
- API error responses no longer leak raw exception text/paths to the browser — routed through a shared handler that logs the full exception server-side with a correlation id and returns a short generic message instead.
quote()now returns open interest (oi) across all three broker adapters (Kite passthrough, Breeze/Dhan unverified live).
Fixed
- Live-candle instrument-token lookups are now cached per symbol instead of re-resolved on every call.
[0.33.2] — 2026-08-19
Added
- Trade journal CSV (
lib_v1_paper_trade_journal.csv) is now encrypted at rest and in backups, same path asrun_history.db/performance.db(issue #370). - Cheap RSS/thread-count snapshot logged every reconcile cycle, piggybacked on the existing broker round-trip — no documented CPU/RAM footprint for the always-on service previously (issue #375).
- Installer's Finished page now checks whether the NSSM Windows service actually registered instead of assuming success, and reports clearly if it didn't.
Changed
docs/setup.mddocuments the broker read-call retry/backoff behavior (2 retries, 0.5s/1.5s delay, 5 consecutive failures raise a "broker degraded" alert).
[0.33.1] — 2026-08-19
Added
- Build with AI in the Strategy Composer is now an interactive in-app chat when an AI provider is configured (Settings → AI): every reply is validated server-side before it reaches the UI, with one bounded auto-correction retry on an invalid spec. Falls back to the original copy/paste handoff when no provider is configured. Bring-your-own-key via any OpenAI-compatible endpoint (Groq default, Ollama for local/no-key use, OpenAI, or custom).
Fixed
- "Build with AI" occasionally producing an invalid spec with no feedback — replies are now run through the composer's own validator instead of trusted as-is.
- The AI prompt's block catalog dump cut from ~7960 to ~2960 tokens (dropped UI-only description/label text, compacted JSON) — was causing 413s and leaving no headroom for a retry within free-tier per-minute token limits.
[0.33.0] — 2026-08-19
Added
- Manual takeover:
square_off()accepts an optionallegs=[...]param to close a single position and leave the rest running (real order, not just a record). A strategy that's had a leg closed this way setsmanual_control— autopilot (entries, exits, adjustments, reentry) leaves it alone until every leg is closed, then resumes on its own. Strategy card and trade-status modal show a ✋ MANUAL badge; the manual-exit form gets a per-leg "Close Now" button.
Fixed
- Token-freshness nudges and broker session-dead notifications (Kite/Breeze/Dhan) no longer fire outside trading hours — gated to market hours ± 2h via new
holidays.in_reminder_window().
[0.32.17] — 2026-08-18
Added
- "External Data Sources" card on the Data Management page: last-updated status for the cached NFO instrument snapshot, NSE holiday calendar, and Yahoo-derived regime calibration. New
docs/data-sources.mddocuments each source. tools/replay_session.py: replays a historical volatile trading session through the real scheduler/monitor loop in paper mode with an optional broker-throttling burst injected, to verify ticks keep pace (issue #368).- Entry fills now warn (never abort) when a live fill lands more than 5% from its pre-trade reference price (
STRATEGY_MAX_SLIPPAGE_PCT).
Fixed
- A close order that never confirms filled is no longer silently dropped: the leg is flagged
_exit_unconfirmed, blocks new entries/re-entries until a human confirms via Mark Exited, is picked up immediately by reconciliation, and the trade notification now says "may still be open at the broker" instead of a plain ✅. - A disabled strategy with an open position now keeps running its monitor tick (PT/SL/EOD exits still fire) — only new entries are blocked, not the exits that protect an existing position.
run_entry()now takes an exclusive file lock so a cron tick and a manual/retry trigger landing in the same window can no longer both place orders.state.jsonwrites go through tmp+rename for atomicity, avoiding a truncated read by a concurrent dashboard poll.- Entry-rejection notifications report the actual per-leg broker status instead of a hardcoded "(margin?)" guess.
[0.32.16] — 2026-08-18
Added
- Custom 404/500 error pages (
templates/error.html); API routes now return a generic JSON error with a logged correlation id instead of leaking raw exception text/paths (_safe_error_message/_api_error, issue #366). - Live automation-latency metric:
SpecEnginetimes signa...