What we've shipped.

2026-05-14 · v0.67 feature

Twelve Data WebSocket streaming (Phase 1: backend-only)

Phase 1 of the WebSocket migration. Backend now maintains a persistent WS connection to Twelve Data and stores the latest tick per symbol in a thread-safe in-memory dict. The existing /api/quote/live endpoint checks this dict BEFORE making a REST /quote call — on hit (which is the common case after the first ~1s for a subscribed symbol), it returns the WS tick without burning any TD credits. Frontend protocol unchanged (still polls every 2.5s), but each poll now returns sub-second-old data instead of REST's 1-2s vendor-lag floor. Source label is 'twelvedata-ws' on WS-served quotes vs 'twelvedata' on REST. Falls back to REST silently when WS isn't connected yet, hasn't received a tick for that symbol, or the user is on a plan that doesn't include WS. previous_close (which WS doesn't push) is cached separately for 10 min so we don't burn REST credits computing change/percent_change on every WS-served response. Phase 2 (SSE-style streaming to the browser) is the next push — that's what removes the polling entirely and gives true ToS-style sub-second smoothness. New dependency: websocket-client>=1.7.0 (lazy-imported so the app still boots if it's missing). Bumped SW to tt-v75.

2026-05-14 · v0.66 fix

Polling cadence retuned for TD Pro Lite (610 credits/min)

User upgraded TD to Pro and immediately hit rate limit: '803 API credits used, current limit 610'. Turns out the entry Pro tier (Pro Lite, ~$29/mo) caps at 610 credits/min, not the 1500/min of standard Pro. Our defaults were tuned for 1500. Retuned for 610: live-quote server cache 0.5s→1.5s, client poll 1s→2.5s (24 req/min instead of 60), NASDAQ options cache 8s→20s, options-chain frontend poll 15s→30s. Total per-session TD usage now ~50 credits/min vs the previous ~120+, leaving comfortable headroom for paper-position repricing + multi-tab sessions. Best-case live-quote lag goes from ~1.5s to ~4s — still meaningfully fresher than NASDAQ-delayed Grow, just not the sub-second ToS experience that requires standard Pro 1500/min or higher. Anyone bumping to standard Pro can tighten these back later; for now no rate-limit errors and real-time data flows. Bumped SW to tt-v74.

2026-05-14 · v0.65.2 fix

Fix: options chain values frozen because backend cache > poll interval

Diagnostic on a live TSLA chain showed two consecutive frontend polls 10 seconds apart returning byte-for-byte identical payloads — same spot, same ATM call mid ($5.825 each). Root cause: NASDAQ backend cache TTL (30s, from v0.63) was longer than the frontend poll interval (15s), so 50% of polls hit the same cached upstream response and the table never visibly ticked. Dropped TTL from 30s → 8s, which is strictly LESS than the 15s client poll cadence — guarantees every frontend tick triggers a fresh upstream fetch. NASDAQ load: ~4 req/min/ticker/user, well within their tolerance for a single user. Bumped SW to tt-v73.

2026-05-14 · v0.65.1 fix

Fix: options chain went dark for ~half a second every 15s refresh

loadChain() was wiping the canvas to a 'Fetching options chain…' card on every entry — including the silent auto-refresh, which fires every 15s. Result: the table visibly blanked out and reappeared on each tick. Fix: loadChain now takes a {silent:true} option; the auto-refresh timer + visibilitychange resume use it. In silent mode, the function skips the loading status, leaves the previous table on screen during the fetch, and only swaps in the new HTML once it's ready — single synchronous DOM op, no perceptible flash. Silent fetch errors no longer wipe the table either: they're console-warned and the next 15s tick retries. Manual loads (button click, ticker/expiry change, practice presets) still show the loading state. Bumped SW to tt-v72.

2026-05-14 · v0.65 fix

Paper option positions now reprice from the LIVE options chain

Real correctness bug. Open paper option positions had their 'Current premium' generated by a synthetic random-walk simulator (rng.gauss in _advance_paper_position) — completely disconnected from the actual options chain. So a TSLA $460C might show 'Current: $3.16' on the position card while the chain showed $3.24 for the same contract, and floating P&L / stop-target auto-fire were both computed off the wrong number. New _live_option_mark() helper looks up the contract's real mark from the options-chain sources (Twelve Data → NASDAQ, both with their existing caches). _advance_paper_position now uses the live mark for OPTION positions before falling back to the synthetic walk (kept for market-closed / source-down scenarios). Server-side stop/target hit detection mirrors the same check so backend auto-close stays consistent with the frontend watchdog from v0.64. Position card now matches the chain view tick-for-tick. Bumped SW to tt-v71.

2026-05-14 · v0.64.1 fix

Fix: Limit/Stop tab snapped back to Market every portfolio poll

Real bug in v0.64. Clicking Limit or Stop on the close form would visibly switch back to Market within a couple seconds. Cause: the portfolio refresh polls every few seconds and re-renders the open positions list from scratch, blowing away the form's HTML — which defaulted back to the Market tab. The exit-price input value was preserved across re-renders (existing draft logic) but the active tab wasn't. Fix: capture data-mode for each form before re-render, stash it on the rebuilt form via dataset._prevMode, and call applyMode(restoredMode) after wiring so the previously-active tab is repainted with its label/banner/button colors. Also re-restores the user's typed draft AFTER applyMode runs, so the input value doesn't get clobbered by the mode's default. Bumped SW to tt-v70.

2026-05-14 · v0.64 feature

Position close form: Market / Limit / Stop modes (and options auto-fire)

The close form on each open position now has Market / Limit / Stop tabs, replacing the previous one-mode 'Close NOW (instant)' form. Market keeps the instant-close behavior with the current premium / price pre-filled. Limit saves a sell-at target via the new PUT /api/paper/positions/<id>/levels endpoint — the existing watchdog auto-closes the position when the live price (or option mark) reaches the target. Stop does the same on the downside. Stops you from bouncing to the Trade Desk just to set a limit-sell on a position you opened from the options chain. Plus: the watchdog (checkStopTriggers in static/app.js) now also fires for OPTION positions, comparing the position's current_price (mark) against stop/target instead of the underlying — previously options levels were saved but never auto-fired, which is the kind of silent failure that costs people money. Toast on auto-close now distinguishes contract closes (e.g. '100 TSLA ct @ $5.00/sh') from share closes. Tabs are color-coded — Market red, Limit green, Stop amber — matching broker conventions. Bumped SW to tt-v69.

2026-05-14 · v0.63 feature

Snappier real-time: live-quote latency cut + options chain auto-polls

Two related freshness improvements that visibly close the gap with ToS during fast tape. (1) Live-quote latency: backend cache TTL dropped from 1.5s → 0.5s on paid TD plans, frontend poll cadence tightened from 2s → 1s. Worst-case server-perceivable lag drops from ~3.5s to ~1.5s. Grow's 1500/min credit budget has comfortable headroom for the new cadence (~60 req/min per session vs 1500 available); combined with the existing rate-limit backoff, no risk of triggering Yahoo fallback. True sub-second smoothness still requires TD Pro WebSocket streaming. (2) Options chain auto-polls. The chain used to be a snapshot — once you clicked Load, contract prices never moved until you clicked again. Now it auto-refreshes every 15s while the view is mounted, the tab is visible, and you're not in practice mode. Pauses on tab hide, snaps fresh on tab focus. Backend NASDAQ cache shortened from 5min → 30s so two adjacent polls share an upstream fetch at most. Bumped SW to tt-v68. Plan-upgrade reminder: TD Pro (~$80/mo) unlocks WebSocket streaming for both stocks and options realtime, which is the only way past the polling-based ceiling.

2026-05-14 · v0.62.2 fix

Chart volume readout: stop showing '—' during the minute-boundary gap

At every minute boundary the chart synthesizes a fresh in-progress bar with v=0 (the real TD candle hasn't been fetched yet — on Grow plan that's up to ~60s away). The right-side volume readout pill then dutifully showed '—' inside a colored border, which looked like a bug ('volume is broken!') when really the data was just in transit. Fix: when the last bar is _synthetic AND v=0, render the pill MUTED with '···' instead of a green/red border with '—'. Signals 'pending' instead of 'broken'. Once the next candle REST poll lands and overwrites the synthetic bar with the real one, the pill snaps back to normal directional coloring with the real volume number. The synthetic-bar window is bounded by candle poll cadence — ~60s on Grow, ~5s on Pro+. Bumped SW to tt-v67.

2026-05-13 · v0.62.1 fix

Fix: production was serving stale app.min.js (chart pop-out fix masked)

Two-part fix for a real foot-gun. (1) The boot-time minifier used mtime-based staleness — `if dst_mtime >= src_mtime: return # already up to date`. On a fresh Render deploy, git checkout writes both app.js and app.min.js with the same mtime, so the check concluded 'up to date' and skipped regeneration. Production served the committed (potentially stale) app.min.js even when app.js had the latest fix. That's exactly how v0.61.2's chart-popout URL fix got masked: the source had `/app#/chart-popout` but production kept serving `/#/chart-popout` because the old minified bundle was already in the repo. Switched the check to a sha256 of app.js stored in a sidecar (app.min.js.src-sha256). Now staleness = source-content mismatch, not mtime — bulletproof against fresh checkouts. (2) Regenerated app.min.js locally with the v0.61.2 fix in place + committed the fresh bundle, so production picks up the corrected pop-out URL on the next deploy without waiting for the new minify logic to kick in.

2026-05-13 · v0.62 feature

Options chain: customizable columns (ToS-style picker)

Adds a ⚙ Columns dropdown to the options-chain toolbar. Pick any combination of Volume, OI, IV, Bid, Ask, Last, Theo, Intrinsic, Extrinsic, Rho, Vega, Theta, Gamma, P(ITM), P(OTM), Delta — same set ToS exposes. Selections persist in localStorage (options_view_prefs_v1.cols), survive page reloads, and the table re-renders instantly without a fresh chain fetch. Defaults stay Volume/OI/Δ so existing users see the same view they had yesterday. Implementation: registry-based COLUMN_DEFS in /static/options-chain.js with per-column getter+formatter so adding a column = one entry, no renderTable surgery. renderTable is now fully dynamic — colgroup widths recompute as 44% / (N+1) per side cell, side-label colspans, header rows, and the spot-marker colspan all derive from the selected count. Mark stays implicit (always rendered, always closest to Strike, always green for calls / red for puts). Empty-state guard: if the user unchecks every column the picker auto-restores Δ + flashes a toast. Reset button restores defaults. Esc closes the picker. Bumped SW to tt-v66.

2026-05-13 · v0.61.2 fix

Fix: chart pop-out opened marketing landing instead of the chart

Clicking the pop-out button on the chart was opening a new window to tradorian.com/#/chart-popout?ticker=...&interval=... — which hit the MARKETING landing page (now served at '/'), not the SPA shell. The landing page doesn't know about hash routes, so users got the homepage in a small window instead of their chart. Root cause: when the landing/SPA split shipped in v0.48 (landing → '/', app → '/app'), the chart pop-out URL builder in app.js was missed. Fixed by changing the target from '/#/chart-popout?…' to '/app#/chart-popout?…'. Bumped SW to tt-v65 so users pick up the new app.js immediately on next load.

2026-05-13 · v0.61.1 fix

Options chain polish — symmetric columns + red Put Mark buttons

Two fixes on top of v0.61's RH Legends rewrite: (1) Put Mark buttons now render in RED instead of green — matches RH Legends / ToS convention (calls = green, puts = red), makes the eye scan much faster. (2) Replaced the nested per-side sub-tables with a flat 9-column main table + colgroup widths (11/11/11/12 % for calls, 12% for strike, 12/11/11/11 % for puts). Now the four call columns and four put columns line up exactly across the spot marker — no more visual drift between sides.

2026-05-13 · v0.61 feature

Options chain: RH Legends side-by-side layout (calls left, puts right)

Rewrote renderTable in /static/options-chain.js to show calls and puts simultaneously like Robinhood Legends / ToS, instead of a single side at a time. Strikes descending in the middle column with a subtle green tint on the ITM call side (left, below spot) and red tint on the ITM put side (right, above spot). The spot-price marker still drops between OTM and ITM rows. Mark buttons remain clickable on either side — clicking the call-side Mark opens a call ticket, put-side opens a put ticket.

2026-05-13 · v0.60 feature

Change-password for logged-in users

Closes a gap in the auth story: previously you could request a password-reset via email (POST /api/auth/forgot-password → token-by-email → POST /api/auth/reset-password), but there was no way for a LOGGED-IN user to change their password directly from account settings without involving email. v0.60 adds POST /api/auth/change-password — verifies current password matches the stored hash, validates new password (min 6 chars, must differ from current), updates the hash, keeps the session alive. Frontend adds a Security card at the bottom of the Profile view with current/new/confirm fields and inline validation. Works in production today even without Postmark wired up (forgot-password still needs Postmark to deliver the reset email — change-password doesn't). 2 new bad-input probes confirm the endpoint rejects unauthenticated access with 401.

2026-05-13 · v0.59 infra

Code-split: drawing-tools chunk (the biggest payload win)

Fourth chunk extracted: /static/drawing-tools.js (136 KB). Contains the pure-rendering functions (renderOneDrawing — the 1,386-line type-switch with all 12 phases of geometry, plus renderHandles and hitTestDrawing) and the read-only registries (DRAW_TOOLS, DRAW_CATEGORIES, DRAW_PALETTE). app.js went 26,954 → 24,801 lines (2,153 moved out — the biggest single extraction so far). Pattern: pure functions take a `ctx` object with closure refs (lastChartLayout, drawDefaultColor, cache, timeToX, priceToY, escapeHTML); a destructure prologue at the top of each function aliases ctx fields back to the original variable names so the 1,386-line function body stays BYTE-FOR-BYTE identical — no risk of subtle rename bugs in the intricate SVG geometry. State (drawings[], activeTool, drawingInProgress, drag/click handlers, persistence, alarms) stayed in the wirePaperChart IIFE closure because lifting that too would have meant re-architecting event wiring across 600+ lines, which is a separate effort with its own dedicated test coverage. Chunk is loaded eagerly on /paper mount via _loadChunk(); stubs return safe defaults (empty SVG, no-hit) during the brief load window. Combined with admin + options-chain + lessons-reader, ~247 KB now lifted off the critical-path bundle for sessions that don't touch /paper. Cache bumped to tt-v61.

2026-05-13 · v0.58 feature

Week 6+ moat: lessons-reader chunked, 3 new modules, numeric_input drill type, pt-BR starter

Four-piece moat drop. (1) Lessons reader extracted to /static/lessons-reader.js (44 KB) — third code-split chunk. app.js went 27,707 → 26,853 lines (854 moved out). Combined with admin + options-chain, ~110 KB lifted off the critical-path bundle for sessions that never open a lesson reader. (2) Three new modules: 65-market-regimes (trending/mean-reverting/choppy + the diagnostic checklist), 66-event-driven-vol (earnings, FOMC, the vol-crush mechanic), 67-correlations-and-pairs (the hidden lever that turns a 'diversified' book into one bet). Curriculum is now 67 modules. (3) New drill type: numeric_input. User types a number, backend checks within tolerance (default ±2% relative, falls back to absolute band when specified). Used for position sizing, R-multiple, drawdown-recovery, Kelly. Five new drills added to 10-risk module showcasing the type. UI shows a 'Your answer / Expected' badge after submit so the user sees how far off they were. (4) Brazilian Portuguese starter bundle at /static/translations/pt.json — hand-translated 110 highest-frequency UI strings (nav, drill, modal, auth, today, lessons, paper, discipline). Remaining 1,000+ keys fall back to English at runtime or auto-translate via /api/translate; full bulk-fill available via 'python3 build/translate_to_es.py --lang pt'.

2026-05-13 · v0.57 fix

Trade Desk: fresh MKT price + marketable ladder clicks + position averaging

Three Trade Desk fixes shipped together. (1) Buy MKT / Sell MKT / Flatten / Reverse now fresh-fetch /api/quote/live right before submitting instead of trusting the cached chip price — closes a real bug where the chip could diverge from the actual market and orders filled at a different price than the user was looking at. Plus a 'market moved' confirm if the fresh quote differs >0.5% from the chip. (2) Ladder clicks at-or-within-0.1%-of-market with an open opposite-side position now close that position immediately via the broker endpoint (instead of placing a passive working order that sits there). Click ASK with an open long near market = instant close, matching the user's mental model. Stops / take-profits farther from market still place working orders, preserved. (3) Opening a second same-direction position on the same ticker now MERGES into the existing row with size-weighted average entry — no more parallel stacked rows. Bracket entries opt out of merging so their child stop/target legs still match the right entry size. Smoke probe verifies 100 NVDA @ $500 + 100 NVDA @ $520 collapses to 1 row of 200 sh @ avg $510.00.

2026-05-13 · v0.56 fix

Cleanup: removed dead #paper-at-open wiring

The legacy 'Open @ staged' button was deleted in v0.46 when ToS-style direct-click ladder semantics replaced the stage-then-submit workflow — but four blocks of JS that referenced it stayed in app.js, including one that logged a loud red 'Trade Desk: #paper-at-open button not found in DOM. Hard-refresh with Cmd+Shift+R.' error to the console on every Trade Desk load. Chart was actually working fine; the error was misleading users into thinking it wasn't. Removed all four references, replaced status-strip updates with toast() calls that were already used in the same flow, bumped SW to tt-v57.

2026-05-13 · v0.55 infra

Code-split: options chain extracted to its own chunk

Second chunk extracted following the code-split pattern shipped in v0.54. /static/options-chain.js (~43 KB) — options chain UI, Black-Scholes synthetic chain generator, and the order-ticket modal — now loads lazily only when a user navigates to #/options. app.js dropped from 28,470 → 27,643 lines (827 moved into the chunk). Combined with admin.js, the critical-path bundle is now ~66 KB lighter for the ~99% of sessions that never touch admin or option chains. Drawing tools (the biggest single payload) deferred — they have closure ties to renderCandleChart's lastChartLayout/drawings/activeTool that need a refactor before clean extraction. Next chunk on deck: lessons reader.

2026-05-13 · v0.54 infra

Code-split admin bundle + Options Coach (5th persona)

Working JS code-split pattern shipped with admin views as the proof-of-pattern. /static/admin.js (~23 KB) now loads lazily via dynamic <script> injection only when an admin navigates to #/admin — saves ~57 KB off the critical-path bundle for non-admin users. Full migration template in docs/ARCHITECTURE.md so the next surfaces (drawing tools, options chain, lessons reader) can follow the same recipe at ~half-a-day each. Also: Options Coach persona added to COACH_DEFS — distinct from Setup Coach (price-action equities) and Risk Coach (generic sizing), focused on Greeks, IV crush, theta decay, spreads vs naked. First Week 6 moat-deepening drop.

2026-05-13 · v0.53 infra

Week 5 — business legible

Public changelog at /changelog (data-driven, edit data/changelog.json to ship news). Public roadmap at /roadmap (4 columns: In progress / Considering / Shipped / Won't do). Stripe past_due dunning polish — banner now routes directly to the Customer Portal for card updates instead of nudging users to subscribe again. SOC 2 Type 1 vendor-scoping doc at docs/SOC2.md.

2026-05-13 · v0.52 infra

Week 4 — engineering invisible

Sentry + PostHog wiring (opt-in via env), 5 funnel events (signup → onboarded → first drill → first paper trade → subscribed), sidebar collapsed from 28 items to ~8 visible-by-default with Show-all toggle, mobile Trade Desk pain-point sweep (tap targets ≥44px, ladder full-width on phone). Code-split + Flask blueprints explicitly deferred — both deserve a dedicated effort.

2026-05-13 · v0.51 feature

Week 3 — the autopsy loop + push delivery plumbing

Trade autopsy as forced post-close screen: 3-card modal (grade letter, 5-dimension breakdown, drill the weak spot). Wired into both manual close and stop/target auto-close. Streaks on Today now full-width with at-risk vs safe states. PWA push infrastructure: push_subscriptions table, /api/push/subscribe + unsubscribe, SW push/notificationclick/pushsubscriptionchange handlers, admin trigger endpoints for daily reminders and the Sunday recap email.

2026-05-12 · v0.50 feature

Week 2 — Day 1 undeniable

Welcome drill (Hammer candle) replaces the timed dismiss after onboarding. Level-up moment with confetti + 'tomorrow at 9 AM' promise + push-permission ask. Landing page got a Watch-30-second-demo play overlay + lightbox (drops in landing-demo.mp4 when ready).

2026-05-12 · v0.49 feature

Week 1 — close the trial-to-paid loop

Pricing chip ($50/mo or $500/yr) inside the trial banner so users know the post-trial ask. /app#signup now actually opens the signup tab. Stripe webhook idempotency race fixed (INSERT-OR-IGNORE-FIRST, atomic). Real Terms / Privacy / Disclaimer copy at /terms /privacy /disclaimer.

2026-05-12 · v0.48 feature

Public landing page at /

Hero with green shield + 'gym for day traders' tagline, 3-feature block (Learn / Practice / Trade), SVG product mockup of the Trade Desk, pricing cards ($50/mo + $500/yr), 5-question FAQ, footer with legal links. SPA moved to /app; PWA manifest start_url updated to /app.

2026-05-12 · v0.47 fix

Audit-driven hardening

Rate-limit hard-fail in production when Flask-Limiter missing (instead of silently no-op). /api/export-db gated on admin. TZ-correct chart slicing for replay (NY-local for stocks). TD timestamp clamp when stale. Mistake-drills status filter now 400s on garbage.

2026-05-12 · v0.46 fix

Trade Desk wiring fixes

Working orders list replaces the dead 'Entry/Stop/Target staged' footer. Position strip (B/S, Pos, Avg, P/L Open) now reflects open positions on every render via portfolio refetch + live-tick. tickSize scales with price ($5 above $10k for BTC). 'Close NOW' relabel + confirm prompt on the paper-portfolio Exit price form.

2026-05-11 · v0.45 feature

Options Practice Mode

Synthetic Black-Scholes chain generation (Today 9:30 / 12 / 3 / Yesterday close presets, step buttons, Play/Pause auto-advance with speed selector). Also fires as a fallback when all real options-chain sources (TD / NASDAQ / Yahoo) fail.

2026-05-11 · v0.44 infra

Coinbase as primary crypto source

BTC/USD, ETH/USD, etc. now route to Coinbase Exchange first for candles + live quotes. Solves the 'X bars without volume' gap users saw on the BTC chart when Twelve Data didn't have crypto data.

← Back to home