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

273 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`)
```typescript
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
```typescript
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
```typescript
'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:
```json
{
"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:
```json
{
"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`:
```typescript
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)