learning_ai_invt_trdg/backend/testChatCopilotFallbacks.ts

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();