Security
ThetaPrime is designed for personal use on your own computer. These protections keep your broker credentials safe and guard against accidental live trades.
Dashboard Password
The dashboard is protected by a password — no one can view your positions or trigger trades without it.
Setting it up: The first time you open the app, you'll be asked to create a password. You only do this once; the password is saved securely and remembered across restarts.
Locked out? After 3 wrong attempts, logins from that device are blocked for 5 minutes. The same lockout applies to the current-password check on Change Password and Regenerate Recovery Code below.
Logging out: Click the ⏻ button in the top-right corner of any page.
Changing your password: Go to the Settings page and find the Change Dashboard Password card. Enter your current password and a new one (minimum 8 characters), then click Change.
Password Recovery
If you forget your dashboard password, there is no "Forgot Password" link. Instead, you reset access by deleting the stored password from Windows — the app then treats it as a fresh install and lets you set a new one.
Steps:
- Click the Windows Start button and search for Credential Manager — open it.
- Click Windows Credentials (the second tab at the top).
- Scroll down and find the entry named
kite_dashboard. - Click on it to expand it, then click Remove.
- Open ThetaPrime. You will see the Set Dashboard Password screen — choose a new password and you're in.
What is NOT affected: Your broker API keys, all trade history, P&L records, strategy settings, and logs are completely untouched. Only the dashboard password is reset.
What Is Protected and What Is Not
Understanding this helps you know where your data is safe and where it isn't.
| What | Where it's stored | Protected? |
|---|---|---|
| Dashboard password | Windows Credential Manager | Yes — stored as a one-way fingerprint; even ThetaPrime cannot read it back |
| Broker API keys & tokens | Windows Credential Manager | Yes — copying ThetaPrime's files to another machine won't expose these |
| Trade history & P&L | Database files on your hard drive (run_history.db, performance.db) |
Yes — encrypted at rest (AES-256-GCM); see Trade Data Encryption below |
| Trade journal | CSV file (strategies/lib_v1_paper_trade_journal.csv) |
Yes — same AES-256-GCM encryption at rest as trade history above |
| App event log | app_events table inside performance.db |
Yes — it's not a separate file, so it's covered by performance.db's encryption above |
| Scheduler job store | jobs.sqlite |
No — holds only job names/next-run times for the scheduler, not credentials or trade data; intentionally left out as lower-sensitivity |
What this means in practice:
- The dashboard password is a web UI gate — it stops someone using a browser to access ThetaPrime. It's a separate lock from the encryption below.
- Trade history, P&L, and the trade journal CSV are encrypted after a clean shutdown/restart — copying these files off this machine at that point gets you unreadable bytes, not a SQLite/CSV file you can open in a free tool.
- While ThetaPrime is running, those files are held decrypted (needed to trade) — same "protected while off, not while on" model Windows itself uses for a locked screen vs. a running session. A hard power-off or forced kill skips the shutdown re-encryption step too, leaving the files plaintext until the next clean stop; see the Known Gap note under Trade Data Encryption.
- A downloaded backup zip (Settings page → Backup & Restore) always contains these files encrypted, regardless of whether the app happened to be mid-session when the backup ran. The key that decrypts them never travels with the zip, so a copy of just the backup can't unlock it.
Trade Data Encryption
run_history.db, performance.db, and the trade journal CSV (lib_v1_paper_trade_journal.csv)
are encrypted after ThetaPrime stops running. The encryption key isn't tied to your dashboard
password — it's held in the same Windows Credential Manager vault as your broker tokens, so
scheduled/automated trading keeps working unattended after a reboot without anyone needing to log
in first.
Known gap: re-encryption runs when ThetaPrime shuts down cleanly (stopping the service, or a normal exit). A hard power-off or a forced kill (Task Manager "End task", a crash) skips that step, leaving these files plaintext on disk until the next clean stop. Nothing is lost or corrupted by this — it's a window where a copy of the files would be readable, not a functional problem — and it self-heals the next time ThetaPrime stops cleanly. This is a known, accepted limitation for a local single-user tool; the BitLocker recommendation below covers this gap at the Windows level.
Recovery code: the first time this feature runs (a fresh install, or your first restart after updating to a version that has it), the Settings page shows a one-time recovery code. Save it somewhere safe — it's the only way to recover your trade data if this computer's Credential Manager is ever lost or corrupted (e.g. a Windows reinstall, or migrating to a new PC). ThetaPrime cannot show it to you again after you dismiss it, but you can generate a fresh one any time from the same Data Encryption card on the Settings page — doing so invalidates the old code.
Recommended: turn on BitLocker (or Device Encryption). ThetaPrime's encryption protects
run_history.db/performance.db only while the app isn't running — while it's actively trading,
those files are held decrypted on disk, the same way any running program needs its data unlocked to
use it. That gap is closed at the Windows level, not the app level: turning on BitLocker
(Windows 10/11 Pro) or Device Encryption (the built-in equivalent on Home editions with
supported hardware — check Settings → Privacy & Security → Device encryption) encrypts your
entire hard drive. If the machine is off, locked, or the drive is removed and read on another
computer, everything on it — including a plaintext run_history.db mid-session — is unreadable
without your Windows login. This is on top of ThetaPrime's own encryption, not instead of it: it
covers "computer off or stolen," ThetaPrime's covers "a copy of just these files." Neither one, nor
any other app, can protect your data from someone who is already logged into this Windows account
while ThetaPrime is running — at that point the app needs the same access to your files that they'd
have.
Your Credentials Are Stored Securely
Your broker API keys and access tokens are saved in your operating system's secure credential store (the same place Windows stores your saved passwords). They are never written to a plain text file unless the secure store is unavailable, in which case a warning banner appears on the Settings page.
Expired Login Prevents Live Trading
If your daily broker login has expired, ThetaPrime will refuse to run any live (real-money) strategy. You'll see a clear error message asking you to re-authenticate first. Paper trading and backtests are not affected — they run regardless.
No Double Entry
If a strategy already has an open trade, ThetaPrime will not let it place another entry. This prevents accidental duplicate positions if the same strategy is triggered more than once.
Maximum Lots Limit
There is a hard cap on the number of lots any strategy can trade. Even if a strategy is misconfigured with a higher number, it will be automatically reduced to the cap before the order is placed, and a warning will appear in the Events log.
To view or change the limit, go to the Settings page and find the Safety Settings card. Enter a new number and click Save. The change takes effect on the next strategy run — no restart needed.
Switching a Strategy to Real Mode
Switching a strategy from Paper to Real is a two-step process so it can never happen by accident:
- Click the Real option in a strategy's mode menu. A confirmation dialog appears showing that strategy's P&L for today.
- Confirm the dialog to actually switch the mode.
Arming: Even after a strategy is set to Real, it still needs to be armed before it will place live orders — a strategy card shows an amber 🔒 Real badge with an Arm button. Click Arm to turn the badge red (🔓 Armed) and enable live orders.
The armed state is saved and survives a restart — an armed Real strategy stays armed after ThetaPrime restarts, so it resumes placing live orders without you needing to arm it again. If a strategy is switched to Real but never armed, it keeps running as paper (and you'll get one notification per day telling you so) until you arm it.
Daily Loss Limit
You can set a hard stop-loss for the whole day across all your real-money strategies combined.
Setting it up: Go to the Settings page, find the Safety Settings card, and enter a threshold in points and/or rupees (e.g. -200 pts or -10000 ₹). Leave a field blank to disable that check. You can set one or both — whichever is hit first triggers the stop.
What happens when it's hit: As soon as your combined real-mode P&L for the day falls to or below the threshold:
- Every strategy running in Real mode is disabled and disarmed.
- You'll get a notification.
- A banner appears on the dashboard, and the day's P&L chip next to Strategies turns red.
- Real mode is blocked for the rest of the day — you cannot switch a strategy to Real or arm one again until the next calendar day, even across a restart.
Paper trading is never affected — it keeps running normally.
Portfolio Risk Dashboard
The dashboard's Strategies header shows a risk badge next to the daily P&L chip, summarizing live exposure across every open real-mode position: net Delta/Gamma/Theta/Vega (summed across every open leg of every strategy, hover the badge for the full breakdown) and live unrealized MTM P&L. Paper-mode positions carry no real exposure and are excluded. Margin used/available (per broker) is folded into the same underlying data, reusing the Settings page's margin check.
Threshold alerts: in the Safety Settings card, below Daily Loss Limit, you can set:
- Portfolio Delta alert threshold — notify when net Delta's magnitude crosses this.
- Live MTM drawdown alert (₹) — notify when live (not yet closed) unrealized P&L drops to or below this.
- Underlying % move alert — notify when an underlying has moved this much (either direction) from its previous close.
- Alert when a short leg goes ITM — notify when any SELL leg's strike has been crossed by the underlying.
Unlike the Daily Loss Limit, these never disable a strategy or block trading — they're informational only, delivered through the same notification channels as everything else (email/webhook/Telegram/desktop). Each rule re-notifies at most once every 15 minutes while still breached, rather than firing on every check. Checked every 5 minutes in the background (so you're notified even with the dashboard closed) and every time the dashboard badge refreshes. Leave a field blank (or the switch off) to disable that check.
Orphan Position Detection
If a crash, manual square-off at the broker, or a missed exit ever leaves your broker holdings and ThetaPrime's records out of sync, ThetaPrime checks for it automatically — at startup, and then periodically in the background for as long as the server runs.
How it works: Every time the server starts, and every 5 minutes after that (while any strategy has an open trade recorded), ThetaPrime compares its real-mode trade state files against the actual open positions reported by your broker — Kite, Breeze, or Dhan. Paper-mode trades are never sent to the broker, so they're excluded from the comparison entirely — an active paper strategy is expected, not a mismatch.
What it flags:
- Broker-only — the broker shows an open position that no real-mode strategy's state file accounts for.
- State file active, broker empty — a real-mode strategy's state file says a trade is open, but the broker shows nothing (or the wrong side).
Where you see it: A small Reconcile badge sits next to the Strategies heading on the dashboard — gray "not checked", green "clean", or red "N mismatch". On a mismatch, the affected symbols appear directly beneath the badge, split into broker-only vs state-only. Click the badge any time to force an immediate check. It also refreshes automatically every minute, so it reflects the periodic background check even if you never click it. Hover the badge to see, per broker, when its last login passed the API Contract Check — a stale date means that broker hasn't had a verified login recently.
What happens when a mismatch is found: The badge turns red and you get a notification. Nothing is closed automatically — ThetaPrime only surfaces the mismatch; you decide what to do via your broker's own terminal. Click the badge again once you've squared things up — it turns green if everything now matches.
State file active, broker empty — reconciling ThetaPrime's own records: If you closed a position directly at the broker (e.g. a missed automated exit), the broker side is already correct, but ThetaPrime's trade state still shows it open. Open that strategy's Trade Status modal (dashboard → Details) and click Mark Exited (Manual). It shows one row per open leg with an inline exit-price field — no more one-at-a-time prompts — and a Validate vs Broker button that checks each symbol is still tradable and flags a price that's far from the live LTP before you confirm. Recording the exit never places an order, so it's safe to use after the fact. This is different from Sq Off, which places a real closing order and should only be used when the position is still genuinely open at the broker.
Only some legs closed at the broker (e.g. a diagonal's short side): You don't have to close everything at once. Leave the price field blank for any leg that's still genuinely open — only the legs you fill in get marked exited, the rest are left untouched. For example, if you closed just the short SC/SP legs of a diagonal by hand and the long LC/LP legs are still live at the broker:
- Open Trade Status → Mark Exited (Manual).
- Fill in SC and SP with the prices you actually got at the broker (from your order book, not the app's live LTP column).
- Leave LC and LP blank.
- Optionally click Validate vs Broker to sanity-check the SC/SP symbols and prices before confirming.
- Click Confirm Mark Exited. The confirmation dialog lists only the legs you priced, and notes that the rest stay open.
The trade moves to SHORT_CLOSED instead of CLOSED — exactly the state it would be in if the engine's own short-leg exit had fired — and ThetaPrime keeps monitoring LC/LP normally (EOD handling, exits, etc.) from the next scheduled run. Nothing needs editing by hand.
Broker-only — a missed entry you placed by hand: If automation failed to fire an entry and you placed the legs directly at the broker instead, ThetaPrime's state file has no record of it and will flag it as broker-only. Open the strategy's Trade Status modal and click Manual Entry. Fill in each leg you actually placed (symbol, BUY/SELL, fill price) — use + Add leg for anything beyond the four default rows — plus the expiry and lot details, then optionally hit Validate vs Broker to confirm each symbol and price before saving. This only backfills the state file, it never places an order, so it's safe once the position is already real at the broker. Once saved, the position shows as ACTIVE and is monitored (exits, adjustments, EOD handling) exactly like a normal automated entry from the next scheduled run onward.
Fixing a mistake after the fact: If a manual entry/exit (or an automated one) was recorded with a wrong price, symbol, or strike, click Edit Trade State in the same modal. It lists every leg — open and closed — with all fields editable, a Validate vs Broker check, and a confirmation dialog that spells out exactly what's changing before it saves. It only corrects bookkeeping (it can't open or close a leg — use Manual Entry / Mark Exited for that), and every save is appended to the state file's edit_log so a correction is always traceable.
Turning it off: The periodic background check is a Safety Settings toggle on the Settings page ("Automatic orphan-position reconciliation"), on by default. Turning it off stops the every-5-minutes background check and the startup check; the dashboard badge still works on demand regardless.
This check requires a fresh broker login; it's skipped (not flagged as an error) if your token has expired.
AI assistant write access: A separate Safety Settings toggle ("Allow AI assistant (MCP) to arm/square off strategies"), off by default, controls whether an AI assistant connected via MCP can arm a strategy or square off a position — see docs/mcp-setup.md.
Entry time clustering warning
Each broker has a per-day API call quota. If several enabled strategies on the same broker enter within a couple minutes of each other, they can burst past that quota faster than the same strategies spread across the day — this is what fills the Events log with skipped-quote warnings on a heavy day.
Where you see it: When you save a strategy's entry time, or enable a strategy, ThetaPrime checks whether any other enabled strategy on the same broker enters within 2 minutes. If so, a warning toast names the clustering strategies. It's advisory only — it never blocks the save or the enable, and it doesn't stagger anything for you; you decide whether to move an entry time.
Turning it off: A Safety Settings toggle on the Settings page ("Warn when strategy entry times cluster"), on by default.
Worked example: a real fill misread as "rejected," then Reset Live State makes it worse
This walks through an actual incident end to end — a diagonal's long legs (LC/LP) genuinely filled at the broker, but ThetaPrime's own fill check misread them as rejected, and the first recovery attempt (Reset Live State) deleted the only local record that they were real. Recognize this shape and follow this sequence rather than improvising — some of the obvious-looking recovery steps make it worse.
How to recognize it: The trade shows ENTRY REJECTED — rolled back, but the rejection reason recorded per leg is literally UNKNOWN rather than a real broker message (a margin shortfall or a banned-contract error shows the broker's own text instead). UNKNOWN means ThetaPrime's own fill check couldn't read the broker's order status at all — it does not mean the broker actually rejected anything. Before doing anything else, check the broker's own order book / trade book (not ThetaPrime's dashboard) for the legs named in the rejection — if they show as filled there, this is that scenario, and the "rejected" leg is a real, live position ThetaPrime has lost track of.
Step 1 — Get the ground truth from the broker first, before touching any button in the app. Open the broker's own Positions tab (not Executed Orders — that only shows individual fills, not net quantity) for every symbol involved. Write down the real net quantity per leg. This is the one fact nothing in ThetaPrime can substitute for, and every step below depends on it.
Step 2 — Disable the strategy toggle. This stops the scheduler from firing a fresh automated entry into the gap you're about to create in step 3 — that gap, filled by an unplanned re-entry, is what turned the original incident into a second, bigger one. Disabling only sets a flag; scheduled ticks keep firing but no-op while it's off.
Step 3 — Do not reach for Edit Trade State's per-leg fields or Confirm Active yet. A leg that was misclassified as rejected lives only inside the trade's internal _partial_entry record, not in the normal leg list Edit Trade State edits — so a correction there fails with "Unknown leg", and Confirm Active (broker-verified) re-checks the broker using the original, possibly wrong, recorded symbol/strike regardless of what you just edited — it can fail with "broker shows 0, expected N" even though the position is genuinely open, if that recorded strike doesn't match what actually got filled. Neither screen can currently fix this — don't spend time fighting them.
Step 4 — Reset Live State, now that step 1 and 2 are done. This is safe at this point because you've already recorded the true broker position and disabled auto re-entry. Resetting before step 1 is the mistake that compounds this incident — it deletes the entire local record, including any parked long legs that are genuinely live at the broker, leaving them completely untracked with nothing to flag it.
Step 5 — Manual Entry, immediately, using what you wrote down in step 1 — not the values that were in the broken record. Fill in every leg that's genuinely open at the broker (symbol, action, real fill price, strike), matching the broker's own order/trade book. If the structure has both a short-leg expiry field and a long-leg expiry field but you're only recording surviving long legs, put the same (long-leg) date in both fields — the short-leg expiry is inert when no short leg is being submitted, and leaving it blank only trips client-side validation, not a real backend requirement. Manual Entry independently re-verifies every leg against broker.positions() before saving, so it will only succeed if what you typed is real.
Step 6 — Re-enable the strategy once Manual Entry confirms the trade is ACTIVE.
If a leg is genuinely missing (e.g. the short side of a diagonal never filled at all): Manual Entry always records the trade as plain ACTIVE, not SHORT_CLOSED — so the automatic "roll the untested side back in" mechanic (which only triggers from SHORT_CLOSED) will never fire, and clicking Run will not place the missing leg for you. To add it: place that leg directly at the broker's own terminal yourself, then repeat steps 2, 4 and 5 — one more Reset, then a single Manual Entry submitting all legs (the ones already confirmed plus the newly-placed one) together.
If this keeps happening on Dhan specifically: a rejection reason of UNKNOWN (rather than a real broker message) on Dhan is a known signature of the broker adapter misreading Dhan's order-status response — confirm the running app build actually includes the fix for it before assuming this is a margin or contract problem. This is a broker-adapter defect, not something to work around per-incident in the UI.