feat: Add position scaling for strong confirmation signals
**Feature: Position Scaling** Allows adding to existing profitable positions when high-quality signals confirm trend strength. **Configuration (config/trading.ts):** - enablePositionScaling: false (disabled by default - enable after testing) - minScaleQualityScore: 75 (higher bar than initial 60) - minProfitForScale: 0.4% (must be at/past TP1) - maxScaleMultiplier: 2.0 (max 200% of original size) - scaleSizePercent: 50% (add 50% of original position) - minAdxIncrease: 5 (ADX must strengthen) - maxPricePositionForScale: 70% (don't chase resistance) **Validation Logic (check-risk endpoint):** Same-direction signal triggers scaling check if enabled: 1. Quality score ≥75 (stronger than initial entry) 2. Position profitable ≥0.4% (at/past TP1) 3. ADX increased ≥5 points (trend strengthening) 4. Price position <70% (not near resistance) 5. Total size <2x original (risk management) 6. Returns 'allowed: true, reason: Position scaling' if all pass **Execution (execute endpoint):** - Opens additional position at scale size (50% of original) - Updates ActiveTrade: timesScaled, totalScaleAdded, currentSize - Tracks originalAdx from first entry for comparison - Returns 'action: scaled' with scale details **ActiveTrade Interface:** Added fields: - originalAdx?: number (for scaling validation) - timesScaled?: number (track scaling count) - totalScaleAdded?: number (total USD added) **Example Scenario:** 1. LONG SOL at $176 (quality: 45, ADX: 13.4) - weak but entered 2. Price hits $176.70 (+0.4%) - at TP1 3. New LONG signal (quality: 78, ADX: 19) - strong confirmation 4. Scaling validation: ✅ Quality 78 ✅ Profit +0.4% ✅ ADX +5.6 ✅ Price 68% 5. Adds 50% more position at $176.70 6. Total position: 150% of original size **Conservative Design:** - Disabled by default (requires manual enabling) - Only scales INTO profitable positions (never averaging down) - Requires significant quality improvement (75 vs 60) - Requires trend confirmation (ADX increase) - Hard cap at 2x original size - Won't chase near resistance levels **Next Steps:** 1. Enable in settings: ENABLE_POSITION_SCALING=true 2. Test with small positions first 3. Monitor data: do scaled positions outperform? 4. Adjust thresholds based on results **Safety:** - All existing duplicate prevention logic intact - Flip logic unchanged (still requires quality check) - Position Manager tracks scaling state - Can be toggled on/off without code changes
This commit is contained in:
@@ -213,18 +213,79 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
||||
trade => trade.symbol === driftSymbol && trade.direction !== body.direction
|
||||
)
|
||||
|
||||
// SAFETY CHECK: Prevent multiple positions on same symbol
|
||||
// Check for same direction position (scaling vs duplicate)
|
||||
const sameDirectionPosition = existingTrades.find(
|
||||
trade => trade.symbol === driftSymbol && trade.direction === body.direction
|
||||
)
|
||||
|
||||
if (sameDirectionPosition) {
|
||||
// Position scaling enabled - scale into existing position
|
||||
if (config.enablePositionScaling) {
|
||||
console.log(`📈 POSITION SCALING: Adding to existing ${body.direction} position on ${driftSymbol}`)
|
||||
|
||||
// Calculate scale size
|
||||
const scaleSize = (positionSize * leverage) * (config.scaleSizePercent / 100)
|
||||
|
||||
console.log(`💰 Scaling position:`)
|
||||
console.log(` Original size: $${sameDirectionPosition.positionSize}`)
|
||||
console.log(` Scale size: $${scaleSize} (${config.scaleSizePercent}% of original)`)
|
||||
console.log(` Leverage: ${leverage}x`)
|
||||
|
||||
// Open additional position
|
||||
const scaleResult = await openPosition({
|
||||
symbol: driftSymbol,
|
||||
direction: body.direction,
|
||||
sizeUSD: scaleSize,
|
||||
slippageTolerance: config.slippageTolerance,
|
||||
})
|
||||
|
||||
if (!scaleResult.success) {
|
||||
console.error('❌ Failed to scale position:', scaleResult.error)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Position scaling failed',
|
||||
message: scaleResult.error,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
console.log(`✅ Scaled into position at $${scaleResult.fillPrice?.toFixed(4)}`)
|
||||
|
||||
// Update Position Manager tracking
|
||||
const timesScaled = (sameDirectionPosition.timesScaled || 0) + 1
|
||||
const totalScaleAdded = (sameDirectionPosition.totalScaleAdded || 0) + scaleSize
|
||||
const newTotalSize = sameDirectionPosition.currentSize + (scaleResult.fillSize || 0)
|
||||
|
||||
// Update the trade tracking (simplified - just update the active trade object)
|
||||
sameDirectionPosition.timesScaled = timesScaled
|
||||
sameDirectionPosition.totalScaleAdded = totalScaleAdded
|
||||
sameDirectionPosition.currentSize = newTotalSize
|
||||
|
||||
console.log(`📊 Position scaled: ${timesScaled}x total, $${totalScaleAdded.toFixed(2)} added`)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
action: 'scaled',
|
||||
positionId: sameDirectionPosition.positionId,
|
||||
symbol: driftSymbol,
|
||||
direction: body.direction,
|
||||
scalePrice: scaleResult.fillPrice,
|
||||
scaleSize: scaleSize,
|
||||
totalSize: newTotalSize,
|
||||
timesScaled: timesScaled,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
}
|
||||
|
||||
// Scaling disabled - block duplicate
|
||||
console.log(`⛔ DUPLICATE POSITION BLOCKED: Already have ${body.direction} position on ${driftSymbol}`)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Duplicate position detected',
|
||||
message: `Already have an active ${body.direction} position on ${driftSymbol}. Close it first.`,
|
||||
message: `Already have an active ${body.direction} position on ${driftSymbol}. Enable position scaling in settings to add to this position.`,
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
@@ -366,6 +427,10 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
||||
maxAdverseExcursion: 0,
|
||||
maxFavorablePrice: entryPrice,
|
||||
maxAdversePrice: entryPrice,
|
||||
// Position scaling tracking
|
||||
originalAdx: body.adx, // Store for scaling validation
|
||||
timesScaled: 0,
|
||||
totalScaleAdded: 0,
|
||||
priceCheckCount: 0,
|
||||
lastPrice: entryPrice,
|
||||
lastUpdateTime: Date.now(),
|
||||
|
||||
Reference in New Issue
Block a user