Skip to main content

Journal and Diagnostics

These tools let an AI trading agent answer the question "what just happened?" by reading three different timelines:

  • The user's matched-trade journal (NT8 fills, post-edit).
  • The webhook signal history (TradingView and other alert sources).
  • The CrossTrade XT Add-On Activity Log (in-process events).

All three are read-only and Elite-gated like the rest of the MCP surface.

Tools at a Glance

ToolReadsSource of truth
GetJournalTradesMatched trades (entry + exit) with computed P/L, MFE/MAE, efficiency.nt8_trades (NT8-sourced) + manual_trades (user-entered), with trade_overrides applied.
GetSignalHistoryWebhook signals: command, account, symbol, ack/error, source IP.API ingest log.
GetActivityLogXT Add-On in-process Info/Warning/Hidden events.Add-on local SQLite logs table.

GetJournalTrades

Paginated read of the materialized journal. This is the same trade list the user sees on /user/journal after their edits and manual entries are applied.

Why call this

An AI trading agent that wants to discuss outcomes ("how did MES do this morning?", "did my reversal strategy hit its target?") needs the corrected trade view, not the raw executions. GetJournalTrades returns:

  • NT8-sourced trades from nt8_trades (FIFO-matched executions, with per-trade P/L, MFE/MAE, efficiency).
  • User overrides applied per column. Editing a single field (commission, exit price) replaces only that field; everything else stays from the original NT8 record.
  • Soft-deleted trades excluded by default.
  • Manual trades the user typed in by hand (manual_trades), merged into the same timeline by exit_time.

Arguments

NameTypeDefaultNotes
sync_firstbooleanfalseWhen true, drain the add-on's local SQLite into the journal before reading. No-op if the add-on is offline; the read still runs against existing data.
accountstring(all)Filter to one account. Matches the exact name or the prop-firm suffix form (e.g. BX101476-01 also picks up BX101476-01!Bulenox!Bulenox).
startstring (ISO 8601)(none)Lower bound on exit_time. Date-only (2026-04-17) or full datetime; naive inputs are treated as UTC.
endstring (ISO 8601)(none)Upper bound on exit_time. Same parsing rules.
limitinteger100Page size. Range 1 to 1000.
offsetinteger0Pagination offset into the combined (NT8 + manual) timeline.
directionenumdescdesc returns newest exits first. asc returns oldest first.
include_flaggedbooleantrueInclude trades NT8 could not match cleanly (parse errors the user has not reviewed). Defaults to true so the agent can see and explain them.
include_deletedbooleanfalseInclude trades the user soft-deleted via override or by removing a manual entry.
include_manualbooleantrueInclude user-entered manual trades from manual_trades.

Response

{
"success": true,
"trades": [ /* see per-trade shape below */ ],
"count": 42,
"total": 1380,
"offset": 0,
"limit": 100,
"direction": "desc",
"last_sync": "2026-05-15T13:42:11Z",
"sync": { /* present only when sync_first:true was sent */ }
}
  • count is the size of trades on this page. total is the size of the merged result set before paging.
  • last_sync is the user's journal_last_sync setting. Useful for deciding whether to set sync_first:true on a subsequent call.
  • sync is the raw return of journal.sync when sync_first:true was passed. On a connected add-on it looks like {"executions": N, "orders": N, "balances": N, "instruments": N, "trades": N}. On a disconnected add-on it looks like {"error": "XT Add-On is not connected"}. The trade read still runs in either case.

Per-trade shape

NT8-sourced trades carry the full performance metric set:

{
"tradeHash": "8c9b…",
"account": "Sim101",
"instrument": "MES 06-26",
"instrumentRoot": "MES",
"side": "long",
"quantity": 2,
"entryPrice": 5314.25,
"exitPrice": 5318.00,
"entryTime": "2026-05-15T13:42:11+00:00",
"exitTime": "2026-05-15T13:55:04+00:00",
"profitCurrency": 37.50,
"profitPoints": 3.75,
"profitTicks": 15.0,
"profitPercent": 0.0705,
"mfeCurrency": 50.00,
"maeCurrency": -12.50,
"mfePoints": 5.0,
"maePoints": -1.25,
"mfeTicks": 20.0,
"maeTicks": -5.0,
"entryEfficiency": 0.85,
"exitEfficiency": 0.75,
"totalEfficiency": 0.80,
"commission": 1.84,
"barsInTrade": 13,
"timeInMarketSeconds": 773,
"entryName": "EntryLong",
"exitName": "ProfitTarget",
"isFlagged": false,
"flagReason": null,
"isManual": false,
"isEdited": false,
"isDeleted": false
}

Manual trades carry a slimmer set (entry/exit prices and times, profit, commission). Their tradeHash is the manual trade_id. isManual is true.

Sync behavior

sync_first:true runs the same drain the user gets from the Sync button on /user/journal:

  1. Read SysInfo from the add-on to identify the NT8 install.
  2. Query the add-on's local SQLite for new executions, orders, and balances.
  3. Pull new instrument specs.
  4. Rebuild matched trades from the new executions using FIFO.

It can take several seconds for users with many accounts or a full month of fills. If last_sync from a previous read is recent and the user is only asking about closed trades from yesterday, skip the sync and use the cached data.

Suggested patterns

# Quick read, no sync.
GetJournalTrades(limit: 50)

# Today only, one account, freshest possible.
GetJournalTrades(
sync_first: true,
account: "Sim101",
start: "2026-05-15",
limit: 200
)

# All-time count + first page for an overview, newest first.
GetJournalTrades(limit: 25, direction: "desc")

# Only trades the agent can explain (no flagged, no deleted).
GetJournalTrades(include_flagged: false, include_deleted: false)
Edits vs. originals

When the user has overridden a column on an NT8 trade, the response returns the override value, not the original. isEdited:true flags this. The unedited NT8 fields are not exposed through this tool; if an agent needs them for forensic comparison, read nt8_trades directly through admin tooling.

GetSignalHistory

Reads the webhook Signal History (a.k.a. Alert History on the user dashboard). Each row is one webhook hit: TradingView alert, custom POST, or any other configured source.

Why call this

When the user asks "did my alert fire?" or "why didn't my order go through?", GetSignalHistory returns the same rows that power the dashboard, plus a built-in diagnostic when the signal failed for a known reason.

Arguments

NameTypeDefaultNotes
offsetinteger0Pagination offset.
limitinteger50Page size. Capped at 200.
statusenum** returns all rows. success, warning, failed narrow to one outcome bucket.
searchstring(none)Substring match across signal id, multi-account, request body, and error text.
directionenumdescdesc returns newest signals first.
include_helpbooleantrueAttach a full help block (title, causes, fixes) when the signal's error or warning matches a known pattern. Set false to skip the help payload and only get a help_key reference.

Response

{
"success": true,
"signals": [
{
"id": "sig_…",
"time": "2026-05-15T13:55:04Z",
"epoch": 1747318504,
"command": "place",
"account": "Sim101",
"symbol": "MES",
"status": "warning",
"valid": true,
"ack": true,
"error": null,
"warning": "instrument_rolled",
"exectime_ms": 412,
"multiacct": null,
"source": "192.0.2.10",
"help_key": "instrument_rolled",
"help": {
"title": "Instrument rolled to next contract",
"causes": [ "Front-month contract expired between alert authoring and fire time." ],
"fixes": [ "Use a continuous-contract root (e.g. MES) in your alert and let CrossTrade resolve the active contract.", "Update the hard-coded contract in the alert payload." ]
}
}
],
"count": 1,
"offset": 0,
"limit": 50
}
  • help_key is set whenever the row matched a known failure pattern. The corresponding help block is only attached when include_help:true.
  • multiacct is the comma-separated account list when the original signal targeted multiple accounts, otherwise null.

Suggested patterns

# Why didn't anything fire in the last 30 minutes?
GetSignalHistory(status: "failed", limit: 25)

# Find the exact signal the user is talking about.
GetSignalHistory(search: "MNQ", limit: 10)

# Skip the help payload when paginating many pages.
GetSignalHistory(include_help: false, limit: 200, offset: 200)

GetActivityLog

Reads the CrossTrade XT Add-On Activity Log straight from the add-on's local SQLite logs table. Useful for time-correlating signal failures with what the add-on saw locally (connection drops, sync events, bracket placements, kill-switch trips).

Why call this

The Activity Log is the add-on's own narrative of what happened on the user's machine. It captures things that never reach the API server: NT8 disconnects, local order acknowledgements, instrument rolls, internal kill-switch trips, and add-on-side warnings that explain why an API call might have looked like it succeeded but produced no trade.

Arguments

NameTypeDefaultNotes
limitinteger100Max log rows. Range 1 to 500.
lookback_hoursinteger24How far back to look. Range 1 to 168 (one week).
levelsarray["Info","Warning","Hidden"]Subset of levels to return. The server-side allowlist is exactly these three.

Level visibility

The user-visible levels are Info, Warning, and Hidden. Error and Debug are admin-only and are silently dropped if requested. This matches the visibility split inside the add-on: end users see normal operational events and surfaceable warnings, but raw exception logs are reserved for support diagnostics.

When the agent passes levels:["Error"], the response is an empty logs array with a detail string explaining the allowlist.

Response

{
"success": true,
"logs": [
{
"timestamp": "2026-05-15 13:55:04.812",
"epoch": 1747318504,
"level": "Info",
"message": "[MES 06-26] Bracket placed (target 5318.00 / stop 5311.50) for Sim101"
}
],
"count": 1,
"lookback_hours": 24,
"levels": ["Info", "Warning", "Hidden"]
}

Add-on required

This tool requires the user's NT8 to be running and connected to the CrossTrade add-on. When the add-on is offline:

{ "success": false, "error": "addon_not_connected",
"detail": "Start NinjaTrader with the CrossTrade add-on." }

When the add-on is reachable but the query fails or times out (30s):

{ "success": false, "error": "addon_query_failed", "detail": "…" }
{ "success": false, "error": "timeout", "detail": "Add-on did not respond within 30 seconds." }

Suggested patterns

# What's been happening in the last hour?
GetActivityLog(limit: 200, lookback_hours: 1)

# Just the warnings from the last day.
GetActivityLog(levels: ["Warning"], lookback_hours: 24)

# Wide window for a post-mortem.
GetActivityLog(limit: 500, lookback_hours: 168)

Correlating Across Tools

A common forensic pattern for an AI trading agent:

GetSignalHistory(status: "failed", limit: 25) // which signals failed?
GetActivityLog(lookback_hours: 6, limit: 200) // what did the add-on see?
GetJournalTrades(sync_first: true, start: "<earliest signal time>")

The signal history gives the agent the user-facing event timeline. The activity log gives the add-on's view of the same window. The journal tells the agent which signals actually produced trades. Crossing all three lets the agent explain the gap when a signal landed but no trade appeared (e.g. signal arrived, add-on rejected for an open opposing position, no trade reached the journal).

Read-only by design

None of these three tools mutate state. They are safe to call repeatedly during a diagnostic conversation. Treat GetJournalTrades(sync_first:true) as the one that does real work: it talks to the add-on and writes to the journal tables. The other two are pure reads.