/** * Trading Service * API client for trading and market data endpoints */ import axios from 'axios'; import type { Candle, Ticker, OrderBook, TradingSymbol, Interval, SMAData, EMAData, RSIData, MACDData, BollingerBandsData, Watchlist, WatchlistSymbolData, PaperAccount, PaperBalance, PaperPosition, PaperOrder, PaperTrade, CreateOrderInput, AccountSummary, } from '../types/trading.types'; // ============================================================================ // API Configuration // ============================================================================ const API_BASE_URL = import.meta.env?.VITE_API_URL || '/api/v1'; const api = axios.create({ baseURL: API_BASE_URL, headers: { 'Content-Type': 'application/json', }, }); // Add request interceptor for auth token api.interceptors.request.use( (config) => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error) ); // Add response interceptor for error handling api.interceptors.response.use( (response) => response, (error) => { if (error.response?.status === 401) { // Token expired or invalid localStorage.removeItem('token'); window.location.href = '/login'; } return Promise.reject(error); } ); // ============================================================================ // Market Data API // ============================================================================ /** * Get candlestick data (OHLCV) */ export async function getKlines( symbol: string, interval: Interval = '1h', limit: number = 500 ): Promise { try { const response = await api.get(`/trading/market/klines/${symbol}`, { params: { interval, limit }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch klines:', error); throw new Error('Failed to fetch chart data'); } } /** * Get current price for a symbol */ export async function getPrice(symbol: string): Promise { try { const response = await api.get(`/trading/market/price/${symbol}`); return response.data.data.price || response.data.price; } catch (error) { console.error('Failed to fetch price:', error); throw new Error('Failed to fetch price'); } } /** * Get 24h ticker data */ export async function getTicker(symbol: string): Promise { try { const response = await api.get(`/trading/market/ticker/${symbol}`); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch ticker:', error); throw new Error('Failed to fetch ticker data'); } } /** * Get all tickers */ export async function getAllTickers(): Promise { try { const response = await api.get('/trading/market/tickers'); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch tickers:', error); throw new Error('Failed to fetch tickers'); } } /** * Get order book */ export async function getOrderBook(symbol: string, limit: number = 20): Promise { try { const response = await api.get(`/trading/market/orderbook/${symbol}`, { params: { limit }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch order book:', error); throw new Error('Failed to fetch order book'); } } /** * Search for symbols */ export async function searchSymbols(query: string): Promise { try { const response = await api.get('/trading/market/search', { params: { query }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to search symbols:', error); throw new Error('Failed to search symbols'); } } /** * Get popular symbols */ export async function getPopularSymbols(): Promise { try { const response = await api.get('/trading/market/popular'); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch popular symbols:', error); throw new Error('Failed to fetch popular symbols'); } } // ============================================================================ // Technical Indicators API // ============================================================================ /** * Get Simple Moving Average (SMA) */ export async function getSMA( symbol: string, interval: Interval = '1h', period: number = 20 ): Promise { try { const response = await api.get(`/trading/indicators/${symbol}/sma`, { params: { interval, period }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch SMA:', error); throw new Error('Failed to fetch SMA'); } } /** * Get Exponential Moving Average (EMA) */ export async function getEMA( symbol: string, interval: Interval = '1h', period: number = 20 ): Promise { try { const response = await api.get(`/trading/indicators/${symbol}/ema`, { params: { interval, period }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch EMA:', error); throw new Error('Failed to fetch EMA'); } } /** * Get Relative Strength Index (RSI) */ export async function getRSI( symbol: string, interval: Interval = '1h', period: number = 14 ): Promise { try { const response = await api.get(`/trading/indicators/${symbol}/rsi`, { params: { interval, period }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch RSI:', error); throw new Error('Failed to fetch RSI'); } } /** * Get MACD */ export async function getMACD( symbol: string, interval: Interval = '1h', fastPeriod: number = 12, slowPeriod: number = 26, signalPeriod: number = 9 ): Promise { try { const response = await api.get(`/trading/indicators/${symbol}/macd`, { params: { interval, fastPeriod, slowPeriod, signalPeriod }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch MACD:', error); throw new Error('Failed to fetch MACD'); } } /** * Get Bollinger Bands */ export async function getBollingerBands( symbol: string, interval: Interval = '1h', period: number = 20, stdDev: number = 2 ): Promise { try { const response = await api.get(`/trading/indicators/${symbol}/bollinger`, { params: { interval, period, stdDev }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch Bollinger Bands:', error); throw new Error('Failed to fetch Bollinger Bands'); } } // ============================================================================ // Watchlist API // ============================================================================ /** * Get all user watchlists */ export async function getWatchlists(): Promise { try { const response = await api.get('/trading/watchlists'); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch watchlists:', error); throw new Error('Failed to fetch watchlists'); } } /** * Get default watchlist */ export async function getDefaultWatchlist(): Promise { try { const response = await api.get('/trading/watchlists/default'); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch default watchlist:', error); throw new Error('Failed to fetch default watchlist'); } } /** * Get watchlist by ID */ export async function getWatchlist(id: string): Promise { try { const response = await api.get(`/trading/watchlists/${id}`); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch watchlist:', error); throw new Error('Failed to fetch watchlist'); } } /** * Create new watchlist */ export async function createWatchlist(name: string): Promise { try { const response = await api.post('/trading/watchlists', { name }); return response.data.data || response.data; } catch (error) { console.error('Failed to create watchlist:', error); throw new Error('Failed to create watchlist'); } } /** * Update watchlist */ export async function updateWatchlist(id: string, name: string): Promise { try { const response = await api.patch(`/trading/watchlists/${id}`, { name }); return response.data.data || response.data; } catch (error) { console.error('Failed to update watchlist:', error); throw new Error('Failed to update watchlist'); } } /** * Delete watchlist */ export async function deleteWatchlist(id: string): Promise { try { await api.delete(`/trading/watchlists/${id}`); } catch (error) { console.error('Failed to delete watchlist:', error); throw new Error('Failed to delete watchlist'); } } /** * Add symbol to watchlist */ export async function addSymbolToWatchlist(watchlistId: string, symbol: string): Promise { try { await api.post(`/trading/watchlists/${watchlistId}/symbols`, { symbol }); } catch (error) { console.error('Failed to add symbol to watchlist:', error); throw new Error('Failed to add symbol to watchlist'); } } /** * Remove symbol from watchlist */ export async function removeSymbolFromWatchlist(watchlistId: string, symbol: string): Promise { try { await api.delete(`/trading/watchlists/${watchlistId}/symbols/${symbol}`); } catch (error) { console.error('Failed to remove symbol from watchlist:', error); throw new Error('Failed to remove symbol from watchlist'); } } /** * Get watchlist data with prices */ export async function getWatchlistData(symbols: string[]): Promise { try { const symbolsParam = symbols.join(','); const response = await api.get('/trading/market/watchlist', { params: { symbols: symbolsParam }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch watchlist data:', error); throw new Error('Failed to fetch watchlist data'); } } // ============================================================================ // Paper Trading API // ============================================================================ /** * Initialize paper trading account */ export async function initializePaperAccount( initialBalance: number = 100000, name: string = 'Default Paper Account' ): Promise { try { const response = await api.post('/trading/paper/initialize', { initialBalance, name, }); return response.data.data || response.data; } catch (error) { console.error('Failed to initialize paper account:', error); throw new Error('Failed to initialize paper account'); } } /** * Get paper trading balances */ export async function getPaperBalances(): Promise { try { const response = await api.get('/trading/paper/balances'); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch paper balances:', error); throw new Error('Failed to fetch paper balances'); } } /** * Create paper trading order */ export async function createPaperOrder(orderData: CreateOrderInput): Promise { try { const response = await api.post('/trading/paper/orders', orderData); return response.data.data || response.data; } catch (error) { console.error('Failed to create paper order:', error); throw new Error('Failed to create paper order'); } } /** * Cancel paper trading order */ export async function cancelPaperOrder(orderId: string): Promise { try { await api.delete(`/trading/paper/orders/${orderId}`); } catch (error) { console.error('Failed to cancel paper order:', error); throw new Error('Failed to cancel paper order'); } } /** * Get paper trading orders */ export async function getPaperOrders(status?: string): Promise { try { const response = await api.get('/trading/paper/orders', { params: status ? { status } : undefined, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch paper orders:', error); throw new Error('Failed to fetch paper orders'); } } /** * Get paper trading positions */ export async function getPaperPositions(status: string = 'open'): Promise { try { const response = await api.get('/trading/paper/positions', { params: { status }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch paper positions:', error); throw new Error('Failed to fetch paper positions'); } } /** * Close paper trading position */ export async function closePaperPosition(positionId: string): Promise { try { const response = await api.post(`/trading/paper/positions/${positionId}/close`); return response.data.data || response.data; } catch (error) { console.error('Failed to close paper position:', error); throw new Error('Failed to close paper position'); } } /** * Get paper trading trades history */ export async function getPaperTrades(limit: number = 50): Promise { try { const response = await api.get('/trading/paper/trades', { params: { limit }, }); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch paper trades:', error); throw new Error('Failed to fetch paper trades'); } } /** * Get paper trading portfolio summary */ export async function getPaperPortfolio(): Promise { try { const response = await api.get('/trading/paper/portfolio'); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch paper portfolio:', error); throw new Error('Failed to fetch paper portfolio'); } } /** * Reset paper trading account */ export async function resetPaperAccount(): Promise { try { const response = await api.post('/trading/paper/reset'); return response.data.data || response.data; } catch (error) { console.error('Failed to reset paper account:', error); throw new Error('Failed to reset paper account'); } } /** * Get paper trading statistics */ export async function getPaperStats(): Promise { try { const response = await api.get('/trading/paper/stats'); return response.data.data || response.data; } catch (error) { console.error('Failed to fetch paper stats:', error); throw new Error('Failed to fetch paper stats'); } } // ============================================================================ // ML-Powered Trade Execution // ============================================================================ export interface MLTradeRequest { symbol: string; direction: 'buy' | 'sell'; source: 'ict' | 'ensemble' | 'manual'; entry_price?: number; stop_loss?: number; take_profit?: number; risk_percent?: number; lot_size?: number; analysis_data?: Record; } export interface MLTradeResult { success: boolean; trade_id?: string; order_id?: string; executed_price?: number; lot_size?: number; message: string; error?: string; } export interface MT4Account { account_id: string; broker: string; balance: number; equity: number; margin: number; free_margin: number; leverage: number; currency: string; connected: boolean; } export interface MT4Position { ticket: number; symbol: string; type: 'buy' | 'sell'; volume: number; open_price: number; current_price: number; stop_loss: number; take_profit: number; profit: number; open_time: string; comment?: string; } const LLM_AGENT_URL = import.meta.env.VITE_LLM_AGENT_URL || 'http://localhost:8003'; /** * Execute a trade based on ML signal via LLM Agent */ export async function executeMLTrade(request: MLTradeRequest): Promise { try { const response = await fetch(`${LLM_AGENT_URL}/api/trade/execute`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}`, }, body: JSON.stringify(request), }); if (!response.ok) { const error = await response.json(); return { success: false, message: 'Trade execution failed', error: error.detail || error.message || 'Unknown error', }; } return await response.json(); } catch (error) { console.error('Failed to execute ML trade:', error); return { success: false, message: 'Trade execution failed', error: error instanceof Error ? error.message : 'Network error', }; } } /** * Get MT4 account information */ export async function getMT4Account(): Promise { try { const response = await fetch(`${LLM_AGENT_URL}/api/mt4/account`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}`, }, }); if (!response.ok) return null; return await response.json(); } catch (error) { console.error('Failed to get MT4 account:', error); return null; } } /** * Get MT4 open positions */ export async function getMT4Positions(): Promise { try { const response = await fetch(`${LLM_AGENT_URL}/api/mt4/positions`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}`, }, }); if (!response.ok) return []; const data = await response.json(); return data.positions || []; } catch (error) { console.error('Failed to get MT4 positions:', error); return []; } } /** * Close MT4 position */ export async function closeMT4Position(ticket: number): Promise { try { const response = await fetch(`${LLM_AGENT_URL}/api/mt4/positions/${ticket}/close`, { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}`, }, }); if (!response.ok) { const error = await response.json(); return { success: false, message: 'Failed to close position', error: error.detail || error.message, }; } return await response.json(); } catch (error) { console.error('Failed to close MT4 position:', error); return { success: false, message: 'Failed to close position', error: error instanceof Error ? error.message : 'Network error', }; } } /** * Modify MT4 position (SL/TP) */ export async function modifyMT4Position( ticket: number, stopLoss?: number, takeProfit?: number ): Promise { try { const response = await fetch(`${LLM_AGENT_URL}/api/mt4/positions/${ticket}/modify`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}`, }, body: JSON.stringify({ stop_loss: stopLoss, take_profit: takeProfit }), }); if (!response.ok) { const error = await response.json(); return { success: false, message: 'Failed to modify position', error: error.detail || error.message, }; } return await response.json(); } catch (error) { console.error('Failed to modify MT4 position:', error); return { success: false, message: 'Failed to modify position', error: error instanceof Error ? error.message : 'Network error', }; } } /** * Calculate position size based on risk */ export async function calculatePositionSize( symbol: string, stopLossPips: number, riskPercent: number = 1 ): Promise<{ lot_size: number; risk_amount: number } | null> { try { const response = await fetch(`${LLM_AGENT_URL}/api/mt4/calculate-size`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}`, }, body: JSON.stringify({ symbol, stop_loss_pips: stopLossPips, risk_percent: riskPercent, }), }); if (!response.ok) return null; return await response.json(); } catch (error) { console.error('Failed to calculate position size:', error); return null; } } /** * Get LLM Agent health status */ export async function getLLMAgentHealth(): Promise { try { const response = await fetch(`${LLM_AGENT_URL}/health`); return response.ok; } catch { return false; } } // ============================================================================ // Export // ============================================================================ export const tradingService = { // Market data getKlines, getPrice, getTicker, getAllTickers, getOrderBook, searchSymbols, getPopularSymbols, // Indicators getSMA, getEMA, getRSI, getMACD, getBollingerBands, // Watchlist getWatchlists, getDefaultWatchlist, getWatchlist, createWatchlist, updateWatchlist, deleteWatchlist, addSymbolToWatchlist, removeSymbolFromWatchlist, getWatchlistData, // Paper Trading initializePaperAccount, getPaperBalances, createPaperOrder, cancelPaperOrder, getPaperOrders, getPaperPositions, closePaperPosition, getPaperTrades, getPaperPortfolio, resetPaperAccount, getPaperStats, // ML-Powered Trading & MT4 executeMLTrade, getMT4Account, getMT4Positions, closeMT4Position, modifyMT4Position, calculatePositionSize, getLLMAgentHealth, }; export default tradingService;