feat: Add per-symbol trading controls for SOL and ETH

- Add SymbolSettings interface with enabled/positionSize/leverage fields
- Implement per-symbol ENV variables (SOLANA_*, ETHEREUM_*)
- Add SOL and ETH sections to settings UI with enable/disable toggles
- Add symbol-specific test buttons (SOL LONG/SHORT, ETH LONG/SHORT)
- Update execute and test endpoints to check symbol enabled status
- Add real-time risk/reward calculator per symbol
- Rename 'Position Sizing' to 'Global Fallback' for clarity
- Fix position manager P&L calculation for externally closed positions
- Fix zero P&L bug affecting 12 historical trades
- Add SQL scripts for recalculating historical P&L data
- Move archive TypeScript files to .archive to fix build

Defaults:
- SOL: 10 base × 10x leverage = 100 notional (profit trading)
- ETH:  base × 1x leverage =  notional (data collection)
- Global: 10 × 10x for BTC and other symbols

Configuration priority: Per-symbol ENV > Market config > Global ENV > Defaults
This commit is contained in:
mindesbunister
2025-11-03 10:28:48 +01:00
parent aa8e9f130a
commit 881a99242d
17 changed files with 1996 additions and 108 deletions

View File

@@ -4,11 +4,21 @@
* Optimized for 5-minute scalping with 10x leverage on Drift Protocol
*/
export interface SymbolSettings {
enabled: boolean
positionSize: number
leverage: number
}
export interface TradingConfig {
// Position sizing
// Position sizing (global fallback)
positionSize: number // USD amount to trade
leverage: number // Leverage multiplier
// Per-symbol settings
solana?: SymbolSettings
ethereum?: SymbolSettings
// Risk management (as percentages of entry price)
stopLossPercent: number // Negative number (e.g., -1.5)
takeProfit1Percent: number // Positive number (e.g., 0.7)
@@ -39,7 +49,6 @@ export interface TradingConfig {
maxDailyDrawdown: number // USD stop trading threshold
maxTradesPerHour: number // Limit overtrading
minTimeBetweenTrades: number // Cooldown period (minutes)
minQualityScore: number // Minimum signal quality score (0-100) to accept trade
// Execution
useMarketOrders: boolean // true = instant execution
@@ -62,10 +71,22 @@ export interface MarketConfig {
// Default configuration for 5-minute scalping with $1000 capital and 10x leverage
export const DEFAULT_TRADING_CONFIG: TradingConfig = {
// Position sizing
// Position sizing (global fallback)
positionSize: 50, // $50 base capital (SAFE FOR TESTING)
leverage: 10, // 10x leverage = $500 position size
// Per-symbol settings
solana: {
enabled: true,
positionSize: 210, // $210 base capital
leverage: 10, // 10x leverage = $2100 notional
},
ethereum: {
enabled: true,
positionSize: 4, // $4 base capital (DATA ONLY - minimum size)
leverage: 1, // 1x leverage = $4 notional
},
// Risk parameters (wider for DEX slippage/wicks)
stopLossPercent: -1.5, // -1.5% price = -15% account loss (closes 100%)
takeProfit1Percent: 0.7, // +0.7% price = +7% account gain (closes 50%)
@@ -96,7 +117,6 @@ export const DEFAULT_TRADING_CONFIG: TradingConfig = {
maxDailyDrawdown: -150, // Stop trading if daily loss exceeds $150 (-15%)
maxTradesPerHour: 6, // Max 6 trades per hour
minTimeBetweenTrades: 10, // 10 minutes cooldown
minQualityScore: 60, // Minimum 60/100 quality score to accept trade
// Execution
useMarketOrders: true, // Use market orders for reliable fills
@@ -127,11 +147,11 @@ export const SUPPORTED_MARKETS: Record<string, MarketConfig> = {
symbol: 'ETH-PERP',
driftMarketIndex: 2,
pythPriceFeedId: '0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace',
minOrderSize: 0.002, // 0.002 ETH minimum (~$7-8 for safety above $4 Drift minimum)
minOrderSize: 0.01, // 0.01 ETH minimum
tickSize: 0.01,
// DATA COLLECTION MODE: Minimal risk
positionSize: 8, // $8 base capital (ensures 0.002 ETH at ~$4000/ETH)
leverage: 1, // 1x leverage = $8 total exposure
// DATA COLLECTION MODE: Minimal risk (Drift minimum 0.01 ETH = ~$38)
positionSize: 40, // $40 base capital (meets exchange minimum)
leverage: 1, // 1x leverage = $40 total exposure
},
}
@@ -157,13 +177,31 @@ export function getMarketConfig(symbol: string): MarketConfig {
return config
}
// Get position size for specific symbol (with market-specific overrides)
export function getPositionSizeForSymbol(symbol: string, baseConfig: TradingConfig): { size: number; leverage: number } {
const marketConfig = getMarketConfig(symbol)
// Get position size for specific symbol (prioritizes per-symbol config)
export function getPositionSizeForSymbol(symbol: string, baseConfig: TradingConfig): { size: number; leverage: number; enabled: boolean } {
// Check per-symbol settings first
if (symbol === 'SOL-PERP' && baseConfig.solana) {
return {
size: baseConfig.solana.positionSize,
leverage: baseConfig.solana.leverage,
enabled: baseConfig.solana.enabled,
}
}
if (symbol === 'ETH-PERP' && baseConfig.ethereum) {
return {
size: baseConfig.ethereum.positionSize,
leverage: baseConfig.ethereum.leverage,
enabled: baseConfig.ethereum.enabled,
}
}
// Fallback to market-specific config, then global config
const marketConfig = getMarketConfig(symbol)
return {
size: marketConfig.positionSize ?? baseConfig.positionSize,
leverage: marketConfig.leverage ?? baseConfig.leverage
leverage: marketConfig.leverage ?? baseConfig.leverage,
enabled: true, // BTC or other markets default to enabled
}
}
@@ -196,10 +234,30 @@ export function validateTradingConfig(config: TradingConfig): void {
// Environment-based configuration
export function getConfigFromEnv(): Partial<TradingConfig> {
return {
const config: Partial<TradingConfig> = {
positionSize: process.env.MAX_POSITION_SIZE_USD
? parseFloat(process.env.MAX_POSITION_SIZE_USD)
: undefined,
// Per-symbol settings from ENV
solana: {
enabled: process.env.SOLANA_ENABLED !== 'false',
positionSize: process.env.SOLANA_POSITION_SIZE
? parseFloat(process.env.SOLANA_POSITION_SIZE)
: 210,
leverage: process.env.SOLANA_LEVERAGE
? parseInt(process.env.SOLANA_LEVERAGE)
: 10,
},
ethereum: {
enabled: process.env.ETHEREUM_ENABLED !== 'false',
positionSize: process.env.ETHEREUM_POSITION_SIZE
? parseFloat(process.env.ETHEREUM_POSITION_SIZE)
: 4,
leverage: process.env.ETHEREUM_LEVERAGE
? parseInt(process.env.ETHEREUM_LEVERAGE)
: 1,
},
leverage: process.env.LEVERAGE
? parseInt(process.env.LEVERAGE)
: undefined,
@@ -257,10 +315,9 @@ export function getConfigFromEnv(): Partial<TradingConfig> {
minTimeBetweenTrades: process.env.MIN_TIME_BETWEEN_TRADES
? parseInt(process.env.MIN_TIME_BETWEEN_TRADES)
: undefined,
minQualityScore: process.env.MIN_QUALITY_SCORE
? parseInt(process.env.MIN_QUALITY_SCORE)
: undefined,
}
return config
}
// Merge configurations