154 lines
6.2 KiB
TypeScript
154 lines
6.2 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { ApiServer } from './src/services/apiServer.js';
|
|
|
|
type ChatContext = Parameters<any>[1];
|
|
|
|
function createServerHarness() {
|
|
return Object.create(ApiServer.prototype) as any;
|
|
}
|
|
|
|
function buildContext(): ChatContext {
|
|
return {
|
|
profiles: [
|
|
{
|
|
id: 'p1',
|
|
name: 'High Risk Scalper',
|
|
allocated_capital: 1000,
|
|
risk_per_trade_percent: 1.25,
|
|
symbols: 'BTC/USDT',
|
|
is_active: true,
|
|
strategy_config: {
|
|
rules: [{ ruleId: 'TrendBiasRule', enabled: true }],
|
|
riskLimits: { maxDailyLossUsd: 50, maxOpenTrades: 2, maxConsecutiveLosses: 2 },
|
|
execution: { orderType: 'market', cooldownMinutes: 20, entryMode: 'both' }
|
|
}
|
|
}
|
|
],
|
|
runtime: {
|
|
positions: [
|
|
{
|
|
symbol: 'BTC/USDT',
|
|
side: 'BUY',
|
|
entryPrice: 60000,
|
|
currentPrice: 61500,
|
|
unrealizedPnl: 375,
|
|
unrealizedPnlPercent: 2.5,
|
|
size: 0.25,
|
|
profileId: 'p1',
|
|
profileName: 'High Risk Scalper',
|
|
tradeId: 'trade-1',
|
|
takeProfit: 63000,
|
|
stopLoss: 58500,
|
|
}
|
|
],
|
|
signalContexts: [
|
|
{
|
|
symbol: 'BTC/USDT',
|
|
profileId: 'p1',
|
|
profileName: 'High Risk Scalper',
|
|
signal: 'BUY',
|
|
passed: false,
|
|
reason: 'Waiting for stronger rule alignment.',
|
|
executionStatus: 'SKIPPED',
|
|
executionCode: 'rule_ratio_not_met',
|
|
executionReason: 'Only 2 of 4 voting rules passed.',
|
|
orderId: 'ord-1',
|
|
}
|
|
],
|
|
recentOrders: [],
|
|
recentHistory: [
|
|
{ symbol: 'BTC/USDT', side: 'BUY', pnl: 125, reason: 'Simple target hit', tradeId: 'h1' },
|
|
{ symbol: 'ETH/USDT', side: 'BUY', pnl: -40, reason: 'Stop loss', tradeId: 'h2' },
|
|
],
|
|
orderFailures: [
|
|
{
|
|
symbol: 'BTC/USDT',
|
|
side: 'BUY',
|
|
qty: 0.1,
|
|
reason: 'Duplicate entry request blocked',
|
|
tradeId: 'trade-1',
|
|
timestamp: Date.now(),
|
|
}
|
|
],
|
|
operationalEvents: [
|
|
{
|
|
id: 'evt-1',
|
|
type: 'RECONCILIATION_ALERT',
|
|
severity: 'WARN',
|
|
message: 'Reconciliation mismatch detected for BTC/USDT',
|
|
symbol: 'BTC/USDT',
|
|
profileId: 'p1',
|
|
tradeId: 'trade-1',
|
|
timestamp: Date.now(),
|
|
}
|
|
],
|
|
accountSnapshot: null,
|
|
health: {
|
|
tradingLoopHealthy: true,
|
|
orderSyncHealthy: true,
|
|
reconciliationLoopHealthy: false,
|
|
reconciliationMismatchCount: 2,
|
|
reconciliationMissingFromExchange: 1,
|
|
reconciliationMissingInDb: 0,
|
|
reconciliationNoGoTrades: 1,
|
|
reconciliationParityQuarantinedTrades: 1,
|
|
reconciliationParityAutoClosedTrades: 0,
|
|
reconciliationIntegrityWatchdogTriggered: false,
|
|
lockContentionCount: 0,
|
|
reconciliationLockContentionCount: 0,
|
|
},
|
|
settings: {
|
|
executionMode: 'paper',
|
|
totalCapital: 1000,
|
|
riskPerTrade: 1.25,
|
|
maxOpenTrades: 2,
|
|
isAlgoEnabled: true,
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function testTradePlanRecommendation() {
|
|
const server = createServerHarness();
|
|
const response = server.buildLocalChatFallback('Recommend a trade plan for my BTC position', buildContext());
|
|
assert.equal(response.action, 'recommend_trade_plan');
|
|
assert.ok(Array.isArray(response.insights) && response.insights.length > 0, 'trade-plan recommendation must include insights');
|
|
assert.ok(response.quickLinks?.some((link: any) => link.kind === 'plans'), 'trade-plan recommendation must include a plans link');
|
|
console.log('[PASS] chat fallback builds trade-plan recommendation.');
|
|
}
|
|
|
|
function testReconciliationFollowup() {
|
|
const server = createServerHarness();
|
|
const response = server.buildLocalChatFallback('What should I do about reconciliation right now?', buildContext());
|
|
assert.equal(response.action, 'recommend_reconciliation_followup');
|
|
assert.ok(response.quickLinks?.some((link: any) => link.kind === 'settings'), 'reconciliation follow-up must include settings/admin quick link');
|
|
assert.ok(response.insights?.some((entry: string) => entry.includes('Mismatch count')), 'reconciliation follow-up must include mismatch insights');
|
|
console.log('[PASS] chat fallback builds reconciliation follow-up.');
|
|
}
|
|
|
|
function testRecentTradeReview() {
|
|
const server = createServerHarness();
|
|
const response = server.buildLocalChatFallback('Review my recent trades', buildContext());
|
|
assert.equal(response.action, 'review_recent_trades');
|
|
assert.ok(response.insights?.some((entry: string) => entry.includes('BTC/USDT')), 'recent trade review must include trade evidence');
|
|
console.log('[PASS] chat fallback builds recent-trade review.');
|
|
}
|
|
|
|
function testWaitingExplanation() {
|
|
const server = createServerHarness();
|
|
const response = server.buildLocalChatFallback('Why did no trade fire for BTC?', buildContext());
|
|
assert.equal(response.action, 'explain_waiting');
|
|
assert.ok(response.insights?.some((entry: string) => entry.includes('Execution code')), 'waiting explanation must include execution insight');
|
|
console.log('[PASS] chat fallback builds waiting explanation.');
|
|
}
|
|
|
|
function main() {
|
|
testTradePlanRecommendation();
|
|
testReconciliationFollowup();
|
|
testRecentTradeReview();
|
|
testWaitingExplanation();
|
|
console.log('Chat copilot fallback checks passed');
|
|
}
|
|
|
|
main();
|