trading-platform-frontend-v2/src/services/llmAgentService.ts
rckrdmrd 5b53c2539a feat: Initial commit - Trading Platform Frontend
React frontend with:
- Authentication UI
- Trading dashboard
- ML signals display
- Portfolio management

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 04:30:39 -06:00

533 lines
13 KiB
TypeScript

/**
* LLM Agent Service
* Client for connecting to the LLM Agent API (Predictions & Backtesting)
*
* @version 1.0.0
*/
const LLM_AGENT_URL = import.meta.env.VITE_LLM_AGENT_URL || 'http://localhost:3085';
// ============================================================================
// Types - Predictions
// ============================================================================
export interface PredictionRequest {
symbol: string;
timeframe?: string;
}
export interface AMDPhaseInfo {
phase: string;
confidence: number;
support: number;
resistance: number;
}
export interface SignalInfo {
direction: 'LONG' | 'SHORT' | 'HOLD';
confidence: number;
entry_price: number;
stop_loss: number;
take_profit: number;
}
export interface RangePredictionInfo {
predicted_high: number;
predicted_low: number;
expected_range_pct: number;
}
export interface ICTContextInfo {
market_bias: string;
order_blocks: Array<{ type: string; price: number }>;
fair_value_gaps: Array<{ type: string; high: number; low: number }>;
}
export interface PredictionResponse {
symbol: string;
timeframe: string;
timestamp: string;
current_price: number;
amd_phase: AMDPhaseInfo;
signal: SignalInfo;
range_prediction: RangePredictionInfo;
ict_context: ICTContextInfo;
confluence_score: number;
explanation: string;
risk_assessment?: {
allowed: boolean;
recommended_size: number;
position_risk_pct: number;
checks: Record<string, boolean>;
};
}
export interface ActiveSignal {
symbol: string;
direction: string;
confidence: number;
confluence_score: number;
entry_price: number;
stop_loss: number;
take_profit: number;
timestamp: string;
}
export interface RiskSummary {
level: string;
limits: {
max_position_size_pct: number;
max_daily_drawdown_pct: number;
max_total_exposure_pct: number;
};
current_state: {
daily_pnl: number;
total_exposure_pct: number;
open_trades: number;
};
circuit_breaker_active: boolean;
}
// ============================================================================
// Types - Backtesting
// ============================================================================
export interface BacktestRequest {
initial_balance: number;
risk_per_trade_pct: number;
max_open_trades: number;
min_confluence_score: number;
min_confidence: number;
symbols: string[];
timeframe: string;
days_back: number;
use_amd_filter: boolean;
use_killzone_filter: boolean;
}
export interface BacktestStatus {
id: string;
status: 'pending' | 'running' | 'completed' | 'failed';
progress: number;
started_at?: string;
completed_at?: string;
error?: string;
}
export interface BacktestSummary {
id: string;
initial_balance: number;
final_balance: number;
total_return: number;
total_return_pct: number;
total_trades: number;
win_rate: number;
profit_factor: number;
max_drawdown_pct: number;
sharpe_ratio: number;
duration_days: number;
}
export interface BacktestTrade {
id: string;
symbol: string;
direction: string;
entry_price: number;
exit_price: number;
entry_time: string;
exit_time: string;
pnl: number;
pnl_pips: number;
status: string;
confidence: number;
amd_phase: string;
confluence_score: number;
}
export interface EquityCurvePoint {
timestamp: string;
equity: number;
balance: number;
drawdown_pct: number;
}
export interface BacktestResult {
summary: {
initial_balance: number;
final_balance: number;
total_return: number;
total_return_pct: number;
duration_days: number;
};
performance: {
total_trades: number;
winning_trades: number;
losing_trades: number;
win_rate: number;
profit_factor: number;
sharpe_ratio: number;
sortino_ratio: number;
};
risk: {
max_drawdown: number;
max_drawdown_pct: number;
avg_winner: number;
avg_loser: number;
largest_winner: number;
largest_loser: number;
};
activity: {
avg_trade_pnl: number;
avg_trade_duration_hours: number;
trades_per_day: number;
};
monthly_returns: Record<string, number>;
trades_by_phase: Record<string, { count: number; wins: number; pnl: number; win_rate: number }>;
trades_by_symbol: Record<string, { count: number; wins: number; pnl: number; win_rate: number }>;
equity_curve: EquityCurvePoint[];
trades: BacktestTrade[];
}
// ============================================================================
// Predictions API
// ============================================================================
/**
* Analyze a symbol with all ML models
*/
export async function analyzeSymbol(
symbol: string,
timeframe: string = '5m'
): Promise<PredictionResponse | null> {
try {
const response = await fetch(`${LLM_AGENT_URL}/api/v1/predictions/analyze`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ symbol, timeframe }),
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error analyzing symbol:', error);
return null;
}
}
/**
* Get active signals above minimum confluence
*/
export async function getActiveSignals(
minConfluence: number = 0.6
): Promise<ActiveSignal[]> {
try {
const response = await fetch(
`${LLM_AGENT_URL}/api/v1/predictions/active-signals?min_confluence=${minConfluence}`
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching active signals:', error);
return [];
}
}
/**
* Get current risk summary
*/
export async function getRiskSummary(): Promise<RiskSummary | null> {
try {
const response = await fetch(`${LLM_AGENT_URL}/api/v1/predictions/risk-summary`);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching risk summary:', error);
return null;
}
}
/**
* Set risk level
*/
export async function setRiskLevel(
level: 'minimal' | 'conservative' | 'moderate' | 'aggressive'
): Promise<boolean> {
try {
const response = await fetch(
`${LLM_AGENT_URL}/api/v1/predictions/risk-level?level=${level}`,
{ method: 'POST' }
);
return response.ok;
} catch (error) {
console.error('Error setting risk level:', error);
return false;
}
}
/**
* Validate a trade against risk rules
*/
export async function validateTrade(params: {
symbol: string;
side: 'BUY' | 'SELL';
size: number;
entry_price: number;
stop_loss: number;
pip_value?: number;
}): Promise<{
allowed: boolean;
recommended_size: number;
position_risk_pct: number;
checks: Record<string, boolean>;
formatted_message: string;
} | null> {
try {
const response = await fetch(`${LLM_AGENT_URL}/api/v1/predictions/validate-trade`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params),
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error validating trade:', error);
return null;
}
}
// ============================================================================
// Backtesting API
// ============================================================================
/**
* Start a new backtest (background)
*/
export async function startBacktest(
params: Partial<BacktestRequest>
): Promise<BacktestStatus | null> {
try {
const request: BacktestRequest = {
initial_balance: params.initial_balance || 1000,
risk_per_trade_pct: params.risk_per_trade_pct || 1.0,
max_open_trades: params.max_open_trades || 3,
min_confluence_score: params.min_confluence_score || 0.65,
min_confidence: params.min_confidence || 0.60,
symbols: params.symbols || ['XAUUSD', 'EURUSD', 'GBPUSD'],
timeframe: params.timeframe || '1H',
days_back: params.days_back || 365,
use_amd_filter: params.use_amd_filter ?? true,
use_killzone_filter: params.use_killzone_filter ?? true,
};
const response = await fetch(`${LLM_AGENT_URL}/api/v1/backtesting/run`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error starting backtest:', error);
return null;
}
}
/**
* Run a quick synchronous backtest (< 90 days)
*/
export async function runQuickBacktest(params: {
initial_balance?: number;
days?: number;
symbol?: string;
}): Promise<BacktestResult | null> {
try {
const queryParams = new URLSearchParams({
initial_balance: String(params.initial_balance || 1000),
days: String(params.days || 30),
symbol: params.symbol || 'XAUUSD',
});
const response = await fetch(
`${LLM_AGENT_URL}/api/v1/backtesting/quick-test?${queryParams}`
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error running quick backtest:', error);
return null;
}
}
/**
* Get backtest status
*/
export async function getBacktestStatus(id: string): Promise<BacktestStatus | null> {
try {
const response = await fetch(`${LLM_AGENT_URL}/api/v1/backtesting/status/${id}`);
if (!response.ok) {
if (response.status === 404) return null;
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching backtest status:', error);
return null;
}
}
/**
* Get backtest results
*/
export async function getBacktestResults(id: string): Promise<BacktestResult | null> {
try {
const response = await fetch(`${LLM_AGENT_URL}/api/v1/backtesting/results/${id}`);
if (!response.ok) {
if (response.status === 404) return null;
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching backtest results:', error);
return null;
}
}
/**
* List recent backtests
*/
export async function listBacktests(limit: number = 10): Promise<BacktestSummary[]> {
try {
const response = await fetch(
`${LLM_AGENT_URL}/api/v1/backtesting/list?limit=${limit}`
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error listing backtests:', error);
return [];
}
}
/**
* Delete a backtest
*/
export async function deleteBacktest(id: string): Promise<boolean> {
try {
const response = await fetch(`${LLM_AGENT_URL}/api/v1/backtesting/${id}`, {
method: 'DELETE',
});
return response.ok;
} catch (error) {
console.error('Error deleting backtest:', error);
return false;
}
}
// ============================================================================
// Health Check
// ============================================================================
/**
* Check LLM Agent health
*/
export async function checkHealth(): Promise<{
llmAgent: boolean;
predictions: boolean;
backtesting: boolean;
}> {
const results = {
llmAgent: false,
predictions: false,
backtesting: false,
};
try {
const [rootRes, predictionsRes, backtestingRes] = await Promise.allSettled([
fetch(`${LLM_AGENT_URL}/`),
fetch(`${LLM_AGENT_URL}/api/v1/predictions/health`),
fetch(`${LLM_AGENT_URL}/api/v1/backtesting/health`),
]);
results.llmAgent = rootRes.status === 'fulfilled' && rootRes.value.ok;
results.predictions = predictionsRes.status === 'fulfilled' && predictionsRes.value.ok;
results.backtesting = backtestingRes.status === 'fulfilled' && backtestingRes.value.ok;
} catch (error) {
console.error('Error checking LLM Agent health:', error);
}
return results;
}
// ============================================================================
// Polling utilities
// ============================================================================
/**
* Poll for backtest completion
*/
export async function waitForBacktest(
id: string,
onProgress?: (progress: number) => void,
pollInterval: number = 2000,
timeout: number = 600000 // 10 minutes
): Promise<BacktestResult | null> {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const status = await getBacktestStatus(id);
if (!status) {
throw new Error('Backtest not found');
}
if (onProgress) {
onProgress(status.progress);
}
if (status.status === 'completed') {
return await getBacktestResults(id);
}
if (status.status === 'failed') {
throw new Error(status.error || 'Backtest failed');
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
throw new Error('Backtest timed out');
}