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
This commit is contained in:
mindesbunister
2025-10-31 16:16:03 +01:00
parent a2d7cbcc4c
commit 26f70c6426
3 changed files with 301 additions and 3 deletions

View File

@@ -166,6 +166,14 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
// Get trading configuration
const config = getMergedConfig()
// Get symbol-specific position sizing
const { getPositionSizeForSymbol } = await import('@/config/trading')
const { size: positionSize, leverage } = getPositionSizeForSymbol(driftSymbol, config)
console.log(`📐 Symbol-specific sizing for ${driftSymbol}:`)
console.log(` Position size: $${positionSize}`)
console.log(` Leverage: ${leverage}x`)
// Initialize Drift service if not already initialized
const driftService = await initializeDriftService()
@@ -217,12 +225,12 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
}
// Calculate position size with leverage
const positionSizeUSD = config.positionSize * config.leverage
const positionSizeUSD = positionSize * leverage
console.log(`💰 Opening ${body.direction} position:`)
console.log(` Symbol: ${driftSymbol}`)
console.log(` Base size: $${config.positionSize}`)
console.log(` Leverage: ${config.leverage}x`)
console.log(` Base size: $${positionSize}`)
console.log(` Leverage: ${leverage}x`)
console.log(` Total position: $${positionSizeUSD}`)
// Open position

View File

@@ -54,6 +54,9 @@ export interface MarketConfig {
pythPriceFeedId: string
minOrderSize: number
tickSize: number
// Position sizing overrides (optional)
positionSize?: number
leverage?: number
}
// Default configuration for 5-minute scalping with $1000 capital and 10x leverage
@@ -108,6 +111,7 @@ export const SUPPORTED_MARKETS: Record<string, MarketConfig> = {
pythPriceFeedId: '0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d',
minOrderSize: 0.1, // 0.1 SOL minimum
tickSize: 0.0001,
// Use default config values (positionSize: 50, leverage: 10)
},
'BTC-PERP': {
symbol: 'BTC-PERP',
@@ -115,6 +119,7 @@ export const SUPPORTED_MARKETS: Record<string, MarketConfig> = {
pythPriceFeedId: '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43',
minOrderSize: 0.001, // 0.001 BTC minimum
tickSize: 0.01,
// Use default config values
},
'ETH-PERP': {
symbol: 'ETH-PERP',
@@ -122,6 +127,9 @@ export const SUPPORTED_MARKETS: Record<string, MarketConfig> = {
pythPriceFeedId: '0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace',
minOrderSize: 0.01, // 0.01 ETH minimum
tickSize: 0.01,
// DATA COLLECTION MODE: Minimal risk
positionSize: 1, // $1 USD base capital only
leverage: 1, // 1x leverage = $1 position size total
},
}
@@ -147,6 +155,16 @@ 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)
return {
size: marketConfig.positionSize ?? baseConfig.positionSize,
leverage: marketConfig.leverage ?? baseConfig.leverage
}
}
// Validate trading configuration
export function validateTradingConfig(config: TradingConfig): void {
if (config.positionSize <= 0) {

View File

@@ -0,0 +1,272 @@
# 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)