tradelab 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +112 -0
- package/README.md +188 -328
- package/bin/tradelab-mcp.js +7 -0
- package/bin/tradelab.js +29 -0
- package/dist/cjs/data.cjs +149 -26
- package/dist/cjs/index.cjs +1917 -1005
- package/dist/cjs/live.cjs +536 -25
- package/dist/cjs/ta.cjs +339 -0
- package/docs/README.md +32 -66
- package/docs/api-reference.md +283 -112
- package/docs/backtest-engine.md +210 -252
- package/docs/data-reporting-cli.md +114 -156
- package/docs/examples.md +6 -6
- package/docs/live-trading.md +263 -92
- package/docs/mcp.md +285 -0
- package/docs/research.md +157 -0
- package/examples/liveDashboard.js +33 -0
- package/examples/llmSignal.js +33 -0
- package/examples/mcpLiveTrading.js +77 -0
- package/examples/optimize.js +25 -0
- package/package.json +26 -4
- package/src/engine/asyncSignal.js +28 -0
- package/src/engine/backtest.js +13 -1
- package/src/engine/backtestAsync.js +27 -0
- package/src/engine/backtestTicks.js +13 -2
- package/src/engine/barSystemRunner.js +96 -41
- package/src/engine/execution.js +39 -0
- package/src/engine/grid.js +15 -0
- package/src/engine/llmSignal.js +84 -0
- package/src/engine/optimize.js +110 -0
- package/src/engine/optimizeWorker.js +67 -0
- package/src/engine/portfolio.js +4 -1
- package/src/engine/walkForward.js +1 -0
- package/src/index.js +9 -0
- package/src/live/dashboard/server.js +179 -0
- package/src/live/engine/liveEngine.js +2 -2
- package/src/live/engine/paperEngine.js +5 -0
- package/src/live/index.js +3 -0
- package/src/live/session.js +402 -0
- package/src/mcp/liveTools.js +179 -0
- package/src/mcp/schemas.js +167 -0
- package/src/mcp/server.js +35 -0
- package/src/mcp/tools.js +265 -0
- package/src/metrics/annualize.js +32 -0
- package/src/metrics/benchmark.js +55 -0
- package/src/metrics/buildMetrics.js +34 -13
- package/src/metrics/finite.js +17 -0
- package/src/research/combinations.js +18 -0
- package/src/research/cpcv.js +47 -0
- package/src/research/deflatedSharpe.js +35 -0
- package/src/research/index.js +6 -0
- package/src/research/monteCarlo.js +88 -0
- package/src/research/pbo.js +69 -0
- package/src/research/stats.js +78 -0
- package/src/strategies/builtins.js +96 -0
- package/src/strategies/index.js +30 -0
- package/src/ta/channels.js +67 -0
- package/src/ta/index.js +16 -0
- package/src/ta/oscillators.js +70 -0
- package/src/ta/trend.js +78 -0
- package/src/utils/random.js +33 -0
- package/templates/dashboard.html +661 -0
- package/types/index.d.ts +179 -0
- package/types/live.d.ts +114 -0
- package/types/mcp.d.ts +17 -0
- package/types/ta.d.ts +45 -0
package/docs/live-trading.md
CHANGED
|
@@ -1,54 +1,42 @@
|
|
|
1
|
-
# Live trading
|
|
1
|
+
# Live and paper trading
|
|
2
2
|
|
|
3
|
-
<small>[Back to
|
|
3
|
+
<small>[Back to docs](README.md)</small>
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Use `tradelab/live` when you want the same strategy contract from `backtest()` to run against a paper broker or a broker adapter.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The live module is intentionally small:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- persist state with `JsonFileStorage` for restart safety
|
|
15
|
-
|
|
16
|
-
Import path:
|
|
9
|
+
- a signal receives finalized candles and returns the same order intent used in backtests
|
|
10
|
+
- a broker adapter handles account, order, fill, and position operations
|
|
11
|
+
- a feed provides bars or ticks
|
|
12
|
+
- storage persists state so a process restart can recover cleanly
|
|
13
|
+
- risk controls can block new orders or halt a system
|
|
17
14
|
|
|
18
15
|
```js
|
|
19
|
-
import { LiveEngine,
|
|
16
|
+
import { LiveEngine, PaperEngine, JsonFileStorage } from "tradelab/live";
|
|
20
17
|
```
|
|
21
18
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
| Component | Purpose |
|
|
25
|
-
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
26
|
-
| `LiveEngine` | Single-system live or paper execution loop |
|
|
27
|
-
| `LiveOrchestrator` | Multi-system live execution with shared broker and aggregated status |
|
|
28
|
-
| `PaperEngine` | In-process broker simulator implementing the broker adapter contract |
|
|
29
|
-
| `AlpacaBroker` / `BinanceBroker` / `CoinbaseBroker` / `InteractiveBrokersBroker` | Real broker adapters |
|
|
30
|
-
| `BrokerFeed` / `PollingFeed` | Feed adapters for streaming or polling operation |
|
|
31
|
-
| `RiskManager` | Session windows, daily loss gates, drawdown halts, position checks |
|
|
32
|
-
| `StateManager` / `JsonFileStorage` | Persisted state, trades, and equity curve |
|
|
33
|
-
| `EventBus` / `LiveLogger` | Event fanout and structured logging |
|
|
34
|
-
|
|
35
|
-
## `LiveEngine` quick start
|
|
19
|
+
## Start With Paper Mode
|
|
36
20
|
|
|
37
21
|
```js
|
|
38
22
|
import { LiveEngine, PaperEngine, JsonFileStorage } from "tradelab/live";
|
|
39
23
|
|
|
24
|
+
const broker = new PaperEngine({ equity: 25_000 });
|
|
25
|
+
|
|
40
26
|
const engine = new LiveEngine({
|
|
41
|
-
id: "aapl-
|
|
27
|
+
id: "aapl-paper",
|
|
42
28
|
symbol: "AAPL",
|
|
43
29
|
interval: "1m",
|
|
44
|
-
broker
|
|
30
|
+
broker,
|
|
45
31
|
storage: new JsonFileStorage({ baseDir: "./output/live-state" }),
|
|
32
|
+
mode: "polling",
|
|
46
33
|
riskPct: 1,
|
|
47
|
-
mode: "streaming",
|
|
48
34
|
signal({ bar, openPosition }) {
|
|
49
35
|
if (openPosition) return null;
|
|
36
|
+
|
|
50
37
|
return {
|
|
51
38
|
side: "long",
|
|
39
|
+
entry: bar.close,
|
|
52
40
|
stop: bar.close - 1,
|
|
53
41
|
rr: 2,
|
|
54
42
|
};
|
|
@@ -56,71 +44,136 @@ const engine = new LiveEngine({
|
|
|
56
44
|
});
|
|
57
45
|
|
|
58
46
|
await engine.start();
|
|
59
|
-
|
|
47
|
+
await engine.pollOnce();
|
|
48
|
+
console.log(engine.getStatus());
|
|
60
49
|
await engine.stop();
|
|
61
50
|
```
|
|
62
51
|
|
|
63
|
-
|
|
52
|
+
`PaperEngine` implements the broker interface in memory. Use it first for CLI runs, dashboard checks, and strategy wiring.
|
|
53
|
+
|
|
54
|
+
## Signal Contract
|
|
55
|
+
|
|
56
|
+
The signal function receives:
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
| Field | Meaning |
|
|
59
|
+
| -------------- | ---------------------------------------------- |
|
|
60
|
+
| `candles` | Finalized candle history available at this bar |
|
|
61
|
+
| `index` | Current candle index |
|
|
62
|
+
| `bar` | Current candle |
|
|
63
|
+
| `equity` | Current engine equity |
|
|
64
|
+
| `openPosition` | Current open position, or `null` |
|
|
65
|
+
| `pendingOrder` | Current pending entry order, or `null` |
|
|
69
66
|
|
|
70
|
-
|
|
67
|
+
Return `null` to do nothing. Return an order intent to open a trade:
|
|
71
68
|
|
|
72
69
|
```js
|
|
73
|
-
|
|
70
|
+
return {
|
|
71
|
+
side: "long",
|
|
72
|
+
entry: bar.close,
|
|
73
|
+
stop: bar.close * 0.98,
|
|
74
|
+
rr: 2,
|
|
75
|
+
riskPct: 0.5,
|
|
76
|
+
};
|
|
77
|
+
```
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
broker: new PaperEngine({ equity: 100_000 }),
|
|
77
|
-
storage: new JsonFileStorage({ baseDir: "./output/live-state" }),
|
|
78
|
-
allocation: "weight",
|
|
79
|
-
systems: [
|
|
80
|
-
{ id: "spy", symbol: "SPY", interval: "1m", weight: 2, signal: signalA },
|
|
81
|
-
{ id: "qqq", symbol: "QQQ", interval: "1m", weight: 1, signal: signalB },
|
|
82
|
-
],
|
|
83
|
-
});
|
|
79
|
+
Useful fields:
|
|
84
80
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
| Field | Meaning |
|
|
82
|
+
| -------------------- | -------------------------------------------- |
|
|
83
|
+
| `side` | `"long"`, `"short"`, `"buy"`, or `"sell"` |
|
|
84
|
+
| `entry` | Planned entry price |
|
|
85
|
+
| `stop` | Stop loss price |
|
|
86
|
+
| `takeProfit` or `rr` | Explicit target, or reward/risk multiple |
|
|
87
|
+
| `qty` | Fixed quantity. If omitted, sizing uses risk |
|
|
88
|
+
| `riskPct` | Percent of equity to risk on this trade |
|
|
89
|
+
| `_maxBarsInTrade` | Force an exit after this many completed bars |
|
|
90
|
+
| `_maxHoldMin` | Force an exit after this many minutes |
|
|
91
|
+
|
|
92
|
+
Use `backtest()` or `backtestAsync()` with the same signal before connecting a real broker.
|
|
93
|
+
|
|
94
|
+
## Run From The CLI
|
|
95
|
+
|
|
96
|
+
The CLI has two entry points:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
tradelab paper --symbol AAPL --interval 1m --mode polling --once true
|
|
100
|
+
tradelab live --symbol AAPL --interval 1m --broker alpaca --paper
|
|
88
101
|
```
|
|
89
102
|
|
|
90
|
-
|
|
103
|
+
Common options:
|
|
104
|
+
|
|
105
|
+
| Option | Meaning |
|
|
106
|
+
| ----------------- | --------------------------------------------- |
|
|
107
|
+
| `--strategy` | Built-in strategy name or local strategy file |
|
|
108
|
+
| `--symbol` | Symbol passed to broker and feed |
|
|
109
|
+
| `--interval` | Candle interval, such as `1m`, `5m`, or `1d` |
|
|
110
|
+
| `--mode` | `streaming` or `polling` |
|
|
111
|
+
| `--once true` | Run one polling cycle and exit |
|
|
112
|
+
| `--stateDir` | Directory for persisted live state |
|
|
113
|
+
| `--dashboard` | Start the local dashboard |
|
|
114
|
+
| `--dashboardPort` | Dashboard port. Defaults to `4317` |
|
|
91
115
|
|
|
92
|
-
|
|
116
|
+
Strategy modules can export `default`, `createSignal(args)`, or `signal`:
|
|
93
117
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
118
|
+
```js
|
|
119
|
+
// ./strategies/ema-signal.js
|
|
120
|
+
import { ema } from "tradelab";
|
|
121
|
+
|
|
122
|
+
export function createSignal({ fast = 10, slow = 30, rr = 2 } = {}) {
|
|
123
|
+
return ({ candles, bar }) => {
|
|
124
|
+
if (candles.length < slow + 2) return null;
|
|
125
|
+
|
|
126
|
+
const closes = candles.map((candle) => candle.close);
|
|
127
|
+
const fastLine = ema(closes, Number(fast));
|
|
128
|
+
const slowLine = ema(closes, Number(slow));
|
|
129
|
+
const last = closes.length - 1;
|
|
130
|
+
|
|
131
|
+
if (fastLine[last - 1] <= slowLine[last - 1] && fastLine[last] > slowLine[last]) {
|
|
132
|
+
return {
|
|
133
|
+
side: "long",
|
|
134
|
+
entry: bar.close,
|
|
135
|
+
stop: Math.min(...candles.slice(-15).map((candle) => candle.low)),
|
|
136
|
+
rr: Number(rr),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
99
139
|
|
|
100
|
-
|
|
140
|
+
return null;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
```
|
|
101
144
|
|
|
102
145
|
```bash
|
|
103
146
|
tradelab paper \
|
|
104
|
-
--
|
|
147
|
+
--strategy ./strategies/ema-signal.js \
|
|
105
148
|
--symbol AAPL \
|
|
106
149
|
--interval 1m \
|
|
107
150
|
--mode polling \
|
|
108
|
-
--once true
|
|
109
|
-
--stateDir ./output/live-state
|
|
151
|
+
--once true
|
|
110
152
|
```
|
|
111
153
|
|
|
112
|
-
|
|
154
|
+
## Run Multiple Systems
|
|
113
155
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
156
|
+
Use `LiveOrchestrator` when several systems share one account and broker.
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
import { LiveOrchestrator, PaperEngine, JsonFileStorage } from "tradelab/live";
|
|
160
|
+
|
|
161
|
+
const orchestrator = new LiveOrchestrator({
|
|
162
|
+
broker: new PaperEngine({ equity: 100_000 }),
|
|
163
|
+
storage: new JsonFileStorage({ baseDir: "./output/live-state" }),
|
|
164
|
+
allocation: "weight",
|
|
165
|
+
systems: [
|
|
166
|
+
{ id: "spy", symbol: "SPY", interval: "1m", weight: 2, signal: spySignal },
|
|
167
|
+
{ id: "qqq", symbol: "QQQ", interval: "1m", weight: 1, signal: qqqSignal },
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
await orchestrator.start();
|
|
172
|
+
console.log(orchestrator.getStatus());
|
|
173
|
+
await orchestrator.stop();
|
|
121
174
|
```
|
|
122
175
|
|
|
123
|
-
|
|
176
|
+
CLI config:
|
|
124
177
|
|
|
125
178
|
```json
|
|
126
179
|
{
|
|
@@ -131,56 +184,174 @@ Example config:
|
|
|
131
184
|
"id": "spy-system",
|
|
132
185
|
"symbol": "SPY",
|
|
133
186
|
"interval": "1m",
|
|
134
|
-
"strategy": "./strategies/
|
|
187
|
+
"strategy": "./strategies/spy.js",
|
|
135
188
|
"weight": 2
|
|
136
189
|
},
|
|
137
190
|
{
|
|
138
191
|
"id": "qqq-system",
|
|
139
192
|
"symbol": "QQQ",
|
|
140
193
|
"interval": "1m",
|
|
141
|
-
"strategy": "./strategies/
|
|
194
|
+
"strategy": "./strategies/qqq.js",
|
|
142
195
|
"weight": 1
|
|
143
196
|
}
|
|
144
197
|
]
|
|
145
198
|
}
|
|
146
199
|
```
|
|
147
200
|
|
|
148
|
-
|
|
201
|
+
```bash
|
|
202
|
+
tradelab live --config ./live-portfolio.json --paper --mode polling --once true
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Dashboard
|
|
206
|
+
|
|
207
|
+
Start a local dashboard for an engine or orchestrator:
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
import { createDashboardServer } from "tradelab/live";
|
|
211
|
+
|
|
212
|
+
const dashboard = createDashboardServer({ source: engine, port: 4317 });
|
|
213
|
+
const url = await dashboard.start();
|
|
214
|
+
|
|
215
|
+
console.log(url);
|
|
216
|
+
|
|
217
|
+
// On shutdown:
|
|
218
|
+
await dashboard.close();
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The dashboard is a zero-dependency dark trading cockpit served from a single HTML file. It includes:
|
|
222
|
+
|
|
223
|
+
- **KPI strip** - equity, day P&L (with percent), open position, last price, all in monospace tabular numerals
|
|
224
|
+
- **Equity curve** - canvas chart that grows in real time; green when above session start, red when below
|
|
225
|
+
- **Positions table** - symbol, side badge, qty, entry, mark, unrealized P&L
|
|
226
|
+
- **Open orders table** - type, side, qty, price, with inline cancel
|
|
227
|
+
- **Event feed** - color-coded by severity (fill, exit/warning, reject) with animated entry; capped at 120 rows
|
|
228
|
+
- **Risk-halt banner** - shown when `source.getStatus().risk.halted` is true
|
|
229
|
+
- **Controls** - Stop and Flatten All buttons in the header; cancel links in the orders table
|
|
230
|
+
|
|
231
|
+
The dashboard exposes:
|
|
232
|
+
|
|
233
|
+
| Route | Method | Purpose |
|
|
234
|
+
| ---------- | ------ | ------------------------------------------------------------- |
|
|
235
|
+
| `/` | GET | Static dashboard page |
|
|
236
|
+
| `/state` | GET | Calls optional `source.refresh()`, then returns `getStatus()` |
|
|
237
|
+
| `/events` | GET | Server-Sent Events stream from `eventBus` |
|
|
238
|
+
| `/command` | POST | Dispatch a command to the source (whitelist enforced) |
|
|
239
|
+
|
|
240
|
+
### `/command` endpoint
|
|
241
|
+
|
|
242
|
+
Send a JSON body with a `type` field. Only the following types are accepted; anything else returns `400`:
|
|
243
|
+
|
|
244
|
+
| type | Source method called |
|
|
245
|
+
| --------------- | ---------------------------------- |
|
|
246
|
+
| `flatten` | `source.flatten()` |
|
|
247
|
+
| `stop` | `source.stop()` |
|
|
248
|
+
| `closePosition` | `source.closePosition(cmd.symbol)` |
|
|
249
|
+
| `cancelOrder` | `source.cancelOrder(cmd.orderId)` |
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
// Example: flatten all positions from a browser
|
|
253
|
+
await fetch("/command", {
|
|
254
|
+
method: "POST",
|
|
255
|
+
headers: { "Content-Type": "application/json" },
|
|
256
|
+
body: JSON.stringify({ type: "flatten" }),
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### `source.refresh()`
|
|
261
|
+
|
|
262
|
+
If the source object exposes a `refresh()` method, the dashboard awaits it before each `/state` response. Use this to pull fresh data from a broker before painting the UI - for example, a `TradingSession` from the MCP live tools can expose `refresh()` to sync account state before every poll.
|
|
263
|
+
|
|
264
|
+
CLI:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
tradelab paper --symbol AAPL --interval 1m --mode polling --dashboard --dashboardPort 4317
|
|
268
|
+
tradelab live --config ./live-portfolio.json --paper --dashboard --dashboardPort 4317
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
New browser clients receive a bounded replay of recent events. The equity curve grows from the first data point seen in the session; the chart updates on every `equity:update` SSE event and on each `/state` poll.
|
|
272
|
+
|
|
273
|
+
## State And Recovery
|
|
274
|
+
|
|
275
|
+
`JsonFileStorage` stores one namespace per engine id:
|
|
276
|
+
|
|
277
|
+
| File | Contents |
|
|
278
|
+
| -------------- | ----------------------------------------- |
|
|
279
|
+
| `state.json` | Latest open position, pending order, risk |
|
|
280
|
+
| `trades.jsonl` | Append-only completed trade records |
|
|
281
|
+
| `equity.jsonl` | Append-only equity snapshots |
|
|
282
|
+
|
|
283
|
+
Inspect persisted state:
|
|
149
284
|
|
|
150
285
|
```bash
|
|
151
286
|
tradelab status --dir ./output/live-state
|
|
152
287
|
tradelab status --dir ./output/live-state --namespace spy-system
|
|
153
288
|
```
|
|
154
289
|
|
|
155
|
-
|
|
290
|
+
On restart, `StateManager` compares persisted state with broker positions and reports whether the state is clean, externally closed, adopted from broker state, or mismatched.
|
|
156
291
|
|
|
157
|
-
|
|
292
|
+
## Risk Controls
|
|
158
293
|
|
|
159
|
-
- `
|
|
160
|
-
- `trades.jsonl` (append-only)
|
|
161
|
-
- `equity.jsonl` (append-only)
|
|
294
|
+
Pass top-level risk options to `LiveEngine`, or group them under `risk`.
|
|
162
295
|
|
|
163
|
-
|
|
296
|
+
```js
|
|
297
|
+
const engine = new LiveEngine({
|
|
298
|
+
symbol: "AAPL",
|
|
299
|
+
interval: "1m",
|
|
300
|
+
broker,
|
|
301
|
+
signal,
|
|
302
|
+
riskPct: 0.5,
|
|
303
|
+
risk: {
|
|
304
|
+
maxDailyLossPct: 2,
|
|
305
|
+
maxDrawdownPct: 10,
|
|
306
|
+
maxPositions: 1,
|
|
307
|
+
maxDailyTrades: 4,
|
|
308
|
+
allowedWindows: "09:30-11:30,13:00-15:45",
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The risk manager can block new positions and emit warning or halt events. It does not silently change your signal logic.
|
|
164
314
|
|
|
165
|
-
## Broker
|
|
315
|
+
## Broker Notes
|
|
166
316
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
317
|
+
| Broker | Notes |
|
|
318
|
+
| -------------------------- | -------------------------------------------------------- |
|
|
319
|
+
| `PaperEngine` | Local simulation. Best first step for any new strategy |
|
|
320
|
+
| `AlpacaBroker` | Supports native paper mode through broker config |
|
|
321
|
+
| `BinanceBroker` | Supports exchange-style crypto workflows |
|
|
322
|
+
| `CoinbaseBroker` | Live API adapter. Use `PaperEngine` for local simulation |
|
|
323
|
+
| `InteractiveBrokersBroker` | Requires `@stoqey/ib` in the consuming application |
|
|
170
324
|
|
|
171
|
-
|
|
325
|
+
Real broker adapters require credentials and broker-specific account permissions. Start with paper mode, then use the smallest possible order sizes when switching to live credentials.
|
|
172
326
|
|
|
173
|
-
##
|
|
327
|
+
## Events
|
|
174
328
|
|
|
175
|
-
`EventBus` emits lifecycle and
|
|
329
|
+
`EventBus` emits lifecycle, order, position, equity, and risk events:
|
|
176
330
|
|
|
177
|
-
- `connected
|
|
331
|
+
- `connected`
|
|
332
|
+
- `shutdown`
|
|
178
333
|
- `signal`
|
|
179
|
-
- `order:submitted
|
|
180
|
-
- `
|
|
334
|
+
- `order:submitted`
|
|
335
|
+
- `order:filled`
|
|
336
|
+
- `order:rejected`
|
|
337
|
+
- `order:canceled`
|
|
338
|
+
- `position:opened`
|
|
339
|
+
- `position:closed`
|
|
181
340
|
- `equity:update`
|
|
182
|
-
- `risk:warning
|
|
341
|
+
- `risk:warning`
|
|
342
|
+
- `risk:halt`
|
|
343
|
+
|
|
344
|
+
Attach `LiveLogger` to write structured JSON event logs.
|
|
345
|
+
|
|
346
|
+
```js
|
|
347
|
+
import { createEventBus, createLogger } from "tradelab/live";
|
|
348
|
+
|
|
349
|
+
const eventBus = createEventBus();
|
|
350
|
+
const logger = createLogger({ level: "info" });
|
|
351
|
+
|
|
352
|
+
logger.attach(eventBus);
|
|
353
|
+
```
|
|
183
354
|
|
|
184
|
-
|
|
355
|
+
See [api-reference.md](api-reference.md#live-module-tradelablive) for the full live export list.
|
|
185
356
|
|
|
186
|
-
<small>[Back to
|
|
357
|
+
<small>[Back to docs](README.md)</small>
|