fixparser-plugin-mcp 9.1.7-c415bb75 → 9.1.7-c6228661
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/build/cjs/MCPLocal.js +422 -10
- package/build/cjs/MCPLocal.js.map +4 -4
- package/build/cjs/MCPRemote.js +422 -66
- package/build/cjs/MCPRemote.js.map +4 -4
- package/build/cjs/index.js +422 -66
- package/build/cjs/index.js.map +4 -4
- package/build/esm/MCPLocal.mjs +422 -10
- package/build/esm/MCPLocal.mjs.map +4 -4
- package/build/esm/MCPRemote.mjs +422 -66
- package/build/esm/MCPRemote.mjs.map +4 -4
- package/build/esm/index.mjs +422 -66
- package/build/esm/index.mjs.map +4 -4
- package/build-examples/cjs/example_mcp_local.js +9 -7
- package/build-examples/cjs/example_mcp_local.js.map +4 -4
- package/build-examples/cjs/example_mcp_remote.js +9 -7
- package/build-examples/cjs/example_mcp_remote.js.map +4 -4
- package/build-examples/esm/example_mcp_local.mjs +9 -7
- package/build-examples/esm/example_mcp_local.mjs.map +4 -4
- package/build-examples/esm/example_mcp_remote.mjs +9 -7
- package/build-examples/esm/example_mcp_remote.mjs.map +4 -4
- package/package.json +2 -2
- package/types/MCPBase.d.ts +0 -49
- package/types/MCPLocal.d.ts +0 -32
- package/types/MCPRemote.d.ts +0 -58
- package/types/PluginOptions.d.ts +0 -6
- package/types/index.d.ts +0 -3
- package/types/schemas/index.d.ts +0 -15
- package/types/schemas/marketData.d.ts +0 -48
- package/types/schemas/schemas.d.ts +0 -168
- package/types/tools/index.d.ts +0 -17
- package/types/tools/marketData.d.ts +0 -40
- package/types/tools/order.d.ts +0 -12
- package/types/tools/parse.d.ts +0 -5
- package/types/tools/parseToJSON.d.ts +0 -5
- package/types/utils/messageHandler.d.ts +0 -12
package/build/esm/MCPRemote.mjs
CHANGED
|
@@ -276,17 +276,348 @@ var toolSchemas = {
|
|
|
276
276
|
},
|
|
277
277
|
required: ["symbol"]
|
|
278
278
|
}
|
|
279
|
+
},
|
|
280
|
+
technicalAnalysis: {
|
|
281
|
+
description: "Performs comprehensive technical analysis on market data for a given symbol, including indicators like SMA, EMA, RSI, Bollinger Bands, and trading signals",
|
|
282
|
+
schema: {
|
|
283
|
+
type: "object",
|
|
284
|
+
properties: {
|
|
285
|
+
symbol: {
|
|
286
|
+
type: "string",
|
|
287
|
+
description: "The trading symbol to analyze (e.g., AAPL, MSFT, EURUSD)"
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
required: ["symbol"]
|
|
291
|
+
}
|
|
279
292
|
}
|
|
280
293
|
};
|
|
281
294
|
|
|
295
|
+
// src/tools/analytics.ts
|
|
296
|
+
function sum(numbers) {
|
|
297
|
+
return numbers.reduce((acc, val) => acc + val, 0);
|
|
298
|
+
}
|
|
299
|
+
var TechnicalAnalyzer = class {
|
|
300
|
+
prices;
|
|
301
|
+
volumes;
|
|
302
|
+
highs;
|
|
303
|
+
lows;
|
|
304
|
+
constructor(data) {
|
|
305
|
+
this.prices = data.map((d) => d.trade > 0 ? d.trade : d.midPrice);
|
|
306
|
+
this.volumes = data.map((d) => d.volume);
|
|
307
|
+
this.highs = data.map((d) => d.tradingSessionHighPrice > 0 ? d.tradingSessionHighPrice : d.trade);
|
|
308
|
+
this.lows = data.map((d) => d.tradingSessionLowPrice > 0 ? d.tradingSessionLowPrice : d.trade);
|
|
309
|
+
}
|
|
310
|
+
// Calculate Simple Moving Average
|
|
311
|
+
calculateSMA(data, period) {
|
|
312
|
+
const sma = [];
|
|
313
|
+
for (let i = period - 1; i < data.length; i++) {
|
|
314
|
+
const sum2 = data.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);
|
|
315
|
+
sma.push(sum2 / period);
|
|
316
|
+
}
|
|
317
|
+
return sma;
|
|
318
|
+
}
|
|
319
|
+
// Calculate Exponential Moving Average
|
|
320
|
+
calculateEMA(data, period) {
|
|
321
|
+
const multiplier = 2 / (period + 1);
|
|
322
|
+
const ema = [data[0]];
|
|
323
|
+
for (let i = 1; i < data.length; i++) {
|
|
324
|
+
ema.push(data[i] * multiplier + ema[i - 1] * (1 - multiplier));
|
|
325
|
+
}
|
|
326
|
+
return ema;
|
|
327
|
+
}
|
|
328
|
+
// Calculate RSI
|
|
329
|
+
calculateRSI(data, period = 14) {
|
|
330
|
+
if (data.length < period + 1) return [];
|
|
331
|
+
const changes = [];
|
|
332
|
+
for (let i = 1; i < data.length; i++) {
|
|
333
|
+
changes.push(data[i] - data[i - 1]);
|
|
334
|
+
}
|
|
335
|
+
const gains = changes.map((change) => change > 0 ? change : 0);
|
|
336
|
+
const losses = changes.map((change) => change < 0 ? Math.abs(change) : 0);
|
|
337
|
+
let avgGain = gains.slice(0, period).reduce((a, b) => a + b, 0) / period;
|
|
338
|
+
let avgLoss = losses.slice(0, period).reduce((a, b) => a + b, 0) / period;
|
|
339
|
+
const rsi = [];
|
|
340
|
+
for (let i = period; i < changes.length; i++) {
|
|
341
|
+
const rs = avgGain / avgLoss;
|
|
342
|
+
rsi.push(100 - 100 / (1 + rs));
|
|
343
|
+
avgGain = (avgGain * (period - 1) + gains[i]) / period;
|
|
344
|
+
avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
|
|
345
|
+
}
|
|
346
|
+
return rsi;
|
|
347
|
+
}
|
|
348
|
+
// Calculate Bollinger Bands
|
|
349
|
+
calculateBollingerBands(data, period = 20, stdDev = 2) {
|
|
350
|
+
if (data.length < period) return [];
|
|
351
|
+
const sma = this.calculateSMA(data, period);
|
|
352
|
+
const bands = [];
|
|
353
|
+
for (let i = 0; i < sma.length; i++) {
|
|
354
|
+
const dataSlice = data.slice(i, i + period);
|
|
355
|
+
const mean = sma[i];
|
|
356
|
+
const variance = dataSlice.reduce((sum2, price) => sum2 + (price - mean) ** 2, 0) / period;
|
|
357
|
+
const standardDeviation = Math.sqrt(variance);
|
|
358
|
+
bands.push({
|
|
359
|
+
upper: mean + standardDeviation * stdDev,
|
|
360
|
+
middle: mean,
|
|
361
|
+
lower: mean - standardDeviation * stdDev
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
return bands;
|
|
365
|
+
}
|
|
366
|
+
// Calculate maximum drawdown
|
|
367
|
+
calculateMaxDrawdown(prices) {
|
|
368
|
+
let maxPrice = prices[0];
|
|
369
|
+
let maxDrawdown = 0;
|
|
370
|
+
for (let i = 1; i < prices.length; i++) {
|
|
371
|
+
if (prices[i] > maxPrice) {
|
|
372
|
+
maxPrice = prices[i];
|
|
373
|
+
}
|
|
374
|
+
const drawdown = (maxPrice - prices[i]) / maxPrice;
|
|
375
|
+
if (drawdown > maxDrawdown) {
|
|
376
|
+
maxDrawdown = drawdown;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return maxDrawdown;
|
|
380
|
+
}
|
|
381
|
+
// Calculate price changes for volatility
|
|
382
|
+
calculatePriceChanges() {
|
|
383
|
+
const changes = [];
|
|
384
|
+
for (let i = 1; i < this.prices.length; i++) {
|
|
385
|
+
changes.push((this.prices[i] - this.prices[i - 1]) / this.prices[i - 1]);
|
|
386
|
+
}
|
|
387
|
+
return changes;
|
|
388
|
+
}
|
|
389
|
+
// Generate comprehensive market analysis
|
|
390
|
+
analyze() {
|
|
391
|
+
const currentPrice = this.prices[this.prices.length - 1];
|
|
392
|
+
const startPrice = this.prices[0];
|
|
393
|
+
const sessionHigh = Math.max(...this.highs);
|
|
394
|
+
const sessionLow = Math.min(...this.lows);
|
|
395
|
+
const totalVolume = sum(this.volumes);
|
|
396
|
+
const avgVolume = totalVolume / this.volumes.length;
|
|
397
|
+
const priceChanges = this.calculatePriceChanges();
|
|
398
|
+
const volatility = priceChanges.length > 0 ? Math.sqrt(
|
|
399
|
+
priceChanges.reduce((sum2, change) => sum2 + change ** 2, 0) / priceChanges.length
|
|
400
|
+
) * Math.sqrt(252) * 100 : 0;
|
|
401
|
+
const sessionReturn = (currentPrice - startPrice) / startPrice * 100;
|
|
402
|
+
const pricePosition = (currentPrice - sessionLow) / (sessionHigh - sessionLow) * 100;
|
|
403
|
+
const trueVWAP = this.prices.reduce((sum2, price, i) => sum2 + price * this.volumes[i], 0) / totalVolume;
|
|
404
|
+
const momentum5 = this.prices.length > 5 ? (currentPrice - this.prices[Math.max(0, this.prices.length - 6)]) / this.prices[Math.max(0, this.prices.length - 6)] * 100 : 0;
|
|
405
|
+
const momentum10 = this.prices.length > 10 ? (currentPrice - this.prices[Math.max(0, this.prices.length - 11)]) / this.prices[Math.max(0, this.prices.length - 11)] * 100 : 0;
|
|
406
|
+
const maxDrawdown = this.calculateMaxDrawdown(this.prices);
|
|
407
|
+
return {
|
|
408
|
+
currentPrice,
|
|
409
|
+
startPrice,
|
|
410
|
+
sessionHigh,
|
|
411
|
+
sessionLow,
|
|
412
|
+
totalVolume,
|
|
413
|
+
avgVolume,
|
|
414
|
+
volatility,
|
|
415
|
+
sessionReturn,
|
|
416
|
+
pricePosition,
|
|
417
|
+
trueVWAP,
|
|
418
|
+
momentum5,
|
|
419
|
+
momentum10,
|
|
420
|
+
maxDrawdown
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
// Generate technical indicators
|
|
424
|
+
getTechnicalIndicators() {
|
|
425
|
+
return {
|
|
426
|
+
sma5: this.calculateSMA(this.prices, 5),
|
|
427
|
+
sma10: this.calculateSMA(this.prices, 10),
|
|
428
|
+
ema8: this.calculateEMA(this.prices, 8),
|
|
429
|
+
ema21: this.calculateEMA(this.prices, 21),
|
|
430
|
+
rsi: this.calculateRSI(this.prices, 14),
|
|
431
|
+
bollinger: this.calculateBollingerBands(this.prices, 20, 2)
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
// Generate trading signals
|
|
435
|
+
generateSignals() {
|
|
436
|
+
const analysis = this.analyze();
|
|
437
|
+
let bullishSignals = 0;
|
|
438
|
+
let bearishSignals = 0;
|
|
439
|
+
const signals = [];
|
|
440
|
+
if (analysis.currentPrice > analysis.trueVWAP) {
|
|
441
|
+
signals.push(
|
|
442
|
+
`\u2713 BULLISH: Price above VWAP (+${((analysis.currentPrice - analysis.trueVWAP) / analysis.trueVWAP * 100).toFixed(2)}%)`
|
|
443
|
+
);
|
|
444
|
+
bullishSignals++;
|
|
445
|
+
} else {
|
|
446
|
+
signals.push(
|
|
447
|
+
`\u2717 BEARISH: Price below VWAP (${((analysis.currentPrice - analysis.trueVWAP) / analysis.trueVWAP * 100).toFixed(2)}%)`
|
|
448
|
+
);
|
|
449
|
+
bearishSignals++;
|
|
450
|
+
}
|
|
451
|
+
if (analysis.momentum5 > 0 && analysis.momentum10 > 0) {
|
|
452
|
+
signals.push("\u2713 BULLISH: Positive momentum on both timeframes");
|
|
453
|
+
bullishSignals++;
|
|
454
|
+
} else if (analysis.momentum5 < 0 && analysis.momentum10 < 0) {
|
|
455
|
+
signals.push("\u2717 BEARISH: Negative momentum on both timeframes");
|
|
456
|
+
bearishSignals++;
|
|
457
|
+
} else {
|
|
458
|
+
signals.push("\u25D0 MIXED: Conflicting momentum signals");
|
|
459
|
+
}
|
|
460
|
+
const currentVolume = this.volumes[this.volumes.length - 1];
|
|
461
|
+
const volumeRatio = currentVolume / analysis.avgVolume;
|
|
462
|
+
if (volumeRatio > 1.2 && analysis.sessionReturn > 0) {
|
|
463
|
+
signals.push("\u2713 BULLISH: Above-average volume supporting upward move");
|
|
464
|
+
bullishSignals++;
|
|
465
|
+
} else if (volumeRatio > 1.2 && analysis.sessionReturn < 0) {
|
|
466
|
+
signals.push("\u2717 BEARISH: Above-average volume supporting downward move");
|
|
467
|
+
bearishSignals++;
|
|
468
|
+
} else {
|
|
469
|
+
signals.push("\u25D0 NEUTRAL: Volume not providing clear direction");
|
|
470
|
+
}
|
|
471
|
+
if (analysis.pricePosition > 65 && analysis.volatility > 30) {
|
|
472
|
+
signals.push("\u2717 BEARISH: High in range with elevated volatility - reversal risk");
|
|
473
|
+
bearishSignals++;
|
|
474
|
+
} else if (analysis.pricePosition < 35 && analysis.volatility > 30) {
|
|
475
|
+
signals.push("\u2713 BULLISH: Low in range with volatility - potential bounce");
|
|
476
|
+
bullishSignals++;
|
|
477
|
+
} else {
|
|
478
|
+
signals.push("\u25D0 NEUTRAL: Price position and volatility not extreme");
|
|
479
|
+
}
|
|
480
|
+
return { bullishSignals, bearishSignals, signals };
|
|
481
|
+
}
|
|
482
|
+
// Generate comprehensive JSON analysis
|
|
483
|
+
generateJSONAnalysis(symbol) {
|
|
484
|
+
const analysis = this.analyze();
|
|
485
|
+
const indicators = this.getTechnicalIndicators();
|
|
486
|
+
const signals = this.generateSignals();
|
|
487
|
+
const currentSMA5 = indicators.sma5.length > 0 ? indicators.sma5[indicators.sma5.length - 1] : null;
|
|
488
|
+
const currentSMA10 = indicators.sma10.length > 0 ? indicators.sma10[indicators.sma10.length - 1] : null;
|
|
489
|
+
const currentEMA8 = indicators.ema8[indicators.ema8.length - 1];
|
|
490
|
+
const currentEMA21 = indicators.ema21[indicators.ema21.length - 1];
|
|
491
|
+
const currentRSI = indicators.rsi.length > 0 ? indicators.rsi[indicators.rsi.length - 1] : null;
|
|
492
|
+
const currentBB = indicators.bollinger.length > 0 ? indicators.bollinger[indicators.bollinger.length - 1] : null;
|
|
493
|
+
const currentVolume = this.volumes[this.volumes.length - 1];
|
|
494
|
+
const volumeRatio = currentVolume / analysis.avgVolume;
|
|
495
|
+
const currentDrawdown = (analysis.sessionHigh - analysis.currentPrice) / analysis.sessionHigh * 100;
|
|
496
|
+
const rangeWidth = (analysis.sessionHigh - analysis.sessionLow) / analysis.sessionLow * 100;
|
|
497
|
+
const priceVsVWAP = (analysis.currentPrice - analysis.trueVWAP) / analysis.trueVWAP * 100;
|
|
498
|
+
const totalScore = signals.bullishSignals - signals.bearishSignals;
|
|
499
|
+
const overallSignal = totalScore > 0 ? "BULLISH_BIAS" : totalScore < 0 ? "BEARISH_BIAS" : "NEUTRAL";
|
|
500
|
+
const targetEntry = Math.max(analysis.sessionLow * 1.005, analysis.trueVWAP * 0.998);
|
|
501
|
+
const stopLoss = analysis.sessionLow * 0.995;
|
|
502
|
+
const profitTarget = analysis.sessionHigh * 0.995;
|
|
503
|
+
const riskRewardRatio = (profitTarget - analysis.currentPrice) / (analysis.currentPrice - stopLoss);
|
|
504
|
+
return {
|
|
505
|
+
symbol,
|
|
506
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
507
|
+
marketStructure: {
|
|
508
|
+
currentPrice: analysis.currentPrice,
|
|
509
|
+
startPrice: analysis.startPrice,
|
|
510
|
+
sessionHigh: analysis.sessionHigh,
|
|
511
|
+
sessionLow: analysis.sessionLow,
|
|
512
|
+
rangeWidth,
|
|
513
|
+
totalVolume: analysis.totalVolume,
|
|
514
|
+
sessionPerformance: analysis.sessionReturn,
|
|
515
|
+
positionInRange: analysis.pricePosition
|
|
516
|
+
},
|
|
517
|
+
volatility: {
|
|
518
|
+
impliedVolatility: analysis.volatility,
|
|
519
|
+
maxDrawdown: analysis.maxDrawdown * 100,
|
|
520
|
+
currentDrawdown
|
|
521
|
+
},
|
|
522
|
+
technicalIndicators: {
|
|
523
|
+
sma5: currentSMA5,
|
|
524
|
+
sma10: currentSMA10,
|
|
525
|
+
ema8: currentEMA8,
|
|
526
|
+
ema21: currentEMA21,
|
|
527
|
+
rsi: currentRSI,
|
|
528
|
+
bollingerBands: currentBB ? {
|
|
529
|
+
upper: currentBB.upper,
|
|
530
|
+
middle: currentBB.middle,
|
|
531
|
+
lower: currentBB.lower,
|
|
532
|
+
position: (analysis.currentPrice - currentBB.lower) / (currentBB.upper - currentBB.lower) * 100
|
|
533
|
+
} : null
|
|
534
|
+
},
|
|
535
|
+
volumeAnalysis: {
|
|
536
|
+
currentVolume,
|
|
537
|
+
averageVolume: Math.round(analysis.avgVolume),
|
|
538
|
+
volumeRatio,
|
|
539
|
+
trueVWAP: analysis.trueVWAP,
|
|
540
|
+
priceVsVWAP
|
|
541
|
+
},
|
|
542
|
+
momentum: {
|
|
543
|
+
momentum5: analysis.momentum5,
|
|
544
|
+
momentum10: analysis.momentum10,
|
|
545
|
+
sessionROC: analysis.sessionReturn
|
|
546
|
+
},
|
|
547
|
+
tradingSignals: {
|
|
548
|
+
...signals,
|
|
549
|
+
overallSignal,
|
|
550
|
+
signalScore: totalScore
|
|
551
|
+
},
|
|
552
|
+
riskManagement: {
|
|
553
|
+
targetEntry,
|
|
554
|
+
stopLoss,
|
|
555
|
+
profitTarget,
|
|
556
|
+
riskRewardRatio
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
var createTechnicalAnalysisHandler = (marketDataPrices) => {
|
|
562
|
+
return async (args) => {
|
|
563
|
+
try {
|
|
564
|
+
const symbol = args.symbol;
|
|
565
|
+
const priceHistory = marketDataPrices.get(symbol) || [];
|
|
566
|
+
if (priceHistory.length === 0) {
|
|
567
|
+
return {
|
|
568
|
+
content: [
|
|
569
|
+
{
|
|
570
|
+
type: "text",
|
|
571
|
+
text: `No price data available for ${symbol}. Please request market data first.`,
|
|
572
|
+
uri: "technicalAnalysis"
|
|
573
|
+
}
|
|
574
|
+
]
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
const analyzer = new TechnicalAnalyzer(priceHistory);
|
|
578
|
+
const analysis = analyzer.generateJSONAnalysis(symbol);
|
|
579
|
+
return {
|
|
580
|
+
content: [
|
|
581
|
+
{
|
|
582
|
+
type: "text",
|
|
583
|
+
text: `Technical Analysis for ${symbol}:
|
|
584
|
+
|
|
585
|
+
${JSON.stringify(analysis, null, 2)}`,
|
|
586
|
+
uri: "technicalAnalysis"
|
|
587
|
+
}
|
|
588
|
+
]
|
|
589
|
+
};
|
|
590
|
+
} catch (error) {
|
|
591
|
+
return {
|
|
592
|
+
content: [
|
|
593
|
+
{
|
|
594
|
+
type: "text",
|
|
595
|
+
text: `Error performing technical analysis: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
596
|
+
uri: "technicalAnalysis"
|
|
597
|
+
}
|
|
598
|
+
],
|
|
599
|
+
isError: true
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
};
|
|
604
|
+
|
|
282
605
|
// src/tools/marketData.ts
|
|
283
606
|
import { Field, Fields, MDEntryType, Messages } from "fixparser";
|
|
284
607
|
import QuickChart from "quickchart-js";
|
|
285
608
|
var createMarketDataRequestHandler = (parser, pendingRequests) => {
|
|
286
609
|
return async (args) => {
|
|
287
610
|
try {
|
|
611
|
+
parser.logger.log({
|
|
612
|
+
level: "info",
|
|
613
|
+
message: `Sending market data request for symbols: ${args.symbols.join(", ")}`
|
|
614
|
+
});
|
|
288
615
|
const response = new Promise((resolve) => {
|
|
289
616
|
pendingRequests.set(args.mdReqID, resolve);
|
|
617
|
+
parser.logger.log({
|
|
618
|
+
level: "info",
|
|
619
|
+
message: `Registered callback for market data request ID: ${args.mdReqID}`
|
|
620
|
+
});
|
|
290
621
|
});
|
|
291
622
|
const entryTypes = args.mdEntryTypes || [
|
|
292
623
|
MDEntryType.Bid,
|
|
@@ -355,6 +686,10 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
|
|
|
355
686
|
});
|
|
356
687
|
const mdr = parser.createMessage(...messageFields);
|
|
357
688
|
if (!parser.connected) {
|
|
689
|
+
parser.logger.log({
|
|
690
|
+
level: "error",
|
|
691
|
+
message: "Not connected. Cannot send market data request."
|
|
692
|
+
});
|
|
358
693
|
return {
|
|
359
694
|
content: [
|
|
360
695
|
{
|
|
@@ -366,8 +701,16 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
|
|
|
366
701
|
isError: true
|
|
367
702
|
};
|
|
368
703
|
}
|
|
704
|
+
parser.logger.log({
|
|
705
|
+
level: "info",
|
|
706
|
+
message: `Sending market data request message: ${JSON.stringify(mdr?.toFIXJSON())}`
|
|
707
|
+
});
|
|
369
708
|
parser.send(mdr);
|
|
370
709
|
const fixData = await response;
|
|
710
|
+
parser.logger.log({
|
|
711
|
+
level: "info",
|
|
712
|
+
message: `Received market data response for request ID: ${args.mdReqID}`
|
|
713
|
+
});
|
|
371
714
|
return {
|
|
372
715
|
content: [
|
|
373
716
|
{
|
|
@@ -416,6 +759,9 @@ var createGetStockGraphHandler = (marketDataPrices) => {
|
|
|
416
759
|
const offerData = priceHistory.map((point) => point.offer);
|
|
417
760
|
const spreadData = priceHistory.map((point) => point.spread);
|
|
418
761
|
const volumeData = priceHistory.map((point) => point.volume);
|
|
762
|
+
const tradeData = priceHistory.map((point) => point.trade);
|
|
763
|
+
const vwapData = priceHistory.map((point) => point.vwap);
|
|
764
|
+
const twapData = priceHistory.map((point) => point.twap);
|
|
419
765
|
const config = {
|
|
420
766
|
type: "line",
|
|
421
767
|
data: {
|
|
@@ -445,6 +791,30 @@ var createGetStockGraphHandler = (marketDataPrices) => {
|
|
|
445
791
|
fill: false,
|
|
446
792
|
tension: 0.4
|
|
447
793
|
},
|
|
794
|
+
{
|
|
795
|
+
label: "Trade",
|
|
796
|
+
data: tradeData,
|
|
797
|
+
borderColor: "#ffc107",
|
|
798
|
+
backgroundColor: "rgba(255, 193, 7, 0.1)",
|
|
799
|
+
fill: false,
|
|
800
|
+
tension: 0.4
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
label: "VWAP",
|
|
804
|
+
data: vwapData,
|
|
805
|
+
borderColor: "#17a2b8",
|
|
806
|
+
backgroundColor: "rgba(23, 162, 184, 0.1)",
|
|
807
|
+
fill: false,
|
|
808
|
+
tension: 0.4
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
label: "TWAP",
|
|
812
|
+
data: twapData,
|
|
813
|
+
borderColor: "#6610f2",
|
|
814
|
+
backgroundColor: "rgba(102, 16, 242, 0.1)",
|
|
815
|
+
fill: false,
|
|
816
|
+
tension: 0.4
|
|
817
|
+
},
|
|
448
818
|
{
|
|
449
819
|
label: "Volume",
|
|
450
820
|
data: volumeData,
|
|
@@ -490,7 +860,7 @@ var createGetStockGraphHandler = (marketDataPrices) => {
|
|
|
490
860
|
content: [
|
|
491
861
|
{
|
|
492
862
|
type: "text",
|
|
493
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to generate
|
|
863
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to generate graph"}`,
|
|
494
864
|
uri: "getStockGraph"
|
|
495
865
|
}
|
|
496
866
|
],
|
|
@@ -528,7 +898,48 @@ var createGetStockPriceHistoryHandler = (marketDataPrices) => {
|
|
|
528
898
|
bid: point.bid,
|
|
529
899
|
offer: point.offer,
|
|
530
900
|
spread: point.spread,
|
|
531
|
-
volume: point.volume
|
|
901
|
+
volume: point.volume,
|
|
902
|
+
trade: point.trade,
|
|
903
|
+
indexValue: point.indexValue,
|
|
904
|
+
openingPrice: point.openingPrice,
|
|
905
|
+
closingPrice: point.closingPrice,
|
|
906
|
+
settlementPrice: point.settlementPrice,
|
|
907
|
+
tradingSessionHighPrice: point.tradingSessionHighPrice,
|
|
908
|
+
tradingSessionLowPrice: point.tradingSessionLowPrice,
|
|
909
|
+
vwap: point.vwap,
|
|
910
|
+
imbalance: point.imbalance,
|
|
911
|
+
openInterest: point.openInterest,
|
|
912
|
+
compositeUnderlyingPrice: point.compositeUnderlyingPrice,
|
|
913
|
+
simulatedSellPrice: point.simulatedSellPrice,
|
|
914
|
+
simulatedBuyPrice: point.simulatedBuyPrice,
|
|
915
|
+
marginRate: point.marginRate,
|
|
916
|
+
midPrice: point.midPrice,
|
|
917
|
+
emptyBook: point.emptyBook,
|
|
918
|
+
settleHighPrice: point.settleHighPrice,
|
|
919
|
+
settleLowPrice: point.settleLowPrice,
|
|
920
|
+
priorSettlePrice: point.priorSettlePrice,
|
|
921
|
+
sessionHighBid: point.sessionHighBid,
|
|
922
|
+
sessionLowOffer: point.sessionLowOffer,
|
|
923
|
+
earlyPrices: point.earlyPrices,
|
|
924
|
+
auctionClearingPrice: point.auctionClearingPrice,
|
|
925
|
+
swapValueFactor: point.swapValueFactor,
|
|
926
|
+
dailyValueAdjustmentForLongPositions: point.dailyValueAdjustmentForLongPositions,
|
|
927
|
+
cumulativeValueAdjustmentForLongPositions: point.cumulativeValueAdjustmentForLongPositions,
|
|
928
|
+
dailyValueAdjustmentForShortPositions: point.dailyValueAdjustmentForShortPositions,
|
|
929
|
+
cumulativeValueAdjustmentForShortPositions: point.cumulativeValueAdjustmentForShortPositions,
|
|
930
|
+
fixingPrice: point.fixingPrice,
|
|
931
|
+
cashRate: point.cashRate,
|
|
932
|
+
recoveryRate: point.recoveryRate,
|
|
933
|
+
recoveryRateForLong: point.recoveryRateForLong,
|
|
934
|
+
recoveryRateForShort: point.recoveryRateForShort,
|
|
935
|
+
marketBid: point.marketBid,
|
|
936
|
+
marketOffer: point.marketOffer,
|
|
937
|
+
shortSaleMinPrice: point.shortSaleMinPrice,
|
|
938
|
+
previousClosingPrice: point.previousClosingPrice,
|
|
939
|
+
thresholdLimitPriceBanding: point.thresholdLimitPriceBanding,
|
|
940
|
+
dailyFinancingValue: point.dailyFinancingValue,
|
|
941
|
+
accruedFinancingValue: point.accruedFinancingValue,
|
|
942
|
+
twap: point.twap
|
|
532
943
|
}))
|
|
533
944
|
},
|
|
534
945
|
null,
|
|
@@ -543,7 +954,7 @@ var createGetStockPriceHistoryHandler = (marketDataPrices) => {
|
|
|
543
954
|
content: [
|
|
544
955
|
{
|
|
545
956
|
type: "text",
|
|
546
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to get
|
|
957
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to get price history"}`,
|
|
547
958
|
uri: "getStockPriceHistory"
|
|
548
959
|
}
|
|
549
960
|
],
|
|
@@ -651,7 +1062,7 @@ Parameters verified:
|
|
|
651
1062
|
- Symbol: ${args.symbol}
|
|
652
1063
|
- TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
|
|
653
1064
|
|
|
654
|
-
To execute this order, call the executeOrder tool with these exact same parameters
|
|
1065
|
+
To execute this order, call the executeOrder tool with these exact same parameters. Important: The user has to explicitly confirm before executeOrder is called!`,
|
|
655
1066
|
uri: "verifyOrder"
|
|
656
1067
|
}
|
|
657
1068
|
]
|
|
@@ -847,16 +1258,16 @@ var createToolHandlers = (parser, verifiedOrders, pendingRequests, marketDataPri
|
|
|
847
1258
|
executeOrder: createExecuteOrderHandler(parser, verifiedOrders, pendingRequests),
|
|
848
1259
|
marketDataRequest: createMarketDataRequestHandler(parser, pendingRequests),
|
|
849
1260
|
getStockGraph: createGetStockGraphHandler(marketDataPrices),
|
|
850
|
-
getStockPriceHistory: createGetStockPriceHistoryHandler(marketDataPrices)
|
|
1261
|
+
getStockPriceHistory: createGetStockPriceHistoryHandler(marketDataPrices),
|
|
1262
|
+
technicalAnalysis: createTechnicalAnalysisHandler(marketDataPrices)
|
|
851
1263
|
});
|
|
852
1264
|
|
|
853
1265
|
// src/utils/messageHandler.ts
|
|
854
1266
|
import { Fields as Fields3, MDEntryType as MDEntryType2, Messages as Messages3 } from "fixparser";
|
|
1267
|
+
function getEnumValue(enumObj, name) {
|
|
1268
|
+
return enumObj[name] || name;
|
|
1269
|
+
}
|
|
855
1270
|
function handleMessage(message, parser, pendingRequests, marketDataPrices, maxPriceHistory, onPriceUpdate) {
|
|
856
|
-
parser.logger.log({
|
|
857
|
-
level: "info",
|
|
858
|
-
message: `MCP Server received message: ${message.messageType}: ${message.description}`
|
|
859
|
-
});
|
|
860
1271
|
const msgType = message.messageType;
|
|
861
1272
|
if (msgType === Messages3.MarketDataSnapshotFullRefresh || msgType === Messages3.MarketDataIncrementalRefresh) {
|
|
862
1273
|
const symbol = message.getField(Fields3.Symbol)?.value;
|
|
@@ -914,7 +1325,8 @@ function handleMessage(message, parser, pendingRequests, marketDataPrices, maxPr
|
|
|
914
1325
|
const entryType = entry.MDEntryType;
|
|
915
1326
|
const price = entry.MDEntryPx ? Number.parseFloat(entry.MDEntryPx) : 0;
|
|
916
1327
|
const size = entry.MDEntrySize ? Number.parseFloat(entry.MDEntrySize) : 0;
|
|
917
|
-
|
|
1328
|
+
const enumValue = getEnumValue(MDEntryType2, entryType);
|
|
1329
|
+
switch (enumValue) {
|
|
918
1330
|
case MDEntryType2.Bid:
|
|
919
1331
|
data.bid = price;
|
|
920
1332
|
break;
|
|
@@ -1181,25 +1593,13 @@ var MCPRemote = class extends MCPBase {
|
|
|
1181
1593
|
}
|
|
1182
1594
|
});
|
|
1183
1595
|
this.httpServer = createServer(async (req, res) => {
|
|
1184
|
-
this.logger?.log({
|
|
1185
|
-
level: "info",
|
|
1186
|
-
message: `Incoming request: ${req.method} ${req.url}`
|
|
1187
|
-
});
|
|
1188
1596
|
if (!req.url || !req.method) {
|
|
1189
|
-
this.logger?.log({
|
|
1190
|
-
level: "error",
|
|
1191
|
-
message: "Invalid request: missing URL or method"
|
|
1192
|
-
});
|
|
1193
1597
|
res.writeHead(400);
|
|
1194
1598
|
res.end("Bad Request");
|
|
1195
1599
|
return;
|
|
1196
1600
|
}
|
|
1197
1601
|
if (req.url === "/mcp") {
|
|
1198
1602
|
const sessionId = req.headers["mcp-session-id"];
|
|
1199
|
-
this.logger?.log({
|
|
1200
|
-
level: "info",
|
|
1201
|
-
message: `MCP request received. Session ID: ${sessionId || "none"}, headers: ${req.headers}`
|
|
1202
|
-
});
|
|
1203
1603
|
if (req.method === "POST") {
|
|
1204
1604
|
const bodyChunks = [];
|
|
1205
1605
|
req.on("data", (chunk) => {
|
|
@@ -1210,47 +1610,23 @@ var MCPRemote = class extends MCPBase {
|
|
|
1210
1610
|
const body = Buffer.concat(bodyChunks).toString();
|
|
1211
1611
|
try {
|
|
1212
1612
|
parsed = JSON.parse(body);
|
|
1213
|
-
this.logger?.log({
|
|
1214
|
-
level: "info",
|
|
1215
|
-
message: `Parsed request body: ${JSON.stringify(parsed)}`
|
|
1216
|
-
});
|
|
1217
1613
|
} catch (err) {
|
|
1218
|
-
this.logger?.log({
|
|
1219
|
-
level: "error",
|
|
1220
|
-
message: `Failed to parse JSON body: ${err}`
|
|
1221
|
-
});
|
|
1222
1614
|
res.writeHead(400);
|
|
1223
1615
|
res.end(JSON.stringify({ error: "Invalid JSON" }));
|
|
1224
1616
|
return;
|
|
1225
1617
|
}
|
|
1226
1618
|
let transport;
|
|
1227
1619
|
if (sessionId && transports[sessionId]) {
|
|
1228
|
-
this.logger?.log({
|
|
1229
|
-
level: "info",
|
|
1230
|
-
message: `Using existing transport for session: ${sessionId}`
|
|
1231
|
-
});
|
|
1232
1620
|
transport = transports[sessionId];
|
|
1233
1621
|
} else if (!sessionId && req.method === "POST" && isInitializeRequest(parsed)) {
|
|
1234
|
-
this.logger?.log({
|
|
1235
|
-
level: "info",
|
|
1236
|
-
message: "Creating new transport for initialization request"
|
|
1237
|
-
});
|
|
1238
1622
|
transport = new StreamableHTTPServerTransport({
|
|
1239
1623
|
sessionIdGenerator: () => randomUUID(),
|
|
1240
1624
|
onsessioninitialized: (sessionId2) => {
|
|
1241
|
-
this.logger?.log({
|
|
1242
|
-
level: "info",
|
|
1243
|
-
message: `New session initialized: ${sessionId2}`
|
|
1244
|
-
});
|
|
1245
1625
|
transports[sessionId2] = transport;
|
|
1246
1626
|
}
|
|
1247
1627
|
});
|
|
1248
1628
|
transport.onclose = () => {
|
|
1249
1629
|
if (transport.sessionId) {
|
|
1250
|
-
this.logger?.log({
|
|
1251
|
-
level: "info",
|
|
1252
|
-
message: `Session closed: ${transport.sessionId}`
|
|
1253
|
-
});
|
|
1254
1630
|
delete transports[transport.sessionId];
|
|
1255
1631
|
}
|
|
1256
1632
|
};
|
|
@@ -1261,10 +1637,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1261
1637
|
this.setupTools();
|
|
1262
1638
|
await this.mcpServer.connect(transport);
|
|
1263
1639
|
} else {
|
|
1264
|
-
this.logger?.log({
|
|
1265
|
-
level: "error",
|
|
1266
|
-
message: "Invalid request: No valid session ID provided"
|
|
1267
|
-
});
|
|
1268
1640
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1269
1641
|
res.end(
|
|
1270
1642
|
JSON.stringify({
|
|
@@ -1280,10 +1652,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1280
1652
|
}
|
|
1281
1653
|
try {
|
|
1282
1654
|
await transport.handleRequest(req, res, parsed);
|
|
1283
|
-
this.logger?.log({
|
|
1284
|
-
level: "info",
|
|
1285
|
-
message: "Request handled successfully"
|
|
1286
|
-
});
|
|
1287
1655
|
} catch (error) {
|
|
1288
1656
|
this.logger?.log({
|
|
1289
1657
|
level: "error",
|
|
@@ -1294,10 +1662,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1294
1662
|
});
|
|
1295
1663
|
} else if (req.method === "GET" || req.method === "DELETE") {
|
|
1296
1664
|
if (!sessionId || !transports[sessionId]) {
|
|
1297
|
-
this.logger?.log({
|
|
1298
|
-
level: "error",
|
|
1299
|
-
message: `Invalid session ID for ${req.method} request: ${sessionId}`
|
|
1300
|
-
});
|
|
1301
1665
|
res.writeHead(400);
|
|
1302
1666
|
res.end("Invalid or missing session ID");
|
|
1303
1667
|
return;
|
|
@@ -1305,10 +1669,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1305
1669
|
const transport = transports[sessionId];
|
|
1306
1670
|
try {
|
|
1307
1671
|
await transport.handleRequest(req, res);
|
|
1308
|
-
this.logger?.log({
|
|
1309
|
-
level: "info",
|
|
1310
|
-
message: `${req.method} request handled successfully for session: ${sessionId}`
|
|
1311
|
-
});
|
|
1312
1672
|
} catch (error) {
|
|
1313
1673
|
this.logger?.log({
|
|
1314
1674
|
level: "error",
|
|
@@ -1325,10 +1685,6 @@ var MCPRemote = class extends MCPBase {
|
|
|
1325
1685
|
res.end("Method Not Allowed");
|
|
1326
1686
|
}
|
|
1327
1687
|
} else {
|
|
1328
|
-
this.logger?.log({
|
|
1329
|
-
level: "error",
|
|
1330
|
-
message: `Not found: ${req.url}`
|
|
1331
|
-
});
|
|
1332
1688
|
res.writeHead(404);
|
|
1333
1689
|
res.end("Not Found");
|
|
1334
1690
|
}
|