Fix: Open Positions Not Showing (Missing cTrader Secrets + Timestamp Bug)
Date: 2026-05-05
Affected: Positions page, Overview metrics (Capital Consumed / Capital at Risk)
Root Cause #1 — Missing CTRADER_CLIENT_ID / CTRADER_CLIENT_SECRET in Production (Primary)
What was happening
The GET /api/bots/:id/positions handler (bots-positions-handlers.ts) calls resolveBotCTraderCreds() which checks:
const clientId = c.env.CTRADER_CLIENT_ID;
const clientSecret = c.env.CTRADER_CLIENT_SECRET;
if (!clientId || !clientSecret) {
throw new AppError('Server is missing CTRADER_CLIENT_ID / CTRADER_CLIENT_SECRET.', 500);
}
These two environment variables were never pushed as Wrangler secrets to production. The worker was throwing a 500 error on every positions request, returning {"error": "..."} — which the frontend treats as an empty positions array.
Fix
- Pushed both secrets to Cloudflare via
wrangler secret put:CTRADER_CLIENT_IDCTRADER_CLIENT_SECRET
- Added both to
services/backend/.dev.varsfor local development.
Root Cause #2 — Frontend Timestamp × 1000 Bug (Secondary)
What was happening
In BotPositionsPanel.tsx, openTimestamp from the cTrader API is already in unix milliseconds. The code was incorrectly multiplying by 1000 in two places:
Bug 1 — scopedPositions filter:
// BEFORE (wrong): pushed timestamps to year ~56000
const atMs = position.openTimestamp ? position.openTimestamp * 1000 : null;
if (atMs == null) return false; // also dropped positions with no timestamp
With a scope window like "Today" active, every position would be filtered out because atMs was ~1000× in the future. Positions with null timestamps were also hidden.
Bug 2 — openedAt column renderer:
// BEFORE (wrong): displayed year ~56000
const d = new Date(ts * 1000);
Fix
// Fix 1: scopedPositions — no * 1000, keep null-timestamp positions
const atMs = position.openTimestamp ?? null;
if (atMs == null) return true; // include positions with no timestamp
// Fix 2: column renderer — no * 1000
const d = new Date(ts);
Additional: Debug Logging Added to Backend
Added console.log statements to ctrader-positions.ts to help diagnose future issues:
[cTrader-positions] Requesting TraderInfo + SymbolsList for ctid=...[cTrader-positions] TraderRes: balance=... moneyDigits=... currency=...[cTrader-positions] SymbolsListRes: symbolCount=...[cTrader-positions] ReconcileRes received: positions=... keys=...
Also added fallback for openPosition field name in ReconcileRes (some cTrader API versions use openPosition instead of position):
const rawPositions = (
msg.payload?.position ??
msg.payload?.openPosition ??
[]
) as Array<Record<string, unknown>>;
Deployments
- Backend:
npx wrangler deploy— secrets registered, debug logging live - Frontend:
FRONTEND_PAGES_PROJECT=quantago-app FRONTEND_BRANCH=main bash scripts/deploy-frontend.sh— timestamp fix live athttps://9418c777.quantago-app.pages.dev