Replaced Recharts default Tooltip with formatter prop (broken in
Recharts v3 with explicit type annotations) with a custom
TrendTooltip component matching the SQL Explorer pattern. Shows
each series name, value, and color on hover.
/api/patterns/{ticker} returns {ticker, patterns, count} but
useHistoricalPatterns typed its response as HistoricalPattern[].
The .map() call on the object caused 'e.map is not a function'.
Fixed by unwrapping resp.patterns in the hook's queryFn.
Trend charts blank:
- trend_windows uses upsert (1 row per ticker/window), so charts had
at most 1 data point. Added trend_history table (migration 024) that
appends every snapshot. New /api/trends/history endpoint serves the
time series. Frontend now uses useTrendHistory for charts and
useTrends for the latest summary card.
Competitor GUIDs:
- list_competitors query returned raw company_b_id UUIDs without
joining companies table. Added LEFT JOIN with CASE to resolve the
other company's ticker and legal_name. Updated Pydantic model to
include enriched fields. Frontend fallback changed from truncated
UUID to ticker/legal_name/Unknown.
- ID mismatch: API generated a throwaway UUID while BacktestReplay
generated its own internally. Frontend polled with wrong ID and
never found the DB row. Now pre-generate ID in endpoint and pass
it to BacktestReplay.
- Field name: API returned 'backtest_id' but frontend read 'data.id'.
Unified to 'id' everywhere.
- No polling: useBacktestResult fired once and never refreshed.
Added refetchInterval that polls every 2s while status is running.
- Response shape: GET endpoint nested results under 'result' object
but frontend expected flat fields. Flattened response to match
BacktestResult type.
- Added running/failed/completed status indicators in BacktestPanel.
- Add dedup check in recommendation worker: skip generation when latest
rec for same ticker+window has identical action/mode/confidence
- Widen position sizing range (1-10% portfolio, 0.3-2% max loss) and
factor in trend strength + evidence count for differentiated sizing
- API returns only latest recommendation per ticker by default (DISTINCT ON)
to eliminate duplicate rows in the frontend list view
- Sell path: looks up existing position, sells full quantity, returns proceeds to pool
- Correlation matrix: computed from 30-day market_snapshots on startup + every 5min
- Holidays: 10 major US market holidays for 2026 checked in trading window functions
All 152 tasks across both phases are now marked complete:
- Phase 1 (1-26): pure computation modules, property tests, API, frontend, infra
- Phase 2 (27-37): live decision loop, stop-loss monitor, performance metrics,
risk tier scheduler, rebalancer, notification dispatch, backtest replay,
real DB connections, paper trading config, integration tests
Hover over any bar, line point, or scatter dot to see every column
value for that data point. The Y-axis column is highlighted in
brand color, X-axis in white, and other columns in gray. Works
for all chart types (bar, line, scatter, auto).
Adds an '✨ Auto' button that analyzes query results and picks the
best chart type and column mapping:
- Date/time column + numeric → line chart (time series)
- Categorical + numeric → bar chart (categories)
- Two numeric columns → scatter plot
- Shows detected type and column names as a label
Click Auto, run any query, and it figures out the rest.
Deploy scripts live on gremlin-1 at ~/sources/kube/stonks-oracle/,
not in the git repo. They reference local secret files and should
not be version controlled.
The pg-query API returns all values as strings. The chart builder
was using Number() which returns NaN for non-numeric strings.
Now uses parseFloat with NaN fallback to 0.
- Strip SQL comments (-- and /* */) before checking for SELECT,
so queries with leading comments don't get rejected
- Show the actual error detail from the API response instead of
generic 'API error 400' in the SQL Explorer UI
The migration ran on every deploy, inserting duplicate queries each
time (96 instead of 12). Added UNIQUE constraint on name and changed
ON CONFLICT to reference it. Cleaned up 84 duplicates in DB.
The trading engine network policy only allowed egress on ports 443
(HTTPS) and 53 (DNS). Gmail SMTP uses port 587 (STARTTLS), causing
'Network is unreachable' when sending notifications.
Replaced the Gmail API (OAuth2) notification delivery with plain
SMTP using a Gmail app password. Much simpler setup — no Google
Cloud project, no OAuth2 flow, no extra dependencies.
- Rewrote _send_gmail() to use smtplib with smtp.gmail.com:587 TLS
- Added stonks-gmail-secrets to Helm chart (GMAIL_SENDER,
GMAIL_RECIPIENT, GMAIL_APP_PASSWORD)
- Added gmail secret to trading-engine deployment
- Updated runmefirst.sh to read gmail.app from kube dir
- Sender/recipient: celes@celestium.life