Files
trading_bot_v4/docs/guides/SYMBOL_SPECIFIC_SIZING.md
mindesbunister 26f70c6426 feat: implement symbol-specific position sizing for multi-asset trading
- Extended MarketConfig with optional positionSize and leverage fields
- Configured ETH-PERP at  @ 1x leverage for minimal-risk data collection
- Created getPositionSizeForSymbol() helper function in config/trading.ts
- Integrated symbol-specific sizing into execute endpoint
- Added comprehensive guide in docs/guides/SYMBOL_SPECIFIC_SIZING.md

Purpose: Enable ETH trading for faster signal quality data collection
         while preserving SOL's profit-generation sizing (0 @ 10x)

Next: Create ETH alert in TradingView and restart bot
2025-10-31 16:16:03 +01:00

7.4 KiB
Raw Permalink Blame History

Symbol-Specific Position Sizing Guide

Overview

The bot now supports different position sizes and leverage for each trading symbol. This enables strategies like:

  • High-risk symbols (SOL): $50 @ 10x leverage = $500 exposure (profit generation)
  • Low-risk symbols (ETH): $1 @ 1x leverage = $1 exposure (data collection)

Configuration

1. Market Configuration (config/trading.ts)

export const SUPPORTED_MARKETS: Record<string, MarketConfig> = {
  'SOL-PERP': {
    driftMarketIndex: 0,
    pythFeedId: '0xef0d...',
    // Uses default config.positionSize and config.leverage
  },
  'ETH-PERP': {
    driftMarketIndex: 1,
    pythFeedId: '0xff61...',
    // OVERRIDE: Data collection mode with minimal risk
    positionSize: 1,    // $1 base size
    leverage: 1,        // 1x leverage = $1 total exposure
  },
  'BTC-PERP': {
    driftMarketIndex: 2,
    pythFeedId: '0xe62d...',
    // Uses default config.positionSize and config.leverage
  },
}

2. Helper Function

export function getPositionSizeForSymbol(
  symbol: string,
  baseConfig: TradingConfig
): { size: number; leverage: number } {
  const marketConfig = SUPPORTED_MARKETS[symbol]
  if (!marketConfig) {
    throw new Error(`Unsupported symbol: ${symbol}`)
  }

  return {
    size: marketConfig.positionSize ?? baseConfig.positionSize,
    leverage: marketConfig.leverage ?? baseConfig.leverage,
  }
}

3. Execute Endpoint Integration

The app/api/trading/execute/route.ts endpoint now:

  1. Normalizes symbol (ETHUSDT → ETH-PERP)
  2. Gets merged config via getMergedConfig()
  3. Calls getPositionSizeForSymbol(symbol, config) to get symbol-specific sizing
  4. Uses returned { size, leverage } for position calculation

Example: ETH Data Collection Setup

Goal

Collect as many ETH signals as possible with minimal risk to improve signal quality analysis.

Configuration

'ETH-PERP': {
  driftMarketIndex: 1,
  pythFeedId: '0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace',
  positionSize: 1,    // $1 base size
  leverage: 1,        // No leverage
}

Expected Behavior

  • TradingView sends: ETHUSDT LONG or ETHUSDT SHORT
  • Bot normalizes to: ETH-PERP
  • Bot loads sizing: positionSize: 1, leverage: 1
  • Position size: $1 × 1 = $1 total exposure
  • At ETH = $3,500: Opens ~0.00029 ETH position
  • Risk: Maximum loss = ~$1 (if emergency stop hits)

Console Output

📊 Normalized symbol: ETHUSDT → ETH-PERP
📐 Symbol-specific sizing for ETH-PERP:
   Position size: $1
   Leverage: 1x
💰 Opening LONG position:
   Symbol: ETH-PERP
   Base size: $1
   Leverage: 1x
   Total position: $1

Testing

1. SOL Trade (Default Sizing)

Send webhook:

{
  "symbol": "SOLUSDT",
  "direction": "LONG"
}

Expected logs:

📐 Symbol-specific sizing for SOL-PERP:
   Position size: $50
   Leverage: 10x
   Total position: $500

2. ETH Trade (Override Sizing)

Send webhook:

{
  "symbol": "ETHUSDT",
  "direction": "SHORT"
}

Expected logs:

📐 Symbol-specific sizing for ETH-PERP:
   Position size: $1
   Leverage: 1x
   Total position: $1

3. Verify in Drift

Check open positions - ETH position should show ~$1 notional value.

Strategy Rationale

Why $1 @ 1x for ETH?

  1. Data Collection Priority: ETH shows 2-3x more signals than SOL
  2. Risk Management: Cross margin means ETH position uses shared collateral
  3. No Profit Pressure: Not trying to make money on ETH, just gather quality scores
  4. Statistical Significance: Need 20-50 trades with quality scores before Phase 2
  5. Collateral Preservation: Current SOL long uses most collateral, can't risk large ETH positions

When to Increase ETH Sizing?

Only after:

  • Phase 1 complete (20-50 trades with quality scores)
  • ETH signal quality proven ≥ SOL (win rate, profit factor)
  • Sufficient collateral available (not at risk of liquidation)
  • Phase 2 ATR-based targets implemented and validated

Cross Margin Considerations

Critical: All positions (SOL, ETH, BTC) share the same collateral pool on Drift.

Collateral Math

Total Collateral: $500
Current SOL position: ~$450 used
Free collateral: ~$50

Adding ETH @ $1:
- Maintenance margin: ~$0.05 (5%)
- Initial margin: ~$0.10 (10%)
- Impact: Minimal ✅

Adding ETH @ $50:
- Maintenance margin: ~$2.50
- Initial margin: ~$5
- Risk: Higher liquidation risk ⚠️

Safety Buffer

Keep at least 30% free collateral at all times:

  • Total: $500
  • Max used: $350 (70%)
  • Reserve: $150 (30%) for margin calls and new positions

Future Enhancements

Phase 2: Reserve-Based Sizing Module

Create lib/trading/position-sizing.ts:

export interface PositionSizingParams {
  symbol: string
  direction: 'long' | 'short'
  totalCollateral: number
  usedCollateral: number
  marketConfig: MarketConfig
  baseConfig: TradingConfig
}

export function calculatePositionSize(params: PositionSizingParams): number {
  const freeCollateral = params.totalCollateral - params.usedCollateral
  const reservePercent = 0.30 // Keep 30% reserve
  const availableForTrade = freeCollateral * (1 - reservePercent)
  
  // Get base size from market config or default
  const baseSize = params.marketConfig.positionSize ?? params.baseConfig.positionSize
  const leverage = params.marketConfig.leverage ?? params.baseConfig.leverage
  const requiredCollateral = baseSize * leverage * 0.10 // 10% initial margin
  
  // If not enough collateral, reduce position size
  if (requiredCollateral > availableForTrade) {
    return availableForTrade / leverage / 0.10
  }
  
  return baseSize
}

Benefits:

  • Prevents over-leveraging
  • Maintains safety buffer
  • Dynamic sizing based on account state
  • Supports multiple concurrent positions

When to implement: After Phase 1 validation, before increasing ETH position sizes.

Troubleshooting

Issue: ETH still trading at $50

Check:

  1. Restart bot after config changes: docker restart trading-bot-v4
  2. Verify config loaded: Check console logs for "Symbol-specific sizing"
  3. Ensure symbol normalization: ETHUSDT → ETH-PERP (not ETH-USD)

Issue: Position Manager using wrong size

Root cause: Position Manager calculates position amounts from on-chain data, not config.

Behavior:

  • Execute endpoint uses positionSize and leverage from config
  • Position Manager reads actual position size from Drift
  • They're independent systems (by design for safety)

Issue: Database shows wrong positionSize

Root cause: Database stores actual executed size, not config size.

Expected:

  • Config: ETH-PERP positionSize: 1
  • Database: positionSize: 1.0 (matches execution)
  • Drift on-chain: 0.00029 ETH ($1 notional)

All three should align. If not, config didn't load properly.

Summary

Symbol-specific sizing enables:

  • Multi-asset trading with different risk profiles
  • Data collection strategies (ETH @ $1)
  • Profit generation strategies (SOL @ $50)
  • Cross-margin safety (minimal ETH exposure)
  • Faster signal quality validation (more trades)

Next steps:

  1. Config updated (DONE)
  2. Execute endpoint integrated (DONE)
  3. ⏸️ Create ETH alert in TradingView (USER ACTION)
  4. ⏸️ Restart bot: docker restart trading-bot-v4
  5. ⏸️ Monitor first ETH trade for correct sizing
  6. ⏸️ Collect 20-50 trades with quality scores
  7. ⏸️ Proceed to Phase 2 (ATR-based dynamic targets)