# 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

| Method | Path | What it does |
|---|---|---|
| GET | `/api/nexus/v1/journal/stats` | Headline figures for a range: trades, win rate, net P&L, profit factor, consistency |
| GET | `/api/nexus/v1/journal/growth` | The account's cash balance at the end of each day in a range |
| GET | `/api/nexus/v1/journal/breakdown` | Your closed trades split along one dimension |
| GET | `/api/nexus/v1/journal/calendar` | One month of days, each with its realized P&L |
| GET | `/api/nexus/v1/agent/edge` | Your 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`](/docs/api-reference/options-and-stocks/list-your-trades) does:

| `account` | Reads |
|---|---|
| `main`, or left out | Your own simulated wallet |
| `agent` | Your bot's [agent account](https://www.skylit.ai/docs/nexus-trades/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

```bash
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

```bash
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 }
}
```

> **Warning:** **It's a cash curve.** Each point is the account's cash at the end of that day. Buying a position lowers it by what you paid, and selling raises it, so a day you open a trade can look like a loss while the trade is fine. Open positions aren't marked to the market here. For what the account is worth right now, read the [wallet](https://www.skylit.ai/docs/nexus-trades/agent-account#check-the-wallet): `equityCents` is cash plus your open positions at their latest marks.

- `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

```bash
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`.

| `by` | Splits closed trades by |
|---|---|
| `duration` | How long they were held, from under 5 minutes to over 5 days |
| `session` | The part of the trading day they were opened in, in New York time |
| `weekday` | The day they were closed |
| `direction` | Their direction |
| `ticker` | Their underlying |
| `playbook` | The 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

```bash
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.

> **Info:** **Edge appears when Skylit's new ranks launch.** They aren't on for members yet, and there's no date. Until they're on for you, this route answers `200` with no figures:
>
> ```json
> { "data": { "enabled": false }, "meta": { "account": "agent", "port": null } }
> ```
>
> Check for `enabled: false` before you read anything else. Once the ranks are on, the answer is the one below.

```bash
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.

> **Info:** **Private, and never ranked.** Your bot's Edge is worked out when you ask for it and seen only by you. It's never written to a rank, a leaderboard, a season or your profile, and the bot's trades never count toward yours.

## Errors

| Status | `code` | What it means | What your bot should do |
|---|---|---|---|
| 400 | `bad_request` | `account`, `range`, `by` or `month` isn't one of the values above, or `account=main` sent to `agent/edge`. The message says which | Fix the parameter |
| 403 | `feature_disabled` | You sent `port`. Ports aren't available yet | Leave `port` out |
| 404 | `user_not_found` | This Skylit account hasn't used Nexus yet, so it has no journal | Nothing to read until you've used Nexus |
| 500 | `internal_error` | Something went wrong on our side | Retry in a moment |
| 503 | `unavailable` | Edge couldn't be read on this server right now | Retry in a moment |

The key errors (`401`, `403 account_suspended`, `429`) are the same as on every route: see [error codes](https://www.skylit.ai/docs/nexus-trades/overview#error-codes).

> **Note:** These are your own paper results, for reviewing how you and your bot trade. They're not a forecast or a recommendation, and a small sample says little.
