@forgecharts/sdk 1.3.9 → 1.3.10

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.
@@ -0,0 +1,1051 @@
1
+ // src/server/tools.ts
2
+ function getAgentTools() {
3
+ return [
4
+ // ─── Chart Navigation ──────────────────────────────────────────────
5
+ {
6
+ name: "chart_get_state",
7
+ description: "Get the current state of the chart including symbol, timeframe, type, and visible range.",
8
+ input_schema: {
9
+ type: "object",
10
+ properties: {},
11
+ required: []
12
+ }
13
+ },
14
+ {
15
+ name: "chart_set_symbol",
16
+ description: 'Change the currently displayed symbol (ticker). For futures, use continuous notation (e.g., "CME:ES1!") or specific contracts (e.g., "CME:ESM2026"). Always include the exchange prefix for futures.',
17
+ input_schema: {
18
+ type: "object",
19
+ properties: {
20
+ symbol: {
21
+ type: "string",
22
+ description: 'Symbol to display. Examples: "AAPL", "BTCUSD", "CME:ES1!" (continuous), "CME:ESM2026" (specific month)'
23
+ }
24
+ },
25
+ required: ["symbol"]
26
+ }
27
+ },
28
+ {
29
+ name: "contract_get_info",
30
+ description: "Get futures contract information for a symbol: whether it is continuous or specific, the current front-month contract, and a list of upcoming available contracts. Call this BEFORE switching futures symbols to help the user choose between continuous and specific contracts.",
31
+ input_schema: {
32
+ type: "object",
33
+ properties: {
34
+ symbol: {
35
+ type: "string",
36
+ description: 'Futures symbol to look up (e.g., "ES", "ES1!", "CME:ESM2026", "CL"). Defaults to the current chart symbol if omitted.'
37
+ }
38
+ },
39
+ required: []
40
+ }
41
+ },
42
+ {
43
+ name: "chart_set_timeframe",
44
+ description: 'Change the chart timeframe (e.g., "1m", "5m", "1h", "1d").',
45
+ input_schema: {
46
+ type: "object",
47
+ properties: {
48
+ timeframe: { type: "string", description: "Timeframe identifier (1m, 5m, 15m, 1h, 4h, 1d, 1w, etc.)" }
49
+ },
50
+ required: ["timeframe"]
51
+ }
52
+ },
53
+ {
54
+ name: "chart_set_type",
55
+ description: "Change the chart type (candlestick, line, area, bar, etc.).",
56
+ input_schema: {
57
+ type: "object",
58
+ properties: {
59
+ type: { type: "string", description: "Chart type (candlestick, line, area, bar, renko, etc.)" }
60
+ },
61
+ required: ["type"]
62
+ }
63
+ },
64
+ {
65
+ name: "chart_scroll_to_date",
66
+ description: "Scroll the chart to a specific date/timestamp.",
67
+ input_schema: {
68
+ type: "object",
69
+ properties: {
70
+ timestamp: { type: "number", description: "Unix timestamp (seconds) to scroll to" }
71
+ },
72
+ required: ["timestamp"]
73
+ }
74
+ },
75
+ {
76
+ name: "chart_set_visible_range",
77
+ description: "Set the visible price range (Y-axis) on the chart.",
78
+ input_schema: {
79
+ type: "object",
80
+ properties: {
81
+ minPrice: { type: "number", description: "Minimum price to display" },
82
+ maxPrice: { type: "number", description: "Maximum price to display" }
83
+ },
84
+ required: ["minPrice", "maxPrice"]
85
+ }
86
+ },
87
+ // ─── Market Data ──────────────────────────────────────────────────
88
+ {
89
+ name: "data_get_bars",
90
+ description: "Fetch OHLCV bars for analysis. CRITICAL: Always call this BEFORE placing drawings to find exact price levels.",
91
+ input_schema: {
92
+ type: "object",
93
+ properties: {
94
+ symbol: { type: "string", description: "Symbol to fetch bars for" },
95
+ timeframe: { type: "string", description: "Timeframe (1m, 5m, 1h, 1d, etc.)" },
96
+ limit: {
97
+ type: "number",
98
+ description: "Number of bars to fetch (default 100)"
99
+ },
100
+ from: {
101
+ type: "number",
102
+ description: "Unix timestamp (seconds) to start from (optional)"
103
+ },
104
+ to: {
105
+ type: "number",
106
+ description: "Unix timestamp (seconds) to end at (optional)"
107
+ }
108
+ },
109
+ required: ["symbol", "timeframe"]
110
+ }
111
+ },
112
+ {
113
+ name: "data_get_realtime_quote",
114
+ description: "Get the current bid/ask/last price for a symbol.",
115
+ input_schema: {
116
+ type: "object",
117
+ properties: {
118
+ symbol: { type: "string", description: "Symbol to fetch quote for" }
119
+ },
120
+ required: ["symbol"]
121
+ }
122
+ },
123
+ {
124
+ name: "data_get_session_ohlc",
125
+ description: "Get current session OHLC stats (open, high, low, last, settlement) for a symbol.",
126
+ input_schema: {
127
+ type: "object",
128
+ properties: {
129
+ symbol: { type: "string", description: "Symbol to fetch session OHLC for (optional, uses current chart symbol if not provided)" }
130
+ },
131
+ required: []
132
+ }
133
+ },
134
+ // ─── Indicators ───────────────────────────────────────────────────
135
+ {
136
+ name: "indicator_list",
137
+ description: "List all indicators currently applied to the chart.",
138
+ input_schema: {
139
+ type: "object",
140
+ properties: {},
141
+ required: []
142
+ }
143
+ },
144
+ {
145
+ name: "indicator_add",
146
+ description: 'Add a built-in technical indicator to the chart. ALWAYS prefer this over ForgeScript for standard indicators. Built-in types: "sma", "ema", "wma", "rsi", "macd", "bbands" (Bollinger Bands), "stochastic", "atr", "obv", "volume". Overlay indicators (sma, ema, wma, bbands) render on the price chart; non-overlay indicators (rsi, macd, stochastic, atr, obv, volume) get their own sub-pane. Built-in indicators include a full settings dialog (gear icon) for users to adjust parameters.',
147
+ input_schema: {
148
+ type: "object",
149
+ properties: {
150
+ type: { type: "string", description: "Indicator type in lowercase: ema, sma, wma, rsi, macd, bbands, stochastic, atr, obv, volume" },
151
+ params: {
152
+ type: "object",
153
+ description: 'Indicator parameters. Examples: {"period": 50} for EMA/SMA, {"period":20,"maType":"sma","source":"close","multiplier":2,"offset":0} for bbands (maType: "sma"|"ema"|"wma"; source: "close"|"open"|"high"|"low"|"hl2"|"hlc3"|"ohlc4"), {"fast":12,"slow":26,"signal":9} for MACD'
154
+ },
155
+ overlay: {
156
+ type: "boolean",
157
+ description: "Whether to render on the price chart (true) or in a separate sub-pane (false). Defaults are automatic per type \u2014 only set this to override the default placement."
158
+ },
159
+ visible: {
160
+ type: "boolean",
161
+ description: "Whether the indicator should be visible (default true)"
162
+ },
163
+ style: {
164
+ type: "object",
165
+ description: 'Optional visual style overrides. Keys: line1, line2, line3 (each with color, width, lineStyle: "solid"|"dashed"|"dotted", visible), fill1 (color, opacity 0-1, visible). Example for bbands: {"line1":{"color":"#ff0000"},"line2":{"color":"#00ff00","width":2},"fill1":{"opacity":0.15}}'
166
+ }
167
+ },
168
+ required: ["type"]
169
+ }
170
+ },
171
+ {
172
+ name: "indicator_remove",
173
+ description: "Remove an indicator from the chart.",
174
+ input_schema: {
175
+ type: "object",
176
+ properties: {
177
+ indicatorId: { type: "string", description: "ID of the indicator to remove" }
178
+ },
179
+ required: ["indicatorId"]
180
+ }
181
+ },
182
+ {
183
+ name: "indicator_update_params",
184
+ description: "Update parameters of an existing indicator.",
185
+ input_schema: {
186
+ type: "object",
187
+ properties: {
188
+ indicatorId: { type: "string", description: "ID of the indicator" },
189
+ params: {
190
+ type: "object",
191
+ description: "New parameters to apply"
192
+ }
193
+ },
194
+ required: ["indicatorId", "params"]
195
+ }
196
+ },
197
+ {
198
+ name: "indicator_set_visible",
199
+ description: "Show or hide an indicator.",
200
+ input_schema: {
201
+ type: "object",
202
+ properties: {
203
+ indicatorId: { type: "string", description: "ID of the indicator" },
204
+ visible: { type: "boolean", description: "Whether to show (true) or hide (false)" }
205
+ },
206
+ required: ["indicatorId", "visible"]
207
+ }
208
+ },
209
+ {
210
+ name: "indicator_list_available",
211
+ description: "Return full registry of available indicator names and their parameter schemas.",
212
+ input_schema: {
213
+ type: "object",
214
+ properties: {},
215
+ required: []
216
+ }
217
+ },
218
+ {
219
+ name: "indicator_get_values",
220
+ description: "Return computed output series values for a running indicator.",
221
+ input_schema: {
222
+ type: "object",
223
+ properties: {
224
+ indicatorId: { type: "string", description: "ID of the indicator" },
225
+ limit: { type: "number", description: "Maximum number of values to return (default 100)" }
226
+ },
227
+ required: ["indicatorId"]
228
+ }
229
+ },
230
+ // ─── Drawings ─────────────────────────────────────────────────────
231
+ {
232
+ name: "drawing_list",
233
+ description: "List all drawings on the chart (trend lines, support/resistance, annotations).",
234
+ input_schema: {
235
+ type: "object",
236
+ properties: {},
237
+ required: []
238
+ }
239
+ },
240
+ {
241
+ name: "drawing_add",
242
+ description: "Draw on the chart (trend line, horizontal line, rectangle, text annotation, fibonacci tools, etc.). Always fetch bars first with data_get_bars to find exact price levels.",
243
+ input_schema: {
244
+ type: "object",
245
+ properties: {
246
+ type: {
247
+ type: "string",
248
+ description: 'Drawing type: "trendline", "extended_line", "ray", "horizontal_ray", "horizontal_line", "vertical_line", "fibonacci_retracement", "fibonacci_extension", "fibonacci_fan", "pitchfork", "rectangle", "channel", "text_label", "arrow_up", "arrow_down", "price_label", "gann_fan", "regression_trend"'
249
+ },
250
+ p1: {
251
+ type: "object",
252
+ description: "First anchor point {time: unix_seconds, price: number}",
253
+ properties: {
254
+ time: { type: "number", description: "Unix timestamp (seconds)" },
255
+ price: { type: "number", description: "Price level" }
256
+ }
257
+ },
258
+ p2: {
259
+ type: "object",
260
+ description: "Second anchor point (optional for single-point types) {time: unix_seconds, price: number}",
261
+ properties: {
262
+ time: { type: "number", description: "Unix timestamp (seconds)" },
263
+ price: { type: "number", description: "Price level" }
264
+ }
265
+ },
266
+ p3: {
267
+ type: "object",
268
+ description: "Third anchor point (for pitchfork, fibonacci_extension, channel) {time: unix_seconds, price: number}",
269
+ properties: {
270
+ time: { type: "number", description: "Unix timestamp (seconds)" },
271
+ price: { type: "number", description: "Price level" }
272
+ }
273
+ },
274
+ text: {
275
+ type: "string",
276
+ description: "Text content for text_label and price_label types"
277
+ },
278
+ style: {
279
+ type: "object",
280
+ description: "Drawing style properties",
281
+ properties: {
282
+ color: { type: "string", description: 'Color in hex format (e.g., "#FF0000")' },
283
+ lineWidth: { type: "number", description: "Line width in pixels" },
284
+ lineStyle: { type: "string", description: 'Line style: "solid", "dashed", or "dotted"' },
285
+ fillColor: { type: "string", description: "Fill color in hex format (optional)" },
286
+ fillOpacity: { type: "number", description: "Fill opacity from 0 to 1 (optional)" },
287
+ showLabel: { type: "boolean", description: "Whether to show the label (optional)" }
288
+ }
289
+ }
290
+ },
291
+ required: ["type", "p1"]
292
+ }
293
+ },
294
+ {
295
+ name: "drawing_remove",
296
+ description: "Remove a drawing from the chart.",
297
+ input_schema: {
298
+ type: "object",
299
+ properties: {
300
+ drawingId: { type: "string", description: "ID of the drawing to remove" }
301
+ },
302
+ required: ["drawingId"]
303
+ }
304
+ },
305
+ {
306
+ name: "drawing_update",
307
+ description: "Update properties of an existing drawing (move it, change color, etc.).",
308
+ input_schema: {
309
+ type: "object",
310
+ properties: {
311
+ drawingId: { type: "string", description: "ID of the drawing" },
312
+ points: {
313
+ type: "array",
314
+ description: "New points (optional)",
315
+ items: { type: "object" }
316
+ },
317
+ color: {
318
+ type: "string",
319
+ description: "New color in hex format (optional)"
320
+ },
321
+ label: {
322
+ type: "string",
323
+ description: "New label/text (optional)"
324
+ }
325
+ },
326
+ required: ["drawingId"]
327
+ }
328
+ },
329
+ {
330
+ name: "drawing_clear_all",
331
+ description: "DESTRUCTIVE: Remove all drawings from the chart.",
332
+ input_schema: {
333
+ type: "object",
334
+ properties: {},
335
+ required: []
336
+ }
337
+ },
338
+ {
339
+ name: "drawing_list_types",
340
+ description: "Return all supported drawing type identifiers.",
341
+ input_schema: {
342
+ type: "object",
343
+ properties: {},
344
+ required: []
345
+ }
346
+ },
347
+ // ─── ForgeScript ───────────────────────────────────────────────────
348
+ {
349
+ name: "script_list",
350
+ description: "List all ForgeScript custom indicators saved in the workspace. Returns an array of scripts with id, name, and created_at. Use the id field when calling script_delete or script_get_source.",
351
+ input_schema: {
352
+ type: "object",
353
+ properties: {},
354
+ required: []
355
+ }
356
+ },
357
+ {
358
+ name: "script_delete",
359
+ description: "Delete a ForgeScript indicator permanently. Always call script_list first to get the script id, then confirm with the user before deleting.",
360
+ input_schema: {
361
+ type: "object",
362
+ properties: {
363
+ scriptId: { type: "string", description: "UUID id of the script to delete (from script_list)" }
364
+ },
365
+ required: ["scriptId"]
366
+ }
367
+ },
368
+ {
369
+ name: "script_get_source",
370
+ description: "Get the source code of a ForgeScript indicator.",
371
+ input_schema: {
372
+ type: "object",
373
+ properties: {
374
+ scriptId: { type: "string", description: "ID of the script" }
375
+ },
376
+ required: ["scriptId"]
377
+ }
378
+ },
379
+ {
380
+ name: "script_create",
381
+ description: "Create a new ForgeScript indicator. Always validate before compiling. IMPORTANT: Every tuneable value (periods, thresholds, multipliers) MUST use input()/input.int()/input.float() with a descriptive title. Every plot colour MUST use input.color() so the user can customise it from the settings dialog. Never hard-code magic numbers or colours.",
382
+ input_schema: {
383
+ type: "object",
384
+ properties: {
385
+ name: { type: "string", description: "Name of the new script" },
386
+ source: { type: "string", description: "ForgeScript source code. Must include input() for all configurable parameters and input.color() for all plot colours." }
387
+ },
388
+ required: ["name", "source"]
389
+ }
390
+ },
391
+ {
392
+ name: "script_update_source",
393
+ description: "Update the source code of an existing ForgeScript indicator. Always validate before compiling. Ensure all tuneable values use input() and all plot colours use input.color().",
394
+ input_schema: {
395
+ type: "object",
396
+ properties: {
397
+ scriptId: { type: "string", description: "ID of the script" },
398
+ source: { type: "string", description: "New ForgeScript source code. Must include input() for all configurable parameters and input.color() for all plot colours." }
399
+ },
400
+ required: ["scriptId", "source"]
401
+ }
402
+ },
403
+ {
404
+ name: "script_validate",
405
+ description: "Validate ForgeScript source code for syntax and semantic errors. ALWAYS call this before script_compile_attach. Fix any errors and re-validate until clean.",
406
+ input_schema: {
407
+ type: "object",
408
+ properties: {
409
+ source: { type: "string", description: "ForgeScript source code to validate" }
410
+ },
411
+ required: ["source"]
412
+ }
413
+ },
414
+ {
415
+ name: "script_compile_attach",
416
+ description: "DESTRUCTIVE: Compile and attach a ForgeScript indicator to the chart. Always validate with script_validate first and fix all errors.",
417
+ input_schema: {
418
+ type: "object",
419
+ properties: {
420
+ scriptId: { type: "string", description: "ID of the script to compile and attach" }
421
+ },
422
+ required: ["scriptId"]
423
+ }
424
+ },
425
+ {
426
+ name: "script_detach",
427
+ description: "Remove a running script from the chart.",
428
+ input_schema: {
429
+ type: "object",
430
+ properties: {
431
+ scriptId: { type: "string", description: "ID of the script to detach" }
432
+ },
433
+ required: ["scriptId"]
434
+ }
435
+ },
436
+ {
437
+ name: "script_get_console",
438
+ description: "Return last N lines of script console output.",
439
+ input_schema: {
440
+ type: "object",
441
+ properties: {
442
+ scriptId: { type: "string", description: "ID of the script" },
443
+ limit: { type: "number", description: "Number of lines to return (default 50)" }
444
+ },
445
+ required: ["scriptId"]
446
+ }
447
+ },
448
+ // ─── Datasets ─────────────────────────────────────────────────────
449
+ {
450
+ name: "script_dataset_create",
451
+ description: 'Create a named dataset (table) for a script. The dataset defines typed columns that the user can later import data into. The implicit first column is always "time" (timestamptz). Use lowercase snake_case for dataset and column names.',
452
+ input_schema: {
453
+ type: "object",
454
+ properties: {
455
+ scriptId: { type: "string", description: "ID of the parent script" },
456
+ name: { type: "string", description: "Dataset name (lowercase snake_case, 1-64 chars)" },
457
+ columns: {
458
+ type: "array",
459
+ description: "Column definitions. Types: number, integer, text, boolean, date, timestamp, percentage, price.",
460
+ items: {
461
+ type: "object",
462
+ properties: {
463
+ name: { type: "string", description: "Column name (lowercase snake_case)" },
464
+ type: { type: "string", description: "Column type: number | integer | text | boolean | date | timestamp | percentage | price" },
465
+ nullable: { type: "boolean", description: "Whether the column allows null values (default true)" }
466
+ },
467
+ required: ["name", "type"]
468
+ }
469
+ }
470
+ },
471
+ required: ["scriptId", "name", "columns"]
472
+ }
473
+ },
474
+ {
475
+ name: "script_dataset_list",
476
+ description: "List all datasets defined for a script, including their columns, row counts, and ingest status.",
477
+ input_schema: {
478
+ type: "object",
479
+ properties: {
480
+ scriptId: { type: "string", description: "ID of the script" }
481
+ },
482
+ required: ["scriptId"]
483
+ }
484
+ },
485
+ // ─── Documentation ──────────────────────────────────────────────────
486
+ {
487
+ name: "script_docs_write",
488
+ description: "Write or update the Markdown documentation for a script. Include a summary of what the indicator does, how to use it, parameter descriptions, and any relevant notes. Called AFTER script_create and script_compile_attach to document the finished indicator.",
489
+ input_schema: {
490
+ type: "object",
491
+ properties: {
492
+ scriptId: { type: "string", description: "ID of the script" },
493
+ content: { type: "string", description: "Markdown documentation content. Should include: summary, description of what the indicator calculates, parameter table, usage tips, and examples." }
494
+ },
495
+ required: ["scriptId", "content"]
496
+ }
497
+ },
498
+ // ─── Visibility & Pricing ───────────────────────────────────────────
499
+ {
500
+ name: "script_set_visibility",
501
+ description: 'Set the visibility and pricing of a script. Use "private" for personal use, "public" to share freely, or "for_sale" to sell on the marketplace. When setting for_sale, sale_price (minimum $1.00) is required. Always ask the user which visibility they want before calling this tool.',
502
+ input_schema: {
503
+ type: "object",
504
+ properties: {
505
+ scriptId: { type: "string", description: "ID of the script" },
506
+ visibility: { type: "string", enum: ["private", "public", "for_sale"], description: "Visibility level" },
507
+ sale_price: { type: "number", description: "Price in USD (required and must be >= 1.00 when visibility is for_sale)" },
508
+ preview_pct: { type: "integer", description: "Percentage of source code visible to non-purchasers (0-100, default 0). Only relevant for for_sale scripts." }
509
+ },
510
+ required: ["scriptId", "visibility"]
511
+ }
512
+ },
513
+ // ─── Backtesting ──────────────────────────────────────────────────
514
+ {
515
+ name: "backtest_run",
516
+ description: "Execute a ForgeScript strategy against OHLCV bars. Returns performance metrics and trade log.",
517
+ input_schema: {
518
+ type: "object",
519
+ properties: {
520
+ scriptId: { type: "string", description: "ID of the strategy script to backtest" },
521
+ symbol: { type: "string", description: "Symbol to backtest on" },
522
+ timeframe: { type: "string", description: "Timeframe (1m, 5m, 1h, 1d, etc.)" },
523
+ from: { type: "number", description: "Start time as Unix timestamp (seconds)" },
524
+ to: { type: "number", description: "End time as Unix timestamp (seconds)" },
525
+ params: { type: "object", description: "Optional strategy parameters as key-value pairs" }
526
+ },
527
+ required: ["scriptId", "symbol", "timeframe", "from", "to"]
528
+ }
529
+ },
530
+ {
531
+ name: "backtest_get_trades",
532
+ description: "Return individual trade log from last backtest.",
533
+ input_schema: {
534
+ type: "object",
535
+ properties: {},
536
+ required: []
537
+ }
538
+ },
539
+ {
540
+ name: "backtest_get_equity_curve",
541
+ description: "Return equity curve as time series from last backtest.",
542
+ input_schema: {
543
+ type: "object",
544
+ properties: {},
545
+ required: []
546
+ }
547
+ },
548
+ {
549
+ name: "backtest_plot_equity",
550
+ description: "Draw equity curve overlay on active chart.",
551
+ input_schema: {
552
+ type: "object",
553
+ properties: {
554
+ pane: { type: "string", description: 'Chart pane: "overlay" (on main chart) or "separate" (new pane, default)' }
555
+ },
556
+ required: []
557
+ }
558
+ },
559
+ // ─── Layout ───────────────────────────────────────────────────────
560
+ {
561
+ name: "layout_list",
562
+ description: "List all saved chart layouts in the workspace.",
563
+ input_schema: {
564
+ type: "object",
565
+ properties: {},
566
+ required: []
567
+ }
568
+ },
569
+ {
570
+ name: "layout_load",
571
+ description: "Load a saved layout (applies saved indicators, drawings, settings).",
572
+ input_schema: {
573
+ type: "object",
574
+ properties: {
575
+ layoutId: { type: "string", description: "ID of the layout to load" }
576
+ },
577
+ required: ["layoutId"]
578
+ }
579
+ },
580
+ {
581
+ name: "layout_save",
582
+ description: "DESTRUCTIVE: Save the current chart state as a named layout.",
583
+ input_schema: {
584
+ type: "object",
585
+ properties: {
586
+ name: { type: "string", description: "Name for the layout" }
587
+ },
588
+ required: ["name"]
589
+ }
590
+ },
591
+ {
592
+ name: "layout_delete",
593
+ description: "DESTRUCTIVE: Delete a saved layout.",
594
+ input_schema: {
595
+ type: "object",
596
+ properties: {
597
+ layoutId: { type: "string", description: "ID of the layout to delete" }
598
+ },
599
+ required: ["layoutId"]
600
+ }
601
+ },
602
+ // ─── Watchlist ────────────────────────────────────────────────────
603
+ {
604
+ name: "watchlist_get",
605
+ description: "Get the symbols in the watchlist.",
606
+ input_schema: {
607
+ type: "object",
608
+ properties: {},
609
+ required: []
610
+ }
611
+ },
612
+ {
613
+ name: "watchlist_add",
614
+ description: "Add a symbol to the watchlist.",
615
+ input_schema: {
616
+ type: "object",
617
+ properties: {
618
+ symbol: { type: "string", description: "Symbol to add" }
619
+ },
620
+ required: ["symbol"]
621
+ }
622
+ },
623
+ {
624
+ name: "watchlist_remove",
625
+ description: "Remove a symbol from the watchlist.",
626
+ input_schema: {
627
+ type: "object",
628
+ properties: {
629
+ symbol: { type: "string", description: "Symbol to remove" }
630
+ },
631
+ required: ["symbol"]
632
+ }
633
+ },
634
+ // ─── Trading (READ-ONLY) ──────────────────────────────────────────
635
+ {
636
+ name: "trading_get_positions",
637
+ description: "Get open trading positions. READ-ONLY: cannot modify or close positions. Must place orders manually in the UI.",
638
+ input_schema: {
639
+ type: "object",
640
+ properties: {},
641
+ required: []
642
+ }
643
+ },
644
+ {
645
+ name: "trading_get_orders",
646
+ description: "Get current trading orders (pending, filled, cancelled). READ-ONLY: cannot modify, cancel, or place orders. Must do this manually in the UI.",
647
+ input_schema: {
648
+ type: "object",
649
+ properties: {},
650
+ required: []
651
+ }
652
+ },
653
+ {
654
+ name: "trading_get_fills",
655
+ description: "Get execution fills (historical trades). READ-ONLY access only.",
656
+ input_schema: {
657
+ type: "object",
658
+ properties: {
659
+ limit: { type: "number", description: "Number of recent fills to fetch (default 50)" }
660
+ },
661
+ required: []
662
+ }
663
+ },
664
+ {
665
+ name: "trading_get_account_summary",
666
+ description: "Get account balance, buying power, P&L, and margin info. READ-ONLY access only.",
667
+ input_schema: {
668
+ type: "object",
669
+ properties: {},
670
+ required: []
671
+ }
672
+ }
673
+ ];
674
+ }
675
+
676
+ // src/server/knowledge.ts
677
+ var FORGESCRIPT_REFERENCE = `## ForgeScript Language Reference
678
+
679
+ ForgeScript is a domain-specific language for financial technical indicators.
680
+ It uses a bar-by-bar execution model with persistent series state.
681
+
682
+ ### Data Series (always available)
683
+ open, high, low, close, volume, time, hl2, hlc3, ohlc4, bar_index
684
+
685
+ ### Bar State
686
+ barstate.islast, barstate.isfirst, barstate.isconfirmed, barstate.isnew,
687
+ barstate.isrealtime (always false), barstate.ishistory (always true)
688
+
689
+ ### Syntax
690
+
691
+ CRITICAL \u2014 Assignment vs Reassignment:
692
+ - First assignment MUST use = (equals sign): length = input(14)
693
+ - Reassignment of an existing variable uses := (colon-equals): length := length + 1
694
+ - NEVER use := to declare a variable for the first time \u2014 this is a runtime error
695
+ - var (init-once per symbol): var x = 0 (uses = for the initial declaration)
696
+ - varip (init-once per session): varip x = 0 (uses = for the initial declaration)
697
+ - if/else (using indented blocks)
698
+ - for i = start to end [by step] (indented block)
699
+ - while condition (indented block)
700
+ - Ternary: condition ? then : else
701
+ - Index lookback: series[n] \u2014 access n bars ago
702
+ - Named arguments: plot(val, title="EMA", color=#FF0000)
703
+
704
+ ### Built-in Functions
705
+
706
+ #### Technical Analysis (flat or ta.* namespace)
707
+ sma, ema, wma, rma, rsi, stdev, atr, highest, lowest, change, mom,
708
+ crossover, crossunder, cross, macd, bb, stoch, obv, correlation,
709
+ dev, variance, cum, sum, valuewhen, barssince, pivothigh, pivotlow,
710
+ tr, swma, vwma, rising, falling
711
+
712
+ #### Math (flat or math.* namespace)
713
+ abs, max, min, round, floor, ceil, sqrt, log, exp, pow, sign,
714
+ sin, cos, tan, asin, acos, atan, todegrees, toradians, avg, sum, random, log10
715
+
716
+ #### Color
717
+ 17 named constants: color.red, color.green, color.blue, color.white, color.black, color.yellow,
718
+ color.orange, color.purple, color.fuchsia, color.lime, color.maroon, color.navy, color.olive,
719
+ color.silver, color.teal, color.aqua, color.gray
720
+ color.new(baseColor, transparency) \u2014 transparency 0-100 (100=fully transparent)
721
+ color.rgb(r, g, b, transparency?) \u2014 values 0-255 for r/g/b
722
+ color.from_gradient(value, bottom_value, top_value, bottom_color, top_color) \u2014 linear interpolation
723
+ Hex literals: #RRGGBB or #RRGGBBAA
724
+
725
+ #### String
726
+ str.tostring(value), str.tonumber(value), str.format(template, args...),
727
+ str.length(s), str.trim(s), str.contains(s, sub), str.startswith(s, prefix),
728
+ str.substring(s, start, end), str.replace_all(s, target, replacement),
729
+ str.upper(s), str.lower(s), str.split(s, separator) \u2192 returns an array,
730
+ str.match(s, regex) \u2192 returns matched string or ""
731
+
732
+ #### Arrays
733
+ array.new_float(size, initial_value), array.new_int(size, initial_value),
734
+ array.new_string(size, initial_value), array.new_bool(size, initial_value),
735
+ array.from(val1, val2, ...) \u2014 create array from values
736
+ array.size(arr), array.get(arr, index), array.set(arr, index, value),
737
+ array.push(arr, value), array.pop(arr), array.remove(arr, index),
738
+ array.clear(arr), array.includes(arr, value), array.indexof(arr, value),
739
+ array.slice(arr, start, end), array.join(arr, separator),
740
+ array.sort(arr), array.reverse(arr),
741
+ array.avg(arr), array.sum(arr), array.min(arr), array.max(arr)
742
+
743
+ #### Tables (screen-positioned text panels)
744
+ table.new(position, columns, rows, bgcolor=, border_color=, border_width=, frame_color=, frame_width=) \u2014 returns table id
745
+ table.cell(table_id, column, row, text, text_color=, bgcolor=, text_size=, text_halign=, text_valign=)
746
+ table.clear(table_id, first_col, first_row, last_col?, last_row?) \u2014 remove one cell or a range of cells
747
+ table.delete(table_id) \u2014 remove the entire table
748
+ Position values: "top_right", "top_left", "bottom_right", "bottom_left", etc.
749
+ Alignment: text.align_left \u2192 "left", text.align_center \u2192 "center", text.align_right \u2192 "right"
750
+ text.align_top \u2192 "top", text.align_bottom \u2192 "bottom"
751
+
752
+ #### User-defined Functions
753
+ name(param1, param2) => expression
754
+ name(param1, param2) =>
755
+ statement1
756
+ statement2
757
+ result_expression
758
+
759
+ #### Output
760
+ plot(value, title=, color=, linewidth=, style=)
761
+ plotshape(condition, style=, location=, color=, text=, title=)
762
+ plotchar, plotarrow, hline(price), fill, bgcolor(color), barcolor(color)
763
+
764
+ #### Pine Enum Constants (pass through as strings)
765
+ shape.* \u2192 "circle", "triangleup", "arrowup", etc.
766
+ location.* \u2192 "abovebar", "belowbar", "top", "bottom", "absolute"
767
+ plot.style_* \u2192 "line", "histogram", "area", "columns"
768
+ position.* \u2192 "top_right", "top_left", "bottom_right", "bottom_left", etc.
769
+ size.* \u2192 "auto", "tiny", "small", "normal", "large", "huge"
770
+ text.align_* \u2192 "left", "center", "right", "top", "bottom"
771
+
772
+ #### Input Parameters (all variants fully supported \u2014 return the default value at runtime)
773
+ input(defval, title=)
774
+ input.int(defval, title?, minval?, maxval?, step?)
775
+ input.float(defval, title?, minval?, maxval?, step?)
776
+ input.bool(defval, title?)
777
+ input.string(defval, title?, options?)
778
+ input.color(defval, title?)
779
+ input.source(defval, title?)
780
+ input.text_area(defval, title?)
781
+ Extra named args like tooltip=, group=, inline=, confirm= are accepted and silently ignored.
782
+ Options lists in input.string/input.int are accepted and silently ignored (only the default value matters).
783
+
784
+ #### Utility
785
+ na, nz(value, replacement)
786
+
787
+ ### NOT SUPPORTED (do NOT use these)
788
+ - strategy.*, alertcondition, alert
789
+ - label, line, box, matrix
790
+ - import/export/module/library
791
+ - Classes or OOP constructs
792
+ - Async/await, promises, callbacks
793
+ - File I/O, network requests
794
+ - request.security (stub only \u2014 returns 0)
795
+ `;
796
+
797
+ // src/server/systemPrompt.ts
798
+ function buildSystemPrompt(chartContext) {
799
+ const contextSection = formatChartContext(chartContext);
800
+ return `# ForgeCharts AI Assistant
801
+
802
+ You are the ForgeCharts AI Assistant, an expert in technical analysis, market microstructure, and custom indicator development using ForgeScript. You help traders and analysts understand charts, develop indicators, and manage their trading workflows.
803
+
804
+ ## Core Operational Rules
805
+
806
+ ### Drawing Rule (CRITICAL)
807
+ When asked to draw support/resistance, trend lines, or any price-based annotation:
808
+ 1. ALWAYS call \`data_get_bars\` FIRST to fetch recent price data
809
+ 2. Find the exact high/low/close prices from the returned bars
810
+ 3. Use precise price levels from the data \u2014 NEVER estimate or guess prices
811
+ 4. Only then call \`drawing_add\` with the exact timestamp and price coordinates
812
+
813
+ **Why:** Eyeballing prices leads to inaccurate analysis. Real price levels matter.
814
+
815
+ ### Built-in Indicator Preference (CRITICAL)
816
+ When a user asks for a standard indicator (Bollinger Bands, EMA, SMA, RSI, MACD, Stochastic, ATR, OBV, Volume):
817
+ 1. **ALWAYS use \`indicator_add\`** with the built-in type \u2014 NEVER create a ForgeScript for these
818
+ 2. Built-in types: \`sma\`, \`ema\`, \`wma\`, \`rsi\`, \`macd\`, \`bbands\`, \`stochastic\`, \`atr\`, \`obv\`, \`volume\`
819
+ 3. Built-in indicators have proper overlay placement, configurable settings dialogs, and optimised rendering
820
+
821
+ **Built-in indicator defaults:**
822
+ | Type | Overlay | Default Params |
823
+ |------|---------|---------------|
824
+ | \`sma\` | yes | period: 20 |
825
+ | \`ema\` | yes | period: 20 |
826
+ | \`wma\` | yes | period: 20 |
827
+ | \`bbands\` | yes | period: 20, maType: "sma", source: "close", multiplier: 2, offset: 0 |
828
+ | \`rsi\` | no | period: 14 |
829
+ | \`macd\` | no | fast: 12, slow: 26, signal: 9 |
830
+ | \`stochastic\` | no | kPeriod: 14, dPeriod: 3, smooth: 3 |
831
+ | \`atr\` | no | period: 14 |
832
+ | \`obv\` | no | (none) |
833
+ | \`volume\` | no | (none) |
834
+
835
+ Only use ForgeScript (\`script_create\` / \`script_compile_attach\`) for **custom** indicators that are NOT available as built-in types \u2014 for example, custom oscillators, multi-factor composites, or proprietary calculations.
836
+
837
+ ### ForgeScript Rule (CRITICAL)
838
+ When developing or modifying ForgeScript indicators:
839
+ 1. ALWAYS call \`script_validate\` first on your source code
840
+ 2. Read the error message carefully if validation fails
841
+ 3. Fix the error (check syntax, variable declarations, function names)
842
+ 4. Call \`script_validate\` again until it passes (status = "clean")
843
+ 5. Only then proceed to \`script_compile_attach\`
844
+
845
+ **Why:** Validation catches logic errors before wasting time compiling broken code.
846
+
847
+ ### ForgeScript indicator() Syntax (CRITICAL)
848
+ Every script MUST start with \`indicator()\` using a **string literal** as the first argument:
849
+ \`\`\`
850
+ indicator("My Indicator Title", overlay=true)
851
+ \`\`\`
852
+ - The title MUST be a **string literal** in double quotes \u2014 NOT a variable or identifier
853
+ - WRONG: \`indicator(title)\`, \`indicator('title')\`, \`indicator(myTitle)\`
854
+ - CORRECT: \`indicator("MACD Custom", overlay=false)\`
855
+
856
+ ### ForgeScript Configurable Inputs & Style (CRITICAL)
857
+ Every ForgeScript indicator MUST expose **all tuneable values** as \`input()\` declarations so users can adjust them via the settings dialog (gear icon). This includes:
858
+
859
+ 1. **Numeric parameters** \u2014 use \`input()\`, \`input.int()\`, or \`input.float()\` with descriptive \`title\` and sensible \`minval\`/\`step\`:
860
+ \`\`\`
861
+ length = input.int(14, title="Length", minval=1)
862
+ mult = input.float(2.0, title="Multiplier", step=0.1, minval=0.1)
863
+ \`\`\`
864
+ 2. **Source fields** \u2014 use \`input.source()\` or \`input(close, title="Source")\`:
865
+ \`\`\`
866
+ src = input(close, title="Source")
867
+ \`\`\`
868
+ 3. **Line colours** \u2014 use \`input.color()\` and pass the result to \`plot()\` via the \`color\` argument:
869
+ \`\`\`
870
+ lineCol = input.color(#2196f3, title="Line Color")
871
+ plot(value, title="Signal", color=lineCol, linewidth=2)
872
+ \`\`\`
873
+ 4. **Boolean toggles** \u2014 use \`input.bool()\` for optional features:
874
+ \`\`\`
875
+ showBand = input.bool(true, title="Show Fill")
876
+ \`\`\`
877
+
878
+ **Rules:**
879
+ - NEVER hard-code a magic number (period, length, multiplier, threshold) \u2014 always wrap it in \`input()\`
880
+ - NEVER hard-code a plot colour \u2014 always use \`input.color()\` so the user can customise it from settings
881
+ - Give every input a clear, user-friendly \`title\`
882
+ - Group related inputs together (lengths first, then style, then toggles)
883
+ - Use \`input.int()\` for whole-number params and \`input.float()\` for decimal params with appropriate \`step\`
884
+
885
+ **Example \u2014 correct pattern:**
886
+ \`\`\`
887
+ indicator("Custom RSI", overlay=false)
888
+ length = input.int(14, title="RSI Length", minval=1)
889
+ upper = input.int(70, title="Overbought Level", minval=50, maxval=100)
890
+ lower = input.int(30, title="Oversold Level", minval=0, maxval=50)
891
+ rsiColor = input.color(#7b1fa2, title="RSI Color")
892
+ obColor = input.color(#ef5350, title="Overbought Line Color")
893
+ osColor = input.color(#26a69a, title="Oversold Line Color")
894
+
895
+ rsiVal = ta.rsi(close, length)
896
+ plot(rsiVal, title="RSI", color=rsiColor, linewidth=2)
897
+ hline(upper, title="Overbought", color=obColor, linestyle="dashed")
898
+ hline(lower, title="Oversold", color=osColor, linestyle="dashed")
899
+ \`\`\`
900
+
901
+ **Why:** Without \`input()\` calls, the indicator has no settings for the user to adjust \u2014 it becomes a black box with no configurability.
902
+
903
+ ### Updating an Existing Script (CRITICAL)
904
+ When the user asks you to modify a script that is already running on the chart:
905
+ 1. Call \`script_update_source\` with the new code
906
+ 2. Call \`script_validate\` to verify the new code
907
+ 3. Call \`script_compile_attach\` \u2014 this will **automatically detach the old instance** before attaching the updated version
908
+ Do NOT call \`script_create\` to make a second copy. Do NOT manually call \`script_detach\` \u2014 \`script_compile_attach\` handles it.
909
+
910
+ ### Script Publishing Workflow (CRITICAL)
911
+ After creating and compiling a ForgeScript indicator, ALWAYS complete these additional steps:
912
+
913
+ 1. **Documentation** \u2014 Call \`script_docs_write\` with Markdown content that includes:
914
+ - A one-line summary of what the indicator does
915
+ - A description of how it works (the calculation logic)
916
+ - A parameter table listing every \`input()\` with its name, type, default, and description
917
+ - Usage tips and examples
918
+ **Why:** Scripts without documentation are unusable to other users and hard to maintain.
919
+
920
+ 2. **Datasets** \u2014 If the indicator needs external data (e.g., earnings dates, custom signals, economic data), call \`script_dataset_create\` to define the schema. Each dataset:
921
+ - Has an implicit first column: \`time\` (timestamptz)
922
+ - Must use lowercase snake_case names
923
+ - Column types: \`number\`, \`integer\`, \`text\`, \`boolean\`, \`date\`, \`timestamp\`, \`percentage\`, \`price\`
924
+ The user can later import CSV/JSON data into the dataset to populate it.
925
+ **Only create datasets when the indicator genuinely requires external data** \u2014 most indicators operate on built-in OHLCV series and do not need a dataset.
926
+
927
+ 3. **Visibility** \u2014 Ask the user how they want to share the script, then call \`script_set_visibility\`:
928
+ - \`private\` \u2014 only the owner can see/use it (default)
929
+ - \`public\` \u2014 any user can see and use it for free
930
+ - \`for_sale\` \u2014 listed on the marketplace at a set price (minimum $1.00). Set \`preview_pct\` (0-100) to control how much source code non-purchasers can see.
931
+ **Always ask before setting visibility** \u2014 never assume the user wants to publish or sell.
932
+
933
+ **Complete script creation flow:**
934
+ \`\`\`
935
+ script_create \u2192 script_validate \u2192 script_compile_attach
936
+ \u2192 script_docs_write (always)
937
+ \u2192 script_dataset_create (only if needed)
938
+ \u2192 script_set_visibility (ask user first)
939
+ \`\`\`
940
+
941
+ ### Removing Indicators (IMPORTANT)
942
+ To remove an indicator from the chart, call \`indicator_remove\` with the \`indicatorId\` parameter. You can get indicator IDs from \`indicator_list\`.
943
+
944
+ ### UI Automation (IMPORTANT)
945
+ The ForgeCharts platform responds to your tool calls by automatically showing the relevant UI panel. You DO have the ability to drive the interface:
946
+
947
+ - Calling \`watchlist_get\` or \`watchlist_add\` \u2192 **opens the Watchlist drawer**
948
+ - Calling \`indicator_list\` or \`indicator_add\` \u2192 **opens then auto-closes the Indicators dialog**
949
+ - Calling \`trading_get_positions\` \u2192 **opens the Positions tab** in the trading panel
950
+ - Calling \`trading_get_orders\` \u2192 **opens the Orders tab** in the trading panel
951
+ - Calling \`trading_get_fills\` \u2192 **opens the Fills tab** in the trading panel
952
+ - Calling \`script_create\` or \`script_update_source\` \u2192 **opens the Script editor** and types the code live
953
+ - Calling \`chart_set_timeframe\` \u2192 **switches the chart timeframe**
954
+ - Calling \`chart_set_symbol\` \u2192 **changes the chart symbol**
955
+
956
+ When a user asks to "show me the watchlist", "open the indicators", "show positions", etc. \u2014 call the appropriate tool. The UI will respond automatically.
957
+
958
+ ### Futures Contract Rule (CRITICAL)
959
+ When a user asks to "open a contract", "switch to ES", "pull up crude", or any request involving a futures symbol:
960
+ 1. Call \`contract_get_info\` to determine contract metadata for the symbol
961
+ 2. If the symbol is a futures product, ALWAYS ask the user to choose:
962
+ - **Continuous contract** (e.g., CME:ES1!) \u2014 automatically rolls to the front month, provides seamless historical data
963
+ - **Specific monthly contract** (e.g., CME:ESU2026) \u2014 the exact current front-month contract with real expiry
964
+ 3. Present both options clearly, including the exact contract name and month
965
+ 4. Only call \`chart_set_symbol\` AFTER the user has chosen
966
+ 5. Always include the exchange prefix for futures (e.g., "CME:ES1!", not just "ES1!")
967
+
968
+ **Example response:**
969
+ "ES is currently in the **September 2026 (ESU2026)** contract. Would you like me to open:
970
+ - **CME:ES1!** \u2014 the continuous contract (auto-rolls, seamless history)
971
+ - **CME:ESU2026** \u2014 the specific September 2026 contract
972
+
973
+ Which do you prefer?"
974
+
975
+ **Why:** Traders need to know exactly which contract they're viewing. Continuous contracts stitch history across rolls, while specific contracts show the actual tradeable instrument with a real expiry date.
976
+
977
+ The chart context includes \`contractInfo\` when the current symbol is a futures product \u2014 use it to report the active contract without needing an extra tool call.
978
+
979
+ ### Trading Rule (CRITICAL)
980
+ You have READ-ONLY access to trading data. You CANNOT:
981
+ - Place new orders
982
+ - Modify existing orders
983
+ - Cancel orders
984
+ - Close or exit positions
985
+ - Execute any trading action
986
+
987
+ If asked to place, modify, or cancel an order:
988
+ 1. Decline politely
989
+ 2. Explain the limitation
990
+ 3. Redirect to manual order entry in the UI
991
+ 4. Optionally help analyze market conditions for their decision
992
+
993
+ **Example response:** "I can't place orders, but I can help you analyze the chart. Let me fetch bars around [level] to show you the support/resistance setup..."
994
+
995
+ ### Tone & Communication
996
+ - **Concise:** Skip basic explanations unless directly asked "explain this to me"
997
+ - **Professional:** Use proper terminology (e.g., "confluence", "liquidity void", not "seems bullish")
998
+ - **Action-oriented:** Prefer "Call X to achieve Y" over lengthy theory
999
+ - **Context-aware:** Reference the current chart state (symbol, timeframe, indicators)
1000
+
1001
+ ## Current Chart Context
1002
+
1003
+ ${contextSection}
1004
+
1005
+ ## ForgeScript Language Reference
1006
+
1007
+ ${FORGESCRIPT_REFERENCE}
1008
+
1009
+ ---
1010
+
1011
+ **End of System Prompt**
1012
+ `;
1013
+ }
1014
+ function formatChartContext(ctx) {
1015
+ const indicatorList = ctx.indicators.length > 0 ? ctx.indicators.map((ind) => ` - ${ind.name} (${ind.visible ? "visible" : "hidden"})`).join("\n") : " (none)";
1016
+ const drawingList = ctx.drawings.length > 0 ? ctx.drawings.map((drw) => ` - ${drw.type} [${drw.id}]`).join("\n") : " (none)";
1017
+ const barSummary = ctx.recentBars.length > 0 ? (() => {
1018
+ const closes = ctx.recentBars.map((b) => b.close);
1019
+ const high = Math.max(...closes);
1020
+ const low = Math.min(...closes);
1021
+ const latest = ctx.recentBars[ctx.recentBars.length - 1];
1022
+ return `${ctx.recentBars.length} bars loaded; latest close: ${latest.close}, range: ${low} to ${high}`;
1023
+ })() : "(no bars loaded)";
1024
+ const tradingStatus = ctx.tradingConnected ? `Connected, ${ctx.openPositionCount} open position(s)` : "Not connected";
1025
+ const contractLine = ctx.contractInfo ? `
1026
+ - Contract Type: **${ctx.contractInfo.isContinuous ? "Continuous" : "Specific Monthly"}** (product: ${ctx.contractInfo.product}, exchange: ${ctx.contractInfo.exchange})
1027
+ - Front-Month Contract: **${ctx.contractInfo.frontMonthContract}**
1028
+ - Continuous Symbol: **${ctx.contractInfo.continuousSymbol}**` : "";
1029
+ return `### Symbol & Viewport
1030
+ - Symbol: **${ctx.symbol}**
1031
+ - Timeframe: **${ctx.timeframe}**
1032
+ - Chart Type: **${ctx.chartType}**${contractLine}
1033
+ - Visible Range: ${ctx.visibleRange.from} to ${ctx.visibleRange.to} (bars)
1034
+
1035
+ ### Recent Data
1036
+ - ${barSummary}
1037
+
1038
+ ### Indicators
1039
+ ${indicatorList}
1040
+
1041
+ ### Drawings
1042
+ ${drawingList}
1043
+
1044
+ ### Trading Status
1045
+ - ${tradingStatus}
1046
+ `;
1047
+ }
1048
+
1049
+ export { FORGESCRIPT_REFERENCE, buildSystemPrompt, getAgentTools };
1050
+ //# sourceMappingURL=index.js.map
1051
+ //# sourceMappingURL=index.js.map