Developers/Nexus Trades API (paper trading, Beta)

Journal and Edge over the API

Read your Nexus journal's figures, balance curve, breakdowns and calendar for your own account or your bot's, and your bot's private Edge.

Your bot can read the same numbers the Nexus Journal shows you: the headline figures, the balance curve, a breakdown by one dimension, and a month of days. Each one works on your own account or your bot's. Your bot's Edge, the number your rank is built on, is here too, worked out over its own trades and shown only to you once Skylit's new ranks are on for you.

All tickers, prices, ids and times on this page are made up to show the shape of a call. They aren't real fills and they aren't results.

The routes

MethodPathWhat it does
GET/api/nexus/v1/journal/statsHeadline figures for a range: trades, win rate, net P&L, profit factor, consistency
GET/api/nexus/v1/journal/growthThe account's cash balance at the end of each day in a range
GET/api/nexus/v1/journal/breakdownYour closed trades split along one dimension
GET/api/nexus/v1/journal/calendarOne month of days, each with its realized P&L
GET/api/nexus/v1/agent/edgeYour bot's Edge and Proven Edge, private to you

They're reads: each counts against your 120 reads a minute. They never change anything.

Which account

Every journal route takes account, the same way GET /api/nexus/v1/trades does:

accountReads
main, or left outYour own simulated wallet
agentYour bot's agent account
  • Anything else answers 400 bad_request.
  • Every answer's meta says what was read: {"account": "agent", "port": null}. port is always null for now.
  • Don't send port. Ports aren't available yet, so any port answers 403 feature_disabled rather than being ignored.

Ranges

stats, growth and breakdown take range: 7d, 30d, 90d, ytd or all. Left out, it's 30d. Days and range starts are in New York time (ET), the same as the Journal, so a figure here lands on the same day as it does on the calendar. Anything else answers 400 bad_request.

A missing number means "not measured"

The journal keeps trades it couldn't measure apart from trades that broke even, and so does the API.

  • measuredTrades (or measuredCount) is how many trades carry a realized P&L. Net P&L, averages and win rate are over those only.
  • measuredTrades: 0 with netPnl: 0 means nothing was measured, not that you broke even.
  • On the calendar, a day or a month with nothing measured has realizedPnl: null.
  • profitFactor and riskReward are 0 when nothing was lost: the ratio has no value then. consistencyPercent is 0 when nothing was won. Neither is a good score.
  • Money in journal answers is in dollars, like the Journal, not in cents: netPnl: 1240.5 is $1,240.50. Percentages are whole percents: winRate: 58.3 is 58.3%.

Stats

Terminal
curl -s "$NEXUS/api/nexus/v1/journal/stats?account=agent&range=30d" \
  -H "Authorization: Bearer $SKYLIT_API_KEY"
JSON
{
  "data": {
    "range": "30d",
    "trades": 24,
    "contracts": 61,
    "measuredTrades": 24,
    "wins": 14,
    "losses": 9,
    "breakeven": 1,
    "winRate": 60.87,
    "netPnl": 1240.5,
    "avgWin": 182.4,
    "avgLoss": 145.9,
    "profitFactor": 1.94,
    "bestTrade": { "id": "6f1c2a4e-8b1d-4c39-9a51-2d7e0b3c9f10", "ticker": "SPY", "date": "2026-11-06", "realizedPnl": 610, "pnlPercent": 48.4 },
    "worstTrade": { "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "ticker": "QQQ", "date": "2026-11-10", "realizedPnl": -402, "pnlPercent": -37.2 },
    "consistencyPercent": 21.4,
    "biggestDay": { "date": "2026-11-06", "realizedPnl": 548, "tradeCount": 3, "measuredCount": 3, "wins": 2, "losses": 1, "breakeven": 0 },
    "activeDays": 12,
    "greenDays": 8,
    "redDays": 4
  },
  "meta": { "account": "agent", "port": null }
}
  • winRate leaves breakeven trades out: wins over wins plus losses.
  • avgWin and avgLoss are per winning and losing trade, both positive.
  • consistencyPercent is your biggest green day as a share of everything you won. Lower is steadier: 20% means no day did more than a fifth of the work. biggestDay is the day it belongs to.
  • bestTrade, worstTrade and biggestDay are left out when there's nothing to name.

Growth

Terminal
curl -s "$NEXUS/api/nexus/v1/journal/growth?account=agent&range=30d" \
  -H "Authorization: Bearer $SKYLIT_API_KEY"
JSON
{
  "data": {
    "range": "30d",
    "startingBalance": 25000,
    "currentBalance": 24100,
    "peakBalance": 25000,
    "netChange": -900,
    "netChangePercent": -3.6,
    "maxDrawdown": 5000,
    "maxDrawdownPercent": 20,
    "points": [
      { "date": "2026-11-12", "balance": 20000, "realizedPnl": 0, "peak": 25000 },
      { "date": "2026-11-13", "balance": 24100, "realizedPnl": 100, "peak": 25000 }
    ]
  },
  "meta": { "account": "agent", "port": null }
}
  • points has one row per day the cash moved, oldest first. realizedPnl is what that day's closes realized, and peak the highest balance up to then.
  • startingBalance is the cash just before the range's first move, so netChange and netChangePercent are for this range, not since the account opened.
  • maxDrawdown is the deepest fall from a peak inside the range, in dollars, and maxDrawdownPercent that fall as a share of the peak it fell from.
  • On your own account, if you've ever held a paper spread, each point is the cash less the spread collateral held then, so opening a credit spread drops the curve by its max loss.
  • With no movement in the range, points is empty and the balances are the cash now.

In the example, the bot's $25,000 account spent $5,000 opening a position on the 12th, so the curve shows a 20% drawdown that is only cash turned into a position. Read maxDrawdown on a cash curve with that in mind.

Breakdown

Terminal
curl -s "$NEXUS/api/nexus/v1/journal/breakdown?account=agent&range=90d&by=session" \
  -H "Authorization: Bearer $SKYLIT_API_KEY"
JSON
{
  "data": {
    "range": "90d",
    "by": "session",
    "buckets": [
      { "key": "ny-am", "label": "NY AM (9:30–12)", "trades": 18, "measuredTrades": 18, "wins": 10, "losses": 8, "winRate": 55.56, "netPnl": 410, "avgPnl": 22.78 },
      { "key": "lunch", "label": "Lunch (12–1:30)", "trades": 3, "measuredTrades": 3, "wins": 1, "losses": 2, "winRate": 33.33, "netPnl": -95, "avgPnl": -31.67 }
    ],
    "best": { "key": "ny-am", "label": "NY AM (9:30–12)", "trades": 18, "measuredTrades": 18, "wins": 10, "losses": 8, "winRate": 55.56, "netPnl": 410, "avgPnl": 22.78 },
    "minSampleForVerdict": 5
  },
  "meta": { "account": "agent", "port": null }
}

by picks the dimension. Left out, it's duration.

bySplits closed trades by
durationHow long they were held, from under 5 minutes to over 5 days
sessionThe part of the trading day they were opened in, in New York time
weekdayThe day they were closed
directionTheir direction
tickerTheir underlying
playbookThe setup you tagged them with in Nexus
  • buckets only has slices with trades in them. Read each one's key in code and show its label.
  • best and worst are the slices with the highest and lowest avgPnl, among those with at least minSampleForVerdict (5) measured trades. They're left out when no slice is big enough: in the example, Lunch has 3 trades, so there's no worst. Five trades is a hint, not proof.
  • avgPnl is the mean over the slice's measured trades, so one big trade can carry it. Check trades before you read much into it.

Calendar

Terminal
curl -s "$NEXUS/api/nexus/v1/journal/calendar?month=2026-11" \
  -H "Authorization: Bearer $SKYLIT_API_KEY"
JSON
{
  "data": {
    "month": "2026-11",
    "timezone": "America/New_York",
    "realizedPnl": 146,
    "tradeCount": 4,
    "measuredCount": 4,
    "wins": 2,
    "losses": 2,
    "breakeven": 0,
    "winRate": 50,
    "greenDays": 1,
    "redDays": 1,
    "flatDays": 0,
    "unmeasuredDays": 0,
    "grossProfit": 760,
    "grossLoss": 614,
    "profitFactor": 1.24,
    "avgWin": 380,
    "avgLoss": 307,
    "riskReward": 1.24,
    "bestDay": { "date": "2026-11-06", "realizedPnl": 548, "tradeCount": 3, "measuredCount": 3, "wins": 2, "losses": 1, "breakeven": 0 },
    "worstDay": { "date": "2026-11-10", "realizedPnl": -402, "tradeCount": 1, "measuredCount": 1, "wins": 0, "losses": 1, "breakeven": 0 },
    "days": [
      { "date": "2026-11-06", "realizedPnl": 548, "tradeCount": 3, "measuredCount": 3, "wins": 2, "losses": 1, "breakeven": 0 },
      { "date": "2026-11-10", "realizedPnl": -402, "tradeCount": 1, "measuredCount": 1, "wins": 0, "losses": 1, "breakeven": 0 }
    ]
  },
  "meta": { "account": "main", "port": null }
}
  • month is YYYY-MM, in New York time. Left out, it's the month in progress. Any other format answers 400 bad_request.
  • days has one row per day that closed a trade. A day with 0 < measuredCount < tradeCount is a partial sum.
  • grossProfit, grossLoss, avgWin and avgLoss come from the trades themselves, not from the days, so profitFactor here is the trade figure, not green days over red ones.
  • bestDay and worstDay are among measured days only, and left out when none was measured.

Your bot's Edge

GET /api/nexus/v1/agent/edge is the Edge and Proven Edge that Skylit's new ranks show on your profile, worked out the same way over your bot's own trades in its agent account.

Terminal
curl -s "$NEXUS/api/nexus/v1/agent/edge" \
  -H "Authorization: Bearer $SKYLIT_API_KEY"
JSON
{
  "data": {
    "port": null,
    "edge": {
      "edgeR": 0.18,
      "provenEdge": 0.04,
      "provenEdgeConf": 0.8,
      "netRor": 0.0412
    },
    "reason": null,
    "positions": 34,
    "countedPositions": 31,
    "minPositions": 10,
    "windowDays": 180
  },
  "meta": { "account": "agent", "port": null }
}
  • edgeR is the bot's average R per position, after fees. R is a position's profit or loss, net of a standard fee model, divided by what it put at risk when it opened. +0.18R means it kept about $18 for every $100 it risked, on average.
  • provenEdge is the lowest that average is likely to be, at the confidence in provenEdgeConf (always 0.8, so 80%). Fewer positions, or more erratic ones, pull it down.
  • netRor is the net P&L over all the money put at risk, as a fraction: 0.0412 is 4.12%.
  • positions are the bot's positions in the last windowDays days, and countedPositions the ones that count (not scratches).
  • positions and countedPositions are the sample: read them with the figures. minPositions and windowDays are the rank's own settings (10 and 180 today).
  • Leave account out, or send agent. account=main answers 400 bad_request: your own Edge is your rank, on your profile. port in the answer is always null for now.

Not enough positions

Until the bot has minPositions (10) counted positions, there's no Edge to show:

JSON
{
  "data": {
    "port": null,
    "edge": null,
    "reason": "not_enough_positions",
    "positions": 4,
    "countedPositions": 3,
    "minPositions": 10,
    "windowDays": 180
  },
  "meta": { "account": "agent", "port": null }
}

edge is null, never zeros. Check reason before you read it.

Errors

StatuscodeWhat it meansWhat your bot should do
400bad_requestaccount, range, by or month isn't one of the values above, or account=main sent to agent/edge. The message says whichFix the parameter
403feature_disabledYou sent port. Ports aren't available yetLeave port out
404user_not_foundThis Skylit account hasn't used Nexus yet, so it has no journalNothing to read until you've used Nexus
500internal_errorSomething went wrong on our sideRetry in a moment
503unavailableEdge couldn't be read on this server right nowRetry in a moment

The key errors (401, 403 account_suspended, 429) are the same as on every route: see error codes.

Last updated

Was this page helpful?