Restore context metrics in execute endpoint and clean up test files
This commit is contained in:
@@ -100,7 +100,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Insufficient collateral',
|
error: 'Insufficient collateral',
|
||||||
message: 'Free collateral: $' + health.freeCollateral.toFixed(2),
|
message: `Free collateral: $${health.freeCollateral.toFixed(2)}`,
|
||||||
},
|
},
|
||||||
{ status: 400 }
|
{ status: 400 }
|
||||||
)
|
)
|
||||||
@@ -128,7 +128,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
console.error('❌ Failed to close opposite position:', closeResult.error)
|
console.error('❌ Failed to close opposite position:', closeResult.error)
|
||||||
// Continue anyway - we'll try to open the new position
|
// Continue anyway - we'll try to open the new position
|
||||||
} else {
|
} else {
|
||||||
console.log('✅ Closed ' + oppositePosition.direction + ' position at $' + closeResult.closePrice?.toFixed(4) + ' (P&L: $' + closeResult.realizedPnL?.toFixed(2) + ')')
|
console.log(`✅ Closed ${oppositePosition.direction} position at $${closeResult.closePrice?.toFixed(4)} (P&L: $${closeResult.realizedPnL?.toFixed(2)})`)
|
||||||
|
|
||||||
// Position Manager will handle cleanup (including order cancellation)
|
// Position Manager will handle cleanup (including order cancellation)
|
||||||
// The executeExit method already removes the trade and updates database
|
// The executeExit method already removes the trade and updates database
|
||||||
@@ -141,33 +141,11 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
// Calculate position size with leverage
|
// Calculate position size with leverage
|
||||||
const positionSizeUSD = config.positionSize * config.leverage
|
const positionSizeUSD = config.positionSize * config.leverage
|
||||||
|
|
||||||
console.log('💰 Opening ' + body.direction + ' position:')
|
console.log(`💰 Opening ${body.direction} position:`)
|
||||||
console.log(' Symbol: ' + driftSymbol)
|
console.log(` Symbol: ${driftSymbol}`)
|
||||||
console.log(' Base size: $' + config.positionSize)
|
console.log(` Base size: $${config.positionSize}`)
|
||||||
console.log(' Leverage: ' + config.leverage + 'x')
|
console.log(` Leverage: ${config.leverage}x`)
|
||||||
console.log(' Total position: $' + positionSizeUSD)
|
console.log(` Total position: $${positionSizeUSD}`)
|
||||||
|
|
||||||
// Capture market context BEFORE opening position
|
|
||||||
const { getMarketConfig } = await import('@/config/trading')
|
|
||||||
const marketConfig = getMarketConfig(driftSymbol)
|
|
||||||
|
|
||||||
let expectedEntryPrice: number | undefined
|
|
||||||
let fundingRateAtEntry: number | undefined
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get expected entry price from oracle
|
|
||||||
expectedEntryPrice = await driftService.getOraclePrice(marketConfig.driftMarketIndex)
|
|
||||||
console.log('📊 Expected entry price: $' + expectedEntryPrice.toFixed(4))
|
|
||||||
|
|
||||||
// Get funding rate
|
|
||||||
fundingRateAtEntry = await driftService.getFundingRate(marketConfig.driftMarketIndex) || undefined
|
|
||||||
if (fundingRateAtEntry) {
|
|
||||||
console.log('💸 Funding rate: ' + (fundingRateAtEntry * 100).toFixed(4) + '%')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('⚠️ Failed to capture market context:', error)
|
|
||||||
// Don't fail the trade if market context capture fails
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open position
|
// Open position
|
||||||
const openResult = await openPosition({
|
const openResult = await openPosition({
|
||||||
@@ -212,9 +190,9 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
config.hardStopPercent,
|
config.hardStopPercent,
|
||||||
body.direction
|
body.direction
|
||||||
)
|
)
|
||||||
console.log('🛡️ Dual stop system enabled:')
|
console.log('🛡️🛡️ Dual stop system enabled:')
|
||||||
console.log(' Soft stop: $' + softStopPrice.toFixed(4) + ' (' + config.softStopPercent + '%)')
|
console.log(` Soft stop: $${softStopPrice.toFixed(4)} (${config.softStopPercent}%)`)
|
||||||
console.log(' Hard stop: $' + hardStopPrice.toFixed(4) + ' (' + config.hardStopPercent + '%)')
|
console.log(` Hard stop: $${hardStopPrice.toFixed(4)} (${config.hardStopPercent}%)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const tp1Price = calculatePrice(
|
const tp1Price = calculatePrice(
|
||||||
@@ -230,10 +208,10 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
)
|
)
|
||||||
|
|
||||||
console.log('📊 Trade targets:')
|
console.log('📊 Trade targets:')
|
||||||
console.log(' Entry: $' + entryPrice.toFixed(4))
|
console.log(` Entry: $${entryPrice.toFixed(4)}`)
|
||||||
console.log(' SL: $' + stopLossPrice.toFixed(4) + ' (' + config.stopLossPercent + '%)')
|
console.log(` SL: $${stopLossPrice.toFixed(4)} (${config.stopLossPercent}%)`)
|
||||||
console.log(' TP1: $' + tp1Price.toFixed(4) + ' (' + config.takeProfit1Percent + '%)')
|
console.log(` TP1: $${tp1Price.toFixed(4)} (${config.takeProfit1Percent}%)`)
|
||||||
console.log(' TP2: $' + tp2Price.toFixed(4) + ' (' + config.takeProfit2Percent + '%)')
|
console.log(` TP2: $${tp2Price.toFixed(4)} (${config.takeProfit2Percent}%)`)
|
||||||
|
|
||||||
// Calculate emergency stop
|
// Calculate emergency stop
|
||||||
const emergencyStopPrice = calculatePrice(
|
const emergencyStopPrice = calculatePrice(
|
||||||
@@ -269,11 +247,6 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
priceCheckCount: 0,
|
priceCheckCount: 0,
|
||||||
lastPrice: entryPrice,
|
lastPrice: entryPrice,
|
||||||
lastUpdateTime: Date.now(),
|
lastUpdateTime: Date.now(),
|
||||||
maxFavorableExcursion: 0,
|
|
||||||
maxAdverseExcursion: 0,
|
|
||||||
maxFavorablePrice: entryPrice,
|
|
||||||
maxAdversePrice: entryPrice,
|
|
||||||
lastDbMetricsUpdate: Date.now(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRITICAL FIX: Place on-chain TP/SL orders BEFORE adding to Position Manager
|
// CRITICAL FIX: Place on-chain TP/SL orders BEFORE adding to Position Manager
|
||||||
@@ -344,7 +317,6 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
symbol: driftSymbol,
|
symbol: driftSymbol,
|
||||||
direction: body.direction,
|
direction: body.direction,
|
||||||
entryPrice,
|
entryPrice,
|
||||||
entrySlippage: openResult.slippage,
|
|
||||||
positionSizeUSD: positionSizeUSD,
|
positionSizeUSD: positionSizeUSD,
|
||||||
leverage: config.leverage,
|
leverage: config.leverage,
|
||||||
stopLossPrice,
|
stopLossPrice,
|
||||||
@@ -363,9 +335,6 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
hardStopPrice,
|
hardStopPrice,
|
||||||
signalStrength: body.signalStrength,
|
signalStrength: body.signalStrength,
|
||||||
timeframe: body.timeframe,
|
timeframe: body.timeframe,
|
||||||
// Market context
|
|
||||||
expectedEntryPrice,
|
|
||||||
fundingRateAtEntry,
|
|
||||||
// Context metrics from TradingView
|
// Context metrics from TradingView
|
||||||
atrAtEntry: body.atr,
|
atrAtEntry: body.atr,
|
||||||
adxAtEntry: body.adx,
|
adxAtEntry: body.adx,
|
||||||
@@ -377,7 +346,7 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
|
|||||||
console.log('💾 Trade saved to database')
|
console.log('💾 Trade saved to database')
|
||||||
} catch (dbError) {
|
} catch (dbError) {
|
||||||
console.error('❌ Failed to save trade to database:', dbError)
|
console.error('❌ Failed to save trade to database:', dbError)
|
||||||
// Don't fail the database save fails
|
// Don't fail the trade if database save fails
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ Trade executed successfully!')
|
console.log('✅ Trade executed successfully!')
|
||||||
|
|||||||
@@ -182,11 +182,6 @@ export async function POST(request: NextRequest): Promise<NextResponse<TestTrade
|
|||||||
priceCheckCount: 0,
|
priceCheckCount: 0,
|
||||||
lastPrice: entryPrice,
|
lastPrice: entryPrice,
|
||||||
lastUpdateTime: Date.now(),
|
lastUpdateTime: Date.now(),
|
||||||
maxFavorableExcursion: 0,
|
|
||||||
maxAdverseExcursion: 0,
|
|
||||||
maxFavorablePrice: entryPrice,
|
|
||||||
maxAdversePrice: entryPrice,
|
|
||||||
lastDbMetricsUpdate: Date.now(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to position manager
|
// Add to position manager
|
||||||
|
|||||||
@@ -180,11 +180,6 @@ export async function POST(request: NextRequest): Promise<NextResponse<TestTrade
|
|||||||
priceCheckCount: 0,
|
priceCheckCount: 0,
|
||||||
lastPrice: entryPrice,
|
lastPrice: entryPrice,
|
||||||
lastUpdateTime: Date.now(),
|
lastUpdateTime: Date.now(),
|
||||||
maxFavorableExcursion: 0,
|
|
||||||
maxAdverseExcursion: 0,
|
|
||||||
maxFavorablePrice: entryPrice,
|
|
||||||
maxAdversePrice: entryPrice,
|
|
||||||
lastDbMetricsUpdate: Date.now(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to position manager for monitoring
|
// Add to position manager for monitoring
|
||||||
|
|||||||
@@ -42,13 +42,6 @@ export interface ActiveTrade {
|
|||||||
peakPnL: number
|
peakPnL: number
|
||||||
peakPrice: number // Track highest price reached (for trailing)
|
peakPrice: number // Track highest price reached (for trailing)
|
||||||
|
|
||||||
// MAE/MFE tracking (Maximum Adverse/Favorable Excursion)
|
|
||||||
maxFavorableExcursion: number // Best profit % reached
|
|
||||||
maxAdverseExcursion: number // Worst drawdown % reached
|
|
||||||
maxFavorablePrice: number // Best price hit
|
|
||||||
maxAdversePrice: number // Worst price hit
|
|
||||||
lastDbMetricsUpdate: number // Last time we updated MAE/MFE in DB (throttle to 5s)
|
|
||||||
|
|
||||||
// Monitoring
|
// Monitoring
|
||||||
priceCheckCount: number
|
priceCheckCount: number
|
||||||
lastPrice: number
|
lastPrice: number
|
||||||
@@ -117,11 +110,6 @@ export class PositionManager {
|
|||||||
unrealizedPnL: pmState?.unrealizedPnL ?? 0,
|
unrealizedPnL: pmState?.unrealizedPnL ?? 0,
|
||||||
peakPnL: pmState?.peakPnL ?? 0,
|
peakPnL: pmState?.peakPnL ?? 0,
|
||||||
peakPrice: pmState?.peakPrice ?? dbTrade.entryPrice,
|
peakPrice: pmState?.peakPrice ?? dbTrade.entryPrice,
|
||||||
maxFavorableExcursion: pmState?.maxFavorableExcursion ?? 0,
|
|
||||||
maxAdverseExcursion: pmState?.maxAdverseExcursion ?? 0,
|
|
||||||
maxFavorablePrice: pmState?.maxFavorablePrice ?? dbTrade.entryPrice,
|
|
||||||
maxAdversePrice: pmState?.maxAdversePrice ?? dbTrade.entryPrice,
|
|
||||||
lastDbMetricsUpdate: Date.now(),
|
|
||||||
priceCheckCount: 0,
|
priceCheckCount: 0,
|
||||||
lastPrice: pmState?.lastPrice ?? dbTrade.entryPrice,
|
lastPrice: pmState?.lastPrice ?? dbTrade.entryPrice,
|
||||||
lastUpdateTime: Date.now(),
|
lastUpdateTime: Date.now(),
|
||||||
@@ -348,13 +336,8 @@ export class PositionManager {
|
|||||||
holdTimeSeconds,
|
holdTimeSeconds,
|
||||||
maxDrawdown: 0,
|
maxDrawdown: 0,
|
||||||
maxGain: trade.peakPnL,
|
maxGain: trade.peakPnL,
|
||||||
// Save final MAE/MFE values
|
|
||||||
maxFavorableExcursion: trade.maxFavorableExcursion,
|
|
||||||
maxAdverseExcursion: trade.maxAdverseExcursion,
|
|
||||||
maxFavorablePrice: trade.maxFavorablePrice,
|
|
||||||
maxAdversePrice: trade.maxAdversePrice,
|
|
||||||
})
|
})
|
||||||
console.log(`💾 External closure recorded: ${exitReason} at $${currentPrice} | P&L: $${realizedPnL.toFixed(2)} | MFE: ${trade.maxFavorableExcursion.toFixed(2)}% | MAE: ${trade.maxAdverseExcursion.toFixed(2)}%`)
|
console.log(`💾 External closure recorded: ${exitReason} at $${currentPrice} | P&L: $${realizedPnL.toFixed(2)}`)
|
||||||
} catch (dbError) {
|
} catch (dbError) {
|
||||||
console.error('❌ Failed to save external closure:', dbError)
|
console.error('❌ Failed to save external closure:', dbError)
|
||||||
}
|
}
|
||||||
@@ -364,25 +347,12 @@ export class PositionManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position exists but size mismatch (partial close by TP1 or TP2?)
|
// Position exists but size mismatch (partial close by TP1?)
|
||||||
if (position.size < trade.currentSize * 0.95) { // 5% tolerance
|
if (position.size < trade.currentSize * 0.95) { // 5% tolerance
|
||||||
console.log(`⚠️ Position size mismatch: expected ${trade.currentSize}, got ${position.size}`)
|
console.log(`⚠️ Position size mismatch: expected ${trade.currentSize}, got ${position.size}`)
|
||||||
|
|
||||||
// Determine if this was TP1 or TP2 based on size
|
|
||||||
const remainingPercent = (position.size / trade.positionSize) * 100
|
|
||||||
|
|
||||||
if (!trade.tp1Hit && remainingPercent < 30) {
|
|
||||||
// First partial close, likely TP1 (should leave ~25%)
|
|
||||||
trade.tp1Hit = true
|
|
||||||
console.log(`✅ TP1 detected on-chain (${remainingPercent.toFixed(1)}% remaining)`)
|
|
||||||
} else if (trade.tp1Hit && !trade.tp2Hit && remainingPercent < 10) {
|
|
||||||
// Second partial close, likely TP2 (should leave ~5% runner)
|
|
||||||
trade.tp2Hit = true
|
|
||||||
console.log(`✅ TP2 detected on-chain (${remainingPercent.toFixed(1)}% runner remaining)`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update current size to match reality
|
// Update current size to match reality
|
||||||
trade.currentSize = position.size * (trade.positionSize / trade.currentSize) // Convert to USD
|
trade.currentSize = position.size * (trade.positionSize / trade.currentSize) // Convert to USD
|
||||||
|
trade.tp1Hit = true
|
||||||
await this.saveTradeState(trade)
|
await this.saveTradeState(trade)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,23 +381,6 @@ export class PositionManager {
|
|||||||
const accountPnL = profitPercent * trade.leverage
|
const accountPnL = profitPercent * trade.leverage
|
||||||
trade.unrealizedPnL = (trade.currentSize * profitPercent) / 100
|
trade.unrealizedPnL = (trade.currentSize * profitPercent) / 100
|
||||||
|
|
||||||
// Track MAE/MFE (Maximum Adverse/Favorable Excursion)
|
|
||||||
if (profitPercent > trade.maxFavorableExcursion) {
|
|
||||||
trade.maxFavorableExcursion = profitPercent
|
|
||||||
trade.maxFavorablePrice = currentPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
if (profitPercent < trade.maxAdverseExcursion) {
|
|
||||||
trade.maxAdverseExcursion = profitPercent
|
|
||||||
trade.maxAdversePrice = currentPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update MAE/MFE in database (throttled to every 5 seconds to avoid spam)
|
|
||||||
if (Date.now() - trade.lastDbMetricsUpdate > 5000) {
|
|
||||||
await this.updateTradeMetrics(trade)
|
|
||||||
trade.lastDbMetricsUpdate = Date.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track peak P&L
|
// Track peak P&L
|
||||||
if (trade.unrealizedPnL > trade.peakPnL) {
|
if (trade.unrealizedPnL > trade.peakPnL) {
|
||||||
trade.peakPnL = trade.unrealizedPnL
|
trade.peakPnL = trade.unrealizedPnL
|
||||||
@@ -515,7 +468,7 @@ export class PositionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. Take profit 2 (remaining position)
|
// 5. Take profit 2 (remaining position)
|
||||||
if (trade.tp1Hit && !trade.tp2Hit && this.shouldTakeProfit2(currentPrice, trade)) {
|
if (trade.tp1Hit && this.shouldTakeProfit2(currentPrice, trade)) {
|
||||||
console.log(`🎊 TP2 HIT: ${trade.symbol} at ${profitPercent.toFixed(2)}%`)
|
console.log(`🎊 TP2 HIT: ${trade.symbol} at ${profitPercent.toFixed(2)}%`)
|
||||||
|
|
||||||
// Calculate how much to close based on TP2 size percent
|
// Calculate how much to close based on TP2 size percent
|
||||||
@@ -621,13 +574,8 @@ export class PositionManager {
|
|||||||
holdTimeSeconds,
|
holdTimeSeconds,
|
||||||
maxDrawdown: 0, // TODO: Track this
|
maxDrawdown: 0, // TODO: Track this
|
||||||
maxGain: trade.peakPnL,
|
maxGain: trade.peakPnL,
|
||||||
// Save final MAE/MFE values
|
|
||||||
maxFavorableExcursion: trade.maxFavorableExcursion,
|
|
||||||
maxAdverseExcursion: trade.maxAdverseExcursion,
|
|
||||||
maxFavorablePrice: trade.maxFavorablePrice,
|
|
||||||
maxAdversePrice: trade.maxAdversePrice,
|
|
||||||
})
|
})
|
||||||
console.log('💾 Trade saved to database with MAE: ' + trade.maxAdverseExcursion.toFixed(2) + '% | MFE: ' + trade.maxFavorableExcursion.toFixed(2) + '%')
|
console.log('💾 Trade saved to database')
|
||||||
} catch (dbError) {
|
} catch (dbError) {
|
||||||
console.error('❌ Failed to save trade exit to database:', dbError)
|
console.error('❌ Failed to save trade exit to database:', dbError)
|
||||||
// Don't fail the close if database fails
|
// Don't fail the close if database fails
|
||||||
@@ -747,10 +695,6 @@ export class PositionManager {
|
|||||||
unrealizedPnL: trade.unrealizedPnL,
|
unrealizedPnL: trade.unrealizedPnL,
|
||||||
peakPnL: trade.peakPnL,
|
peakPnL: trade.peakPnL,
|
||||||
lastPrice: trade.lastPrice,
|
lastPrice: trade.lastPrice,
|
||||||
maxFavorableExcursion: trade.maxFavorableExcursion,
|
|
||||||
maxAdverseExcursion: trade.maxAdverseExcursion,
|
|
||||||
maxFavorablePrice: trade.maxFavorablePrice,
|
|
||||||
maxAdversePrice: trade.maxAdversePrice,
|
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Failed to save trade state:', error)
|
console.error('❌ Failed to save trade state:', error)
|
||||||
@@ -776,29 +720,6 @@ export class PositionManager {
|
|||||||
symbols,
|
symbols,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update MAE/MFE metrics in database (throttled)
|
|
||||||
*/
|
|
||||||
private async updateTradeMetrics(trade: ActiveTrade): Promise<void> {
|
|
||||||
try {
|
|
||||||
const { getPrismaClient } = await import('../database/trades')
|
|
||||||
const prisma = getPrismaClient()
|
|
||||||
|
|
||||||
await prisma.trade.update({
|
|
||||||
where: { id: trade.id },
|
|
||||||
data: {
|
|
||||||
maxFavorableExcursion: trade.maxFavorableExcursion,
|
|
||||||
maxAdverseExcursion: trade.maxAdverseExcursion,
|
|
||||||
maxFavorablePrice: trade.maxFavorablePrice,
|
|
||||||
maxAdversePrice: trade.maxAdversePrice,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
// Silent failure to avoid disrupting monitoring loop
|
|
||||||
console.error('Failed to update trade metrics:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Singleton instance
|
// Singleton instance
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"method": "POST",
|
|
||||||
"url": "http://10.0.0.48:3001/api/trading/check-risk",
|
|
||||||
"authentication": "genericCredentialType",
|
|
||||||
"genericAuthType": "httpHeaderAuth",
|
|
||||||
"sendHeaders": true,
|
|
||||||
"headerParameters": {
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "Authorization",
|
|
||||||
"value": "Bearer 2a344f0149442c857fb56c038c0c7d1b113883b830bec792c76f1e0efa15d6bb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Content-Type",
|
|
||||||
"value": "application/json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"sendBody": true,
|
|
||||||
"specifyBody": "json",
|
|
||||||
"jsonBody": "={\n \"symbol\": \"{{ $json.symbol }}\",\n \"direction\": \"{{ $json.direction }}\",\n \"atr\": {{ $json.atr || 0 }},\n \"adx\": {{ $json.adx || 0 }},\n \"rsi\": {{ $json.rsi || 0 }},\n \"volumeRatio\": {{ $json.volumeRatio || 0 }},\n \"pricePosition\": {{ $json.pricePosition || 0 }}\n}",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"name": "Check Risk (with Quality Scoring)",
|
|
||||||
"type": "n8n-nodes-base.httpRequest",
|
|
||||||
"typeVersion": 4.2,
|
|
||||||
"position": [
|
|
||||||
-340,
|
|
||||||
560
|
|
||||||
],
|
|
||||||
"notes": "Updated to send 5 context metrics for signal quality scoring:\n- ATR% (volatility)\n- ADX (trend strength)\n- RSI (momentum)\n- Volume Ratio (participation)\n- Price Position (range position)\n\nMinimum quality score: 60/100"
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"method": "POST",
|
|
||||||
"url": "http://10.0.0.48:3001/api/trading/execute",
|
|
||||||
"authentication": "genericCredentialType",
|
|
||||||
"genericAuthType": "httpHeaderAuth",
|
|
||||||
"sendHeaders": true,
|
|
||||||
"headerParameters": {
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "Authorization",
|
|
||||||
"value": "Bearer 2a344f0149442c857fb56c038c0c7d1b113883b830bec792c76f1e0efa15d6bb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Content-Type",
|
|
||||||
"value": "application/json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"sendBody": true,
|
|
||||||
"specifyBody": "json",
|
|
||||||
"jsonBody": "={\n \"symbol\": \"{{ $('Parse Signal Enhanced').item.json.symbol }}\",\n \"direction\": \"{{ $('Parse Signal Enhanced').item.json.direction }}\",\n \"timeframe\": \"{{ $('Parse Signal Enhanced').item.json.timeframe }}\",\n \"signalStrength\": \"strong\",\n \"atr\": {{ $('Parse Signal Enhanced').item.json.atr || 0 }},\n \"adx\": {{ $('Parse Signal Enhanced').item.json.adx || 0 }},\n \"rsi\": {{ $('Parse Signal Enhanced').item.json.rsi || 0 }},\n \"volumeRatio\": {{ $('Parse Signal Enhanced').item.json.volumeRatio || 0 }},\n \"pricePosition\": {{ $('Parse Signal Enhanced').item.json.pricePosition || 0 }}\n}",
|
|
||||||
"options": {
|
|
||||||
"timeout": 120000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"name": "Execute Trade (with Context Metrics)",
|
|
||||||
"type": "n8n-nodes-base.httpRequest",
|
|
||||||
"typeVersion": 4.2,
|
|
||||||
"position": [
|
|
||||||
60,
|
|
||||||
560
|
|
||||||
],
|
|
||||||
"notes": "Updated to send 5 context metrics for database storage:\n- ATR% at entry\n- ADX at entry\n- RSI at entry\n- Volume Ratio at entry\n- Price Position at entry\n\nThese metrics are stored with each trade for post-trade analysis."
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user