feat: Phase 7.3 - 1-Minute Adaptive TP/SL (DEPLOYED Nov 27, 2025)
- Query fresh 1-minute ADX from market cache every monitoring loop - Dynamically adjust trailing stop based on trend strength changes - Acceleration bonus: ADX increased >5 points = 1.3× wider trail - Deceleration penalty: ADX decreased >3 points = 0.7× tighter trail - Combined with existing ADX strength tiers and profit acceleration - Expected impact: +,000-3,000 over 100 trades by capturing accelerating trends - Directly addresses MA crossover pattern (ADX 22.5→29.5 in 35 minutes) - Files: lib/trading/position-manager.ts (adaptive logic), 1MIN_DATA_ENHANCEMENTS_ROADMAP.md (Phase 7.3 complete)
This commit is contained in:
@@ -11,6 +11,7 @@ import { getMergedConfig, TradingConfig, getMarketConfig } from '../../config/tr
|
||||
import { updateTradeExit, updateTradeState, getOpenTrades } from '../database/trades'
|
||||
import { sendPositionClosedNotification } from '../notifications/telegram'
|
||||
import { getStopHuntTracker } from './stop-hunt-tracker'
|
||||
import { getMarketDataCache } from './market-data-cache'
|
||||
|
||||
export interface ActiveTrade {
|
||||
id: string
|
||||
@@ -1362,32 +1363,68 @@ export class PositionManager {
|
||||
|
||||
// If trailing stop is active, adjust SL dynamically
|
||||
if (trade.trailingStopActive) {
|
||||
// Calculate ATR-based trailing distance with ADX trend strength multiplier
|
||||
// PHASE 7.3: 1-Minute Adaptive TP/SL (Nov 27, 2025)
|
||||
// Query fresh 1-minute ADX data and adjust trailing stop based on trend strength changes
|
||||
let currentADX = trade.adxAtEntry || 0
|
||||
let adxChange = 0
|
||||
let usingFreshData = false
|
||||
|
||||
try {
|
||||
const marketCache = getMarketDataCache()
|
||||
const freshData = marketCache.get(trade.symbol)
|
||||
|
||||
if (freshData && freshData.adx) {
|
||||
currentADX = freshData.adx
|
||||
adxChange = currentADX - (trade.adxAtEntry || 0)
|
||||
usingFreshData = true
|
||||
|
||||
console.log(`📊 1-min ADX update: Entry ${(trade.adxAtEntry || 0).toFixed(1)} → Current ${currentADX.toFixed(1)} (${adxChange >= 0 ? '+' : ''}${adxChange.toFixed(1)} change)`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Could not fetch fresh ADX data, using entry ADX: ${error}`)
|
||||
}
|
||||
|
||||
// Calculate ATR-based trailing distance with ADAPTIVE ADX multiplier
|
||||
let trailingDistancePercent: number
|
||||
|
||||
if (trade.atrAtEntry && trade.atrAtEntry > 0) {
|
||||
// Start with base ATR multiplier
|
||||
let trailMultiplier = this.config.trailingStopAtrMultiplier
|
||||
|
||||
// ADX-based trend strength adjustment (graduated)
|
||||
if (trade.adxAtEntry && trade.adxAtEntry > 0) {
|
||||
if (trade.adxAtEntry > 30) {
|
||||
// ADAPTIVE ADX-based trend strength adjustment (Nov 27, 2025)
|
||||
// Uses CURRENT 1-minute ADX if available, falls back to entry ADX
|
||||
if (currentADX > 0) {
|
||||
if (currentADX > 30) {
|
||||
// Very strong trend (ADX > 30): 50% wider trail
|
||||
trailMultiplier *= 1.5
|
||||
console.log(`📈 Very strong trend (ADX ${trade.adxAtEntry.toFixed(1)}): Trail multiplier ${this.config.trailingStopAtrMultiplier}x → ${trailMultiplier.toFixed(2)}x`)
|
||||
} else if (trade.adxAtEntry > 25) {
|
||||
console.log(`📈 ${usingFreshData ? '1-min' : 'Entry'} ADX very strong (${currentADX.toFixed(1)}): Trail multiplier ${this.config.trailingStopAtrMultiplier}x → ${trailMultiplier.toFixed(2)}x`)
|
||||
} else if (currentADX > 25) {
|
||||
// Strong trend (ADX 25-30): 25% wider trail
|
||||
trailMultiplier *= 1.25
|
||||
console.log(`📈 Strong trend (ADX ${trade.adxAtEntry.toFixed(1)}): Trail multiplier ${this.config.trailingStopAtrMultiplier}x → ${trailMultiplier.toFixed(2)}x`)
|
||||
console.log(`📈 ${usingFreshData ? '1-min' : 'Entry'} ADX strong (${currentADX.toFixed(1)}): Trail multiplier ${this.config.trailingStopAtrMultiplier}x → ${trailMultiplier.toFixed(2)}x`)
|
||||
}
|
||||
// Else: weak/moderate trend, use base multiplier
|
||||
|
||||
// ACCELERATION BONUS: If ADX increased significantly, widen trail even more
|
||||
if (usingFreshData && adxChange > 5) {
|
||||
const oldMultiplier = trailMultiplier
|
||||
trailMultiplier *= 1.3
|
||||
console.log(`🚀 ADX acceleration (+${adxChange.toFixed(1)} points): Trail multiplier ${oldMultiplier.toFixed(2)}x → ${trailMultiplier.toFixed(2)}x`)
|
||||
}
|
||||
|
||||
// DECELERATION PENALTY: If ADX decreased significantly, tighten trail
|
||||
if (usingFreshData && adxChange < -3) {
|
||||
const oldMultiplier = trailMultiplier
|
||||
trailMultiplier *= 0.7
|
||||
console.log(`⚠️ ADX deceleration (${adxChange.toFixed(1)} points): Trail multiplier ${oldMultiplier.toFixed(2)}x → ${trailMultiplier.toFixed(2)}x (tighter to protect)`)
|
||||
}
|
||||
}
|
||||
|
||||
// Profit acceleration: bigger profit = wider trail
|
||||
if (profitPercent > 2.0) {
|
||||
const oldMultiplier = trailMultiplier
|
||||
trailMultiplier *= 1.3
|
||||
console.log(`🚀 Large profit (${profitPercent.toFixed(2)}%): Trail multiplier ${oldMultiplier.toFixed(2)}x → ${trailMultiplier.toFixed(2)}x`)
|
||||
console.log(`💰 Large profit (${profitPercent.toFixed(2)}%): Trail multiplier ${oldMultiplier.toFixed(2)}x → ${trailMultiplier.toFixed(2)}x`)
|
||||
}
|
||||
|
||||
// ATR-based: Use ATR% * adjusted multiplier
|
||||
@@ -1400,7 +1437,7 @@ export class PositionManager {
|
||||
Math.min(this.config.trailingStopMaxPercent, rawDistance)
|
||||
)
|
||||
|
||||
console.log(`📊 ATR-based trailing: ${trade.atrAtEntry.toFixed(4)} (${atrPercent.toFixed(2)}%) × ${trailMultiplier.toFixed(2)}x = ${trailingDistancePercent.toFixed(2)}%`)
|
||||
console.log(`📊 Adaptive trailing: ATR ${trade.atrAtEntry.toFixed(4)} (${atrPercent.toFixed(2)}%) × ${trailMultiplier.toFixed(2)}x = ${trailingDistancePercent.toFixed(2)}%`)
|
||||
} else {
|
||||
// Fallback to configured legacy percent with min/max clamping
|
||||
trailingDistancePercent = Math.max(
|
||||
|
||||
Reference in New Issue
Block a user