mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-19 10:24:38 +08:00
* feat: add AI grid trading and market regime classification - Add GridTrader interface with PlaceLimitOrder, CancelOrder, GetOrderBook - Implement GridTrader for all exchanges (Binance, Bybit, OKX, Bitget, Hyperliquid, Aster, Lighter) - Add grid engine with ATR-based boundary calculation and fund distribution - Add market regime classification documents (Chinese/English) - Add GridConfigEditor component for frontend configuration * fix: implement GetOpenOrders for Lighter exchange * debug: add logging for Lighter GetActiveOrders API call * fix: correct Lighter API response parsing for GetOpenOrders - Changed response field from 'data' to 'orders' to match Lighter API - Updated OrderResponse struct to match Lighter's actual field names - Fixed field types: price/quantity as strings, is_ask for side * feat: implement GetOpenOrders for Aster, OKX, Bitget exchanges - Aster: uses /fapi/v3/openOrders endpoint - OKX: uses /api/v5/trade/orders-pending and orders-algo-pending - Bitget: uses /api/v2/mix/order/orders-pending and orders-plan-pending * fix: address code review issues for GetOpenOrders - Add error logging for OKX/Bitget API failures (was silently swallowed) - Fix Lighter position side logic to handle reduce-only orders - Change verbose debug logs from Infof to Debugf level * fix: provide FromAccountIndex and ApiKeyIndex for Lighter nonce auto-fetch Root cause: SDK requires these fields to fetch nonce from API, otherwise nonce gets cached/stuck * fix: use auth query parameter instead of Authorization header for Lighter API * test: add Lighter API authentication tests and diagnostic tools * fix(grid): add leverage setting before order placement CRITICAL BUG FIX: - Call SetLeverage() in GridTraderAdapter.PlaceLimitOrder() - Set leverage during grid initialization - Log leverage setting results * fix(grid): prevent CancelOrder from canceling all orders CRITICAL BUG FIX: - CancelOrder no longer calls CancelAllOrders - Try exchange-specific CancelOrder if available - Return error if individual cancellation not supported * fix(grid): add total position value limit check CRITICAL: Prevent excessive position accumulation - New checkTotalPositionLimit() function - Checks current + pending + new order value - Rejects orders that would exceed TotalInvestment x Leverage - Logs clear error messages when limit exceeded * feat(grid): implement stop loss execution CRITICAL: Add code-level stop loss protection - New checkAndExecuteStopLoss() function - Checks each filled level against StopLossPct - Automatically closes positions exceeding stop loss - Called during every grid state sync * feat(grid): add breakout detection and auto-pause CRITICAL: Detect price breakout from grid range - New checkBreakout() function to detect upper/lower breakouts - Auto-pause grid on significant breakout (>2%) - Cancel all orders when breakout detected - Prevent continued losses in trending market - Minor breakouts (1-2%) logged for AI consideration * feat(grid): enforce max drawdown limit with emergency exit CRITICAL: Add drawdown protection - New checkMaxDrawdown() function tracks peak equity - emergencyExit() closes all positions and cancels orders - Auto-pause grid when MaxDrawdownPct exceeded - Protect capital from excessive losses * feat(grid): enforce daily loss limit - Add checkDailyLossLimit() function to check if daily loss exceeds limit - Track daily PnL with auto-reset at midnight - Pause grid when DailyLossLimitPct exceeded - Add updateDailyPnL() helper for realized PnL tracking - Prevent excessive single-day losses * fix(grid): update daily PnL when stop loss is executed The updateDailyPnL() function was added but never called, leaving DailyPnL always at 0 and preventing daily loss limit checks from triggering. This fix updates DailyPnL and TotalProfit directly in checkAndExecuteStopLoss() when a stop loss is executed. We update directly rather than calling updateDailyPnL() because the mutex is already held in that function. * feat(grid): add automatic grid adjustment - New checkGridSkew() detects imbalanced grid - autoAdjustGrid() reinitializes around current price - Prevents grid from becoming ineffective after drift - Triggers when one side is 3x more filled than other * fix(grid): recalculate bounds in autoAdjustGrid before reinitializing levels Critical fix for grid auto-adjustment: - Recalculate grid bounds (UpperPrice, LowerPrice, GridSpacing) centered on current price before reinitializing grid levels - Preserve filled positions during adjustment by saving and restoring them to the closest new level after reinitialization - Hold mutex lock for the entire adjustment operation to ensure atomicity - Add locked variants of calculateDefaultBounds, calculateATRBounds, and initializeGridLevels to use during adjustment Without this fix, autoAdjustGrid was using old boundaries when creating new grid levels, defeating the purpose of auto-adjustment when price moved significantly. * fix(grid): improve order state sync logic - Don't assume missing orders are filled - Compare position size to determine fill vs cancel - Properly reset cancelled orders to empty state - More accurate grid state tracking * fix(grid): use actual PositionSize sum instead of count in syncGridState heuristic The position-based heuristic was using `float64(previousFilledCount) * level.OrderQuantity` which incorrectly assumed uniform order quantities. Since the grid uses weighted distribution (gaussian, pyramid, uniform) where orders have different quantities, this could lead to incorrect fill detection. Now sums the actual PositionSize from filled levels for accurate comparison. Also adds warning log when GetPositions() fails. * docs: add grid market regime detection design Design for enhanced market state recognition with: - Multi-dimensional indicators (ATR, Bollinger, EMA, MACD, RSI) - Multi-period box indicators (72/240/500 1h candles) - 4-level ranging classification - Breakout detection and handling - Frontend risk control panel * docs: add grid market regime implementation plan 20 tasks covering: - Donchian channel calculation - Box data types and API - Regime classification (4 levels) - Breakout detection and handling - False breakout recovery - Frontend risk panel - AI prompt updates * feat(market): add Donchian channel calculation Add calculateDonchian function to compute highest high and lowest low over a specified period. This is the foundation for box (range) detection in the multi-period box indicator system for grid trading. * fix(market): handle invalid period in calculateDonchian * feat(market): add BoxData and RegimeLevel types * feat(market): add GetBoxData for multi-period box calculation Adds calculateBoxData internal function and GetBoxData public API that fetches 1h klines and computes three Donchian box levels (short/mid/long). This will be used by the grid trading system to detect market regime. * feat(store): add box and regime fields to grid models * feat(trader): add regime classification and breakout detection Implements Tasks 6-9 for grid market regime awareness: - Task 6: classifyRegimeLevel with Bollinger/ATR thresholds - Task 7: detectBoxBreakout for multi-period box breakouts - Task 8: confirmBreakout with 3-candle confirmation logic - Task 9: getBreakoutAction mapping breakout levels to actions * feat(trader): integrate box breakout detection into grid cycle - Task 10: Add checkBoxBreakout with 3-candle confirmation - Task 11: Add checkFalseBreakoutRecovery for 50% position recovery - Task 12: Add box/breakout/regime fields to GridState * feat: add grid risk panel with API endpoint - Task 13: Add GridRiskInfo type to frontend - Task 14: Add /traders/:id/grid-risk API endpoint - Task 15: Add GetGridRiskInfo method to AutoTrader - Task 16: Create GridRiskPanel component with i18n * feat(kernel): add box indicators to AI prompt - Add BoxData field to GridContext - Add box indicator table to both zh/en prompts - Show breakout/warning alerts based on price position * feat(web): integrate GridRiskPanel into TraderDashboardPage * feat(lighter): improve API key validation and market caching - Add API key validation status tracking - Add market list caching to reduce API calls - Improve logging (debug vs info levels) - Add comprehensive integration tests - Update trader manager and store for lighter support * fix: remove hardcoded test wallet address * fix(grid): improve GridRiskPanel layout and fix liquidation data - Make panel collapsible with summary badges when collapsed - Use compact 2-column grid layout for detailed info - Fix auth token key (token -> auth_token) - Only calculate liquidation distance when position exists * fix(grid): add isRunning checks to prevent trades after Stop() is called
822 lines
20 KiB
TypeScript
822 lines
20 KiB
TypeScript
export interface SystemStatus {
|
||
trader_id: string
|
||
trader_name: string
|
||
ai_model: string
|
||
is_running: boolean
|
||
start_time: string
|
||
runtime_minutes: number
|
||
call_count: number
|
||
initial_balance: number
|
||
scan_interval: string
|
||
stop_until: string
|
||
last_reset_time: string
|
||
ai_provider: string
|
||
strategy_type?: 'ai_trading' | 'grid_trading'
|
||
grid_symbol?: string
|
||
}
|
||
|
||
export interface AccountInfo {
|
||
total_equity: number
|
||
wallet_balance: number
|
||
unrealized_profit: number // 未实现盈亏(交易所API官方值)
|
||
available_balance: number
|
||
total_pnl: number
|
||
total_pnl_pct: number
|
||
initial_balance: number
|
||
daily_pnl: number
|
||
position_count: number
|
||
margin_used: number
|
||
margin_used_pct: number
|
||
}
|
||
|
||
export interface Position {
|
||
symbol: string
|
||
side: string
|
||
entry_price: number
|
||
mark_price: number
|
||
quantity: number
|
||
leverage: number
|
||
unrealized_pnl: number
|
||
unrealized_pnl_pct: number
|
||
liquidation_price: number
|
||
margin_used: number
|
||
}
|
||
|
||
export interface DecisionAction {
|
||
action: string
|
||
symbol: string
|
||
quantity: number
|
||
leverage: number
|
||
price: number
|
||
stop_loss?: number // Stop loss price
|
||
take_profit?: number // Take profit price
|
||
confidence?: number // AI confidence (0-100)
|
||
reasoning?: string // Brief reasoning
|
||
order_id: number
|
||
timestamp: string
|
||
success: boolean
|
||
error?: string
|
||
}
|
||
|
||
export interface AccountSnapshot {
|
||
total_balance: number
|
||
available_balance: number
|
||
total_unrealized_profit: number
|
||
position_count: number
|
||
margin_used_pct: number
|
||
}
|
||
|
||
export interface DecisionRecord {
|
||
timestamp: string
|
||
cycle_number: number
|
||
system_prompt: string
|
||
input_prompt: string
|
||
cot_trace: string
|
||
decision_json: string
|
||
account_state: AccountSnapshot
|
||
positions: any[]
|
||
candidate_coins: string[]
|
||
decisions: DecisionAction[]
|
||
execution_log: string[]
|
||
success: boolean
|
||
error_message?: string
|
||
}
|
||
|
||
export interface Statistics {
|
||
total_cycles: number
|
||
successful_cycles: number
|
||
failed_cycles: number
|
||
total_open_positions: number
|
||
total_close_positions: number
|
||
}
|
||
|
||
// AI Trading相关类型
|
||
export interface TraderInfo {
|
||
trader_id: string
|
||
trader_name: string
|
||
ai_model: string
|
||
exchange_id?: string
|
||
is_running?: boolean
|
||
show_in_competition?: boolean
|
||
strategy_id?: string
|
||
strategy_name?: string
|
||
custom_prompt?: string
|
||
use_ai500?: boolean
|
||
use_oi_top?: boolean
|
||
system_prompt_template?: string
|
||
}
|
||
|
||
export interface AIModel {
|
||
id: string
|
||
name: string
|
||
provider: string
|
||
enabled: boolean
|
||
apiKey?: string
|
||
customApiUrl?: string
|
||
customModelName?: string
|
||
}
|
||
|
||
export interface Exchange {
|
||
id: string // UUID (empty for supported exchange templates)
|
||
exchange_type: string // "binance", "bybit", "okx", "hyperliquid", "aster", "lighter"
|
||
account_name: string // User-defined account name
|
||
name: string // Display name
|
||
type: 'cex' | 'dex'
|
||
enabled: boolean
|
||
apiKey?: string
|
||
secretKey?: string
|
||
passphrase?: string // OKX specific
|
||
testnet?: boolean
|
||
// Hyperliquid specific
|
||
hyperliquidWalletAddr?: string
|
||
// Aster specific
|
||
asterUser?: string
|
||
asterSigner?: string
|
||
asterPrivateKey?: string
|
||
// LIGHTER specific
|
||
lighterWalletAddr?: string
|
||
lighterPrivateKey?: string
|
||
lighterApiKeyPrivateKey?: string
|
||
lighterApiKeyIndex?: number
|
||
}
|
||
|
||
export interface CreateExchangeRequest {
|
||
exchange_type: string // "binance", "bybit", "okx", "hyperliquid", "aster", "lighter"
|
||
account_name: string // User-defined account name
|
||
enabled: boolean
|
||
api_key?: string
|
||
secret_key?: string
|
||
passphrase?: string
|
||
testnet?: boolean
|
||
hyperliquid_wallet_addr?: string
|
||
aster_user?: string
|
||
aster_signer?: string
|
||
aster_private_key?: string
|
||
lighter_wallet_addr?: string
|
||
lighter_private_key?: string
|
||
lighter_api_key_private_key?: string
|
||
lighter_api_key_index?: number
|
||
}
|
||
|
||
export interface CreateTraderRequest {
|
||
name: string
|
||
ai_model_id: string
|
||
exchange_id: string
|
||
strategy_id?: string // 策略ID(新版,使用保存的策略配置)
|
||
initial_balance?: number // 可选:创建时由后端自动获取,编辑时可手动更新
|
||
scan_interval_minutes?: number
|
||
is_cross_margin?: boolean
|
||
show_in_competition?: boolean // 是否在竞技场显示
|
||
// 以下字段为向后兼容保留,新版使用策略配置
|
||
btc_eth_leverage?: number
|
||
altcoin_leverage?: number
|
||
trading_symbols?: string
|
||
custom_prompt?: string
|
||
override_base_prompt?: boolean
|
||
system_prompt_template?: string
|
||
use_ai500?: boolean
|
||
use_oi_top?: boolean
|
||
}
|
||
|
||
export interface UpdateModelConfigRequest {
|
||
models: {
|
||
[key: string]: {
|
||
enabled: boolean
|
||
api_key: string
|
||
custom_api_url?: string
|
||
custom_model_name?: string
|
||
}
|
||
}
|
||
}
|
||
|
||
export interface UpdateExchangeConfigRequest {
|
||
exchanges: {
|
||
[key: string]: {
|
||
enabled: boolean
|
||
api_key: string
|
||
secret_key: string
|
||
passphrase?: string
|
||
testnet?: boolean
|
||
// Hyperliquid 特定字段
|
||
hyperliquid_wallet_addr?: string
|
||
// Aster 特定字段
|
||
aster_user?: string
|
||
aster_signer?: string
|
||
aster_private_key?: string
|
||
// LIGHTER 特定字段
|
||
lighter_wallet_addr?: string
|
||
lighter_private_key?: string
|
||
lighter_api_key_private_key?: string
|
||
lighter_api_key_index?: number
|
||
}
|
||
}
|
||
}
|
||
|
||
// Competition related types
|
||
export interface CompetitionTraderData {
|
||
trader_id: string
|
||
trader_name: string
|
||
ai_model: string
|
||
exchange: string
|
||
total_equity: number
|
||
total_pnl: number
|
||
total_pnl_pct: number
|
||
position_count: number
|
||
margin_used_pct: number
|
||
is_running: boolean
|
||
}
|
||
|
||
export interface CompetitionData {
|
||
traders: CompetitionTraderData[]
|
||
count: number
|
||
}
|
||
|
||
// Trader Configuration Data for View Modal
|
||
export interface TraderConfigData {
|
||
trader_id?: string
|
||
trader_name: string
|
||
ai_model: string
|
||
exchange_id: string
|
||
strategy_id?: string // 策略ID
|
||
strategy_name?: string // 策略名称
|
||
is_cross_margin: boolean
|
||
show_in_competition: boolean // 是否在竞技场显示
|
||
scan_interval_minutes: number
|
||
initial_balance: number
|
||
is_running: boolean
|
||
// 以下为旧版字段(向后兼容)
|
||
btc_eth_leverage?: number
|
||
altcoin_leverage?: number
|
||
trading_symbols?: string
|
||
custom_prompt?: string
|
||
override_base_prompt?: boolean
|
||
system_prompt_template?: string
|
||
use_ai500?: boolean
|
||
use_oi_top?: boolean
|
||
}
|
||
|
||
// Backtest types
|
||
export interface BacktestRunSummary {
|
||
symbol_count: number;
|
||
decision_tf: string;
|
||
processed_bars: number;
|
||
progress_pct: number;
|
||
equity_last: number;
|
||
max_drawdown_pct: number;
|
||
liquidated: boolean;
|
||
liquidation_note?: string;
|
||
}
|
||
|
||
export interface BacktestRunMetadata {
|
||
run_id: string;
|
||
label?: string;
|
||
user_id?: string;
|
||
last_error?: string;
|
||
version: number;
|
||
state: string;
|
||
created_at: string;
|
||
updated_at: string;
|
||
summary: BacktestRunSummary;
|
||
}
|
||
|
||
export interface BacktestRunsResponse {
|
||
total: number;
|
||
items: BacktestRunMetadata[];
|
||
}
|
||
|
||
// Position status for real-time display during backtest
|
||
export interface BacktestPositionStatus {
|
||
symbol: string;
|
||
side: string;
|
||
quantity: number;
|
||
entry_price: number;
|
||
mark_price: number;
|
||
leverage: number;
|
||
unrealized_pnl: number;
|
||
unrealized_pnl_pct: number;
|
||
margin_used: number;
|
||
}
|
||
|
||
export interface BacktestStatusPayload {
|
||
run_id: string;
|
||
state: string;
|
||
progress_pct: number;
|
||
processed_bars: number;
|
||
current_time: number;
|
||
decision_cycle: number;
|
||
equity: number;
|
||
unrealized_pnl: number;
|
||
realized_pnl: number;
|
||
positions?: BacktestPositionStatus[];
|
||
note?: string;
|
||
last_error?: string;
|
||
last_updated_iso: string;
|
||
}
|
||
|
||
export interface BacktestEquityPoint {
|
||
ts: number;
|
||
equity: number;
|
||
available: number;
|
||
pnl: number;
|
||
pnl_pct: number;
|
||
dd_pct: number;
|
||
cycle: number;
|
||
}
|
||
|
||
export interface BacktestTradeEvent {
|
||
ts: number;
|
||
symbol: string;
|
||
action: string;
|
||
side?: string;
|
||
qty: number;
|
||
price: number;
|
||
fee: number;
|
||
slippage: number;
|
||
order_value: number;
|
||
realized_pnl: number;
|
||
leverage?: number;
|
||
cycle: number;
|
||
position_after: number;
|
||
liquidation: boolean;
|
||
note?: string;
|
||
}
|
||
|
||
export interface BacktestMetrics {
|
||
total_return_pct: number;
|
||
max_drawdown_pct: number;
|
||
sharpe_ratio: number;
|
||
profit_factor: number;
|
||
win_rate: number;
|
||
trades: number;
|
||
avg_win: number;
|
||
avg_loss: number;
|
||
best_symbol: string;
|
||
worst_symbol: string;
|
||
liquidated: boolean;
|
||
symbol_stats?: Record<
|
||
string,
|
||
{
|
||
total_trades: number;
|
||
winning_trades: number;
|
||
losing_trades: number;
|
||
total_pnl: number;
|
||
avg_pnl: number;
|
||
win_rate: number;
|
||
}
|
||
>;
|
||
}
|
||
|
||
export interface BacktestStartConfig {
|
||
run_id?: string;
|
||
ai_model_id?: string;
|
||
strategy_id?: string; // Optional: use saved strategy from Strategy Studio
|
||
symbols: string[];
|
||
timeframes: string[];
|
||
decision_timeframe: string;
|
||
decision_cadence_nbars: number;
|
||
start_ts: number;
|
||
end_ts: number;
|
||
initial_balance: number;
|
||
fee_bps: number;
|
||
slippage_bps: number;
|
||
fill_policy: string;
|
||
prompt_variant?: string;
|
||
prompt_template?: string;
|
||
custom_prompt?: string;
|
||
override_prompt?: boolean;
|
||
cache_ai?: boolean;
|
||
replay_only?: boolean;
|
||
checkpoint_interval_bars?: number;
|
||
checkpoint_interval_seconds?: number;
|
||
replay_decision_dir?: string;
|
||
shared_ai_cache_path?: string;
|
||
ai?: {
|
||
provider?: string;
|
||
model?: string;
|
||
key?: string;
|
||
secret_key?: string;
|
||
base_url?: string;
|
||
};
|
||
leverage?: {
|
||
btc_eth_leverage?: number;
|
||
altcoin_leverage?: number;
|
||
};
|
||
}
|
||
|
||
// Kline data for backtest chart
|
||
export interface BacktestKline {
|
||
time: number;
|
||
open: number;
|
||
high: number;
|
||
low: number;
|
||
close: number;
|
||
volume: number;
|
||
}
|
||
|
||
export interface BacktestKlinesResponse {
|
||
symbol: string;
|
||
timeframe: string;
|
||
start_ts: number;
|
||
end_ts: number;
|
||
count: number;
|
||
klines: BacktestKline[];
|
||
run_id: string;
|
||
}
|
||
|
||
// Strategy Studio Types
|
||
export interface Strategy {
|
||
id: string;
|
||
name: string;
|
||
description: string;
|
||
is_active: boolean;
|
||
is_default: boolean;
|
||
is_public: boolean; // 是否在策略市场公开
|
||
config_visible: boolean; // 配置参数是否公开可见
|
||
config: StrategyConfig;
|
||
created_at: string;
|
||
updated_at: string;
|
||
}
|
||
|
||
// 策略使用统计
|
||
export interface StrategyStats {
|
||
clone_count: number; // 被克隆次数
|
||
active_users: number; // 当前使用人数
|
||
top_performers?: StrategyPerformer[]; // 收益排行
|
||
}
|
||
|
||
// 策略使用者收益排行
|
||
export interface StrategyPerformer {
|
||
user_id: string;
|
||
user_name: string; // 脱敏后的用户名
|
||
total_pnl_pct: number; // 总收益率
|
||
total_pnl: number; // 总收益金额
|
||
win_rate: number; // 胜率
|
||
trade_count: number; // 交易次数
|
||
using_since: string; // 使用开始时间
|
||
rank: number; // 排名
|
||
}
|
||
|
||
export interface PromptSectionsConfig {
|
||
role_definition?: string;
|
||
trading_frequency?: string;
|
||
entry_standards?: string;
|
||
decision_process?: string;
|
||
}
|
||
|
||
export interface StrategyConfig {
|
||
// Strategy type: "ai_trading" (default) or "grid_trading"
|
||
strategy_type?: 'ai_trading' | 'grid_trading';
|
||
// Language setting: "zh" for Chinese, "en" for English
|
||
// Determines the language used for data formatting and prompt generation
|
||
language?: 'zh' | 'en';
|
||
coin_source: CoinSourceConfig;
|
||
indicators: IndicatorConfig;
|
||
custom_prompt?: string;
|
||
risk_control: RiskControlConfig;
|
||
prompt_sections?: PromptSectionsConfig;
|
||
// Grid trading configuration (only used when strategy_type is 'grid_trading')
|
||
grid_config?: GridStrategyConfig;
|
||
}
|
||
|
||
// Grid trading specific configuration
|
||
export interface GridStrategyConfig {
|
||
// Trading pair (e.g., "BTCUSDT")
|
||
symbol: string;
|
||
// Number of grid levels (5-50)
|
||
grid_count: number;
|
||
// Total investment in USDT
|
||
total_investment: number;
|
||
// Leverage (1-20)
|
||
leverage: number;
|
||
// Upper price boundary (0 = auto-calculate from ATR)
|
||
upper_price: number;
|
||
// Lower price boundary (0 = auto-calculate from ATR)
|
||
lower_price: number;
|
||
// Use ATR to auto-calculate bounds
|
||
use_atr_bounds: boolean;
|
||
// ATR multiplier for bound calculation (default 2.0)
|
||
atr_multiplier: number;
|
||
// Position distribution: "uniform" | "gaussian" | "pyramid"
|
||
distribution: 'uniform' | 'gaussian' | 'pyramid';
|
||
// Maximum drawdown percentage before emergency exit
|
||
max_drawdown_pct: number;
|
||
// Stop loss percentage per position
|
||
stop_loss_pct: number;
|
||
// Daily loss limit percentage
|
||
daily_loss_limit_pct: number;
|
||
// Use maker-only orders for lower fees
|
||
use_maker_only: boolean;
|
||
}
|
||
|
||
export interface CoinSourceConfig {
|
||
source_type: 'static' | 'ai500' | 'oi_top' | 'mixed';
|
||
static_coins?: string[];
|
||
excluded_coins?: string[]; // 排除的币种列表
|
||
use_ai500: boolean;
|
||
ai500_limit?: number;
|
||
use_oi_top: boolean;
|
||
oi_top_limit?: number;
|
||
// Note: API URLs are now built automatically using nofxos_api_key from IndicatorConfig
|
||
}
|
||
|
||
export interface IndicatorConfig {
|
||
klines: KlineConfig;
|
||
// Raw OHLCV kline data - required for AI analysis
|
||
enable_raw_klines: boolean;
|
||
// Technical indicators (optional)
|
||
enable_ema: boolean;
|
||
enable_macd: boolean;
|
||
enable_rsi: boolean;
|
||
enable_atr: boolean;
|
||
enable_boll: boolean;
|
||
enable_volume: boolean;
|
||
enable_oi: boolean;
|
||
enable_funding_rate: boolean;
|
||
ema_periods?: number[];
|
||
rsi_periods?: number[];
|
||
atr_periods?: number[];
|
||
boll_periods?: number[];
|
||
external_data_sources?: ExternalDataSource[];
|
||
|
||
// ========== NofxOS 数据源统一配置 ==========
|
||
// Unified NofxOS API Key - used for all NofxOS data sources
|
||
nofxos_api_key?: string;
|
||
|
||
// 量化数据源(资金流向、持仓变化、价格变化)
|
||
enable_quant_data?: boolean;
|
||
enable_quant_oi?: boolean;
|
||
enable_quant_netflow?: boolean;
|
||
|
||
// OI 排行数据(市场持仓量增减排行)
|
||
enable_oi_ranking?: boolean;
|
||
oi_ranking_duration?: string; // "1h", "4h", "24h"
|
||
oi_ranking_limit?: number;
|
||
|
||
// NetFlow 排行数据(机构/散户资金流向排行)
|
||
enable_netflow_ranking?: boolean;
|
||
netflow_ranking_duration?: string; // "1h", "4h", "24h"
|
||
netflow_ranking_limit?: number;
|
||
|
||
// Price 排行数据(涨跌幅排行)
|
||
enable_price_ranking?: boolean;
|
||
price_ranking_duration?: string; // "1h", "4h", "24h" or "1h,4h,24h"
|
||
price_ranking_limit?: number;
|
||
}
|
||
|
||
export interface KlineConfig {
|
||
primary_timeframe: string;
|
||
primary_count: number;
|
||
longer_timeframe?: string;
|
||
longer_count?: number;
|
||
enable_multi_timeframe: boolean;
|
||
// 新增:支持选择多个时间周期
|
||
selected_timeframes?: string[];
|
||
}
|
||
|
||
export interface ExternalDataSource {
|
||
name: string;
|
||
type: 'api' | 'webhook';
|
||
url: string;
|
||
method: string;
|
||
headers?: Record<string, string>;
|
||
data_path?: string;
|
||
refresh_secs?: number;
|
||
}
|
||
|
||
export interface RiskControlConfig {
|
||
// Max number of coins held simultaneously (CODE ENFORCED)
|
||
max_positions: number;
|
||
|
||
// Trading Leverage - exchange leverage for opening positions (AI guided)
|
||
btc_eth_max_leverage: number; // BTC/ETH max exchange leverage
|
||
altcoin_max_leverage: number; // Altcoin max exchange leverage
|
||
|
||
// Position Value Ratio - single position notional value / account equity (CODE ENFORCED)
|
||
// Max position value = equity × this ratio
|
||
btc_eth_max_position_value_ratio?: number; // default: 5 (BTC/ETH max position = 5x equity)
|
||
altcoin_max_position_value_ratio?: number; // default: 1 (Altcoin max position = 1x equity)
|
||
|
||
// Risk Parameters
|
||
max_margin_usage: number; // Max margin utilization, e.g. 0.9 = 90% (CODE ENFORCED)
|
||
min_position_size: number; // Min position size in USDT (CODE ENFORCED)
|
||
min_risk_reward_ratio: number; // Min take_profit / stop_loss ratio (AI guided)
|
||
min_confidence: number; // Min AI confidence to open position (AI guided)
|
||
}
|
||
|
||
// Debate Arena Types
|
||
export type DebateStatus = 'pending' | 'running' | 'voting' | 'completed' | 'cancelled';
|
||
export type DebatePersonality = 'bull' | 'bear' | 'analyst' | 'contrarian' | 'risk_manager';
|
||
|
||
export interface DebateDecision {
|
||
action: string;
|
||
symbol: string;
|
||
confidence: number;
|
||
leverage?: number;
|
||
position_pct?: number;
|
||
position_size_usd?: number;
|
||
stop_loss?: number;
|
||
take_profit?: number;
|
||
reasoning: string;
|
||
// Execution tracking
|
||
executed?: boolean;
|
||
executed_at?: string;
|
||
order_id?: string;
|
||
error?: string;
|
||
}
|
||
|
||
export interface DebateSession {
|
||
id: string;
|
||
user_id: string;
|
||
name: string;
|
||
strategy_id: string;
|
||
status: DebateStatus;
|
||
symbol: string;
|
||
interval_minutes: number;
|
||
prompt_variant: string;
|
||
trader_id?: string;
|
||
max_rounds: number;
|
||
current_round: number;
|
||
final_decision?: DebateDecision;
|
||
final_decisions?: DebateDecision[]; // Multi-coin decisions
|
||
auto_execute: boolean;
|
||
created_at: string;
|
||
updated_at: string;
|
||
}
|
||
|
||
export interface DebateParticipant {
|
||
id: string;
|
||
session_id: string;
|
||
ai_model_id: string;
|
||
ai_model_name: string;
|
||
provider: string;
|
||
personality: DebatePersonality;
|
||
color: string;
|
||
speak_order: number;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface DebateMessage {
|
||
id: string;
|
||
session_id: string;
|
||
round: number;
|
||
ai_model_id: string;
|
||
ai_model_name: string;
|
||
provider: string;
|
||
personality: DebatePersonality;
|
||
message_type: string;
|
||
content: string;
|
||
decision?: DebateDecision;
|
||
decisions?: DebateDecision[]; // Multi-coin decisions
|
||
confidence: number;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface DebateVote {
|
||
id: string;
|
||
session_id: string;
|
||
ai_model_id: string;
|
||
ai_model_name: string;
|
||
action: string;
|
||
symbol: string;
|
||
confidence: number;
|
||
leverage?: number;
|
||
position_pct?: number;
|
||
stop_loss_pct?: number;
|
||
take_profit_pct?: number;
|
||
reasoning: string;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface DebateSessionWithDetails extends DebateSession {
|
||
participants: DebateParticipant[];
|
||
messages: DebateMessage[];
|
||
votes: DebateVote[];
|
||
}
|
||
|
||
export interface CreateDebateRequest {
|
||
name: string;
|
||
strategy_id: string;
|
||
symbol: string;
|
||
max_rounds?: number;
|
||
interval_minutes?: number; // 5, 15, 30, 60 minutes
|
||
prompt_variant?: string; // balanced, aggressive, conservative, scalping
|
||
auto_execute?: boolean;
|
||
trader_id?: string; // Trader to use for auto-execute
|
||
// OI Ranking data options
|
||
enable_oi_ranking?: boolean; // Whether to include OI ranking data
|
||
oi_ranking_limit?: number; // Number of OI ranking entries (default 10)
|
||
oi_duration?: string; // Duration for OI data (1h, 4h, 24h, etc.)
|
||
participants: {
|
||
ai_model_id: string;
|
||
personality: DebatePersonality;
|
||
}[];
|
||
}
|
||
|
||
export interface DebatePersonalityInfo {
|
||
id: DebatePersonality;
|
||
name: string;
|
||
emoji: string;
|
||
color: string;
|
||
description: string;
|
||
}
|
||
|
||
// Position History Types
|
||
export interface HistoricalPosition {
|
||
id: number;
|
||
trader_id: string;
|
||
exchange_id: string;
|
||
exchange_type: string;
|
||
symbol: string;
|
||
side: string;
|
||
quantity: number;
|
||
entry_quantity: number;
|
||
entry_price: number;
|
||
entry_order_id: string;
|
||
entry_time: string;
|
||
exit_price: number;
|
||
exit_order_id: string;
|
||
exit_time: string;
|
||
realized_pnl: number;
|
||
fee: number;
|
||
leverage: number;
|
||
status: string;
|
||
close_reason: string;
|
||
created_at: string;
|
||
updated_at: string;
|
||
}
|
||
|
||
// Matches Go TraderStats struct exactly
|
||
export interface TraderStats {
|
||
total_trades: number;
|
||
win_trades: number;
|
||
loss_trades: number;
|
||
win_rate: number;
|
||
profit_factor: number;
|
||
sharpe_ratio: number;
|
||
total_pnl: number;
|
||
total_fee: number;
|
||
avg_win: number;
|
||
avg_loss: number;
|
||
max_drawdown_pct: number;
|
||
}
|
||
|
||
// Matches Go SymbolStats struct exactly
|
||
export interface SymbolStats {
|
||
symbol: string;
|
||
total_trades: number;
|
||
win_trades: number;
|
||
win_rate: number;
|
||
total_pnl: number;
|
||
avg_pnl: number;
|
||
avg_hold_mins: number;
|
||
}
|
||
|
||
// Matches Go DirectionStats struct exactly
|
||
export interface DirectionStats {
|
||
side: string;
|
||
trade_count: number;
|
||
win_rate: number;
|
||
total_pnl: number;
|
||
avg_pnl: number;
|
||
}
|
||
|
||
export interface PositionHistoryResponse {
|
||
positions: HistoricalPosition[];
|
||
stats: TraderStats | null;
|
||
symbol_stats: SymbolStats[];
|
||
direction_stats: DirectionStats[];
|
||
}
|
||
|
||
// Grid Risk Information for frontend display
|
||
export interface GridRiskInfo {
|
||
// Leverage info
|
||
current_leverage: number
|
||
effective_leverage: number
|
||
recommended_leverage: number
|
||
|
||
// Position info
|
||
current_position: number
|
||
max_position: number
|
||
position_percent: number
|
||
|
||
// Liquidation info
|
||
liquidation_price: number
|
||
liquidation_distance: number
|
||
|
||
// Market state
|
||
regime_level: string
|
||
|
||
// Box state
|
||
short_box_upper: number
|
||
short_box_lower: number
|
||
mid_box_upper: number
|
||
mid_box_lower: number
|
||
long_box_upper: number
|
||
long_box_lower: number
|
||
current_price: number
|
||
|
||
// Breakout state
|
||
breakout_level: string
|
||
breakout_direction: string
|
||
}
|