feat: Indicator score bypass - v11.2 sends SCORE:100 to bypass bot quality scoring

Changes:
- moneyline_v11_2_indicator.pinescript: Alert format now includes SCORE:100
- parse_signal_enhanced.json: Added indicatorScore parsing (SCORE:X regex)
- execute/route.ts: Added hasIndicatorScore bypass (score >= 90 bypasses quality check)
- Money_Machine.json: Both Execute Trade nodes now pass indicatorScore to API

Rationale: v11.2 indicator filters already optimized (2.544 PF, +51.80% return).
Bot-side quality scoring was blocking proven profitable signals (e.g., quality 75).
Now indicator passes SCORE:100, bot respects it and executes immediately.

This completes the signal chain:
Indicator (SCORE:100) → n8n parser (indicatorScore) → workflow → bot endpoint (bypass)
This commit is contained in:
mindesbunister
2025-12-26 11:40:12 +01:00
parent 91f8abed19
commit ba1fe4433e
19 changed files with 22913 additions and 59 deletions

View File

@@ -16,6 +16,7 @@
import { getInitializedPositionManager } from '../trading/position-manager'
import { getOpenTrades, getPrismaClient } from '../database/trades'
import { getDriftService } from '../drift/client'
import { getMergedConfig } from '../../config/trading'
export interface HealthCheckResult {
isHealthy: boolean
@@ -89,6 +90,103 @@ async function autoSyncUntrackedPositions(): Promise<boolean> {
}
}
function calculatePrice(entry: number, percent: number, direction: 'long' | 'short'): number {
return direction === 'long'
? entry * (1 + percent / 100)
: entry * (1 - percent / 100)
}
async function ensureExitOrdersForTrade(
trade: any,
config: ReturnType<typeof getMergedConfig>
): Promise<{ placed: boolean; message?: string; error?: string }> {
try {
const positionSizeUSD = trade.positionSizeUSD || trade.positionSize || 0
if (!positionSizeUSD || !trade.entryPrice) {
return { placed: false, error: 'Missing position size or entry price' }
}
const direction: 'long' | 'short' = trade.direction === 'short' ? 'short' : 'long'
const tp1Price =
trade.takeProfit1Price || calculatePrice(trade.entryPrice, config.takeProfit1Percent, direction)
const tp2Price =
trade.takeProfit2Price || calculatePrice(trade.entryPrice, config.takeProfit2Percent, direction)
const stopLossPrice =
trade.stopLossPrice || calculatePrice(trade.entryPrice, config.stopLossPercent, direction)
const tp1SizePercent = trade.tp1SizePercent ?? config.takeProfit1SizePercent
const tp2SizePercentRaw = trade.tp2SizePercent ?? config.takeProfit2SizePercent ?? 0
const tp2SizePercent = config.useTp2AsTriggerOnly && tp2SizePercentRaw <= 0 ? 0 : tp2SizePercentRaw
const softStopPrice = config.useDualStops
? calculatePrice(trade.entryPrice, config.softStopPercent, direction)
: undefined
const hardStopPrice = config.useDualStops
? calculatePrice(trade.entryPrice, config.hardStopPercent, direction)
: undefined
const { placeExitOrders } = await import('../drift/orders')
const placeResult = await placeExitOrders({
symbol: trade.symbol,
positionSizeUSD,
entryPrice: trade.entryPrice,
tp1Price,
tp2Price,
stopLossPrice,
tp1SizePercent,
tp2SizePercent,
direction,
useDualStops: config.useDualStops,
softStopPrice,
softStopBuffer: config.softStopBuffer,
hardStopPrice,
})
if (!placeResult.success) {
return { placed: false, error: placeResult.error || 'Unknown error placing exit orders' }
}
const signatures = placeResult.signatures || []
const normalizedTp2Percent = tp2SizePercent === undefined ? 100 : Math.max(0, tp2SizePercent)
const tp1USD = (positionSizeUSD * tp1SizePercent) / 100
const remainingAfterTP1 = positionSizeUSD - tp1USD
const tp2USD = (remainingAfterTP1 * normalizedTp2Percent) / 100
let idx = 0
const updateData: any = {}
if (tp1USD > 0 && idx < signatures.length) {
updateData.tp1OrderTx = signatures[idx++]
}
if (normalizedTp2Percent > 0 && idx < signatures.length) {
updateData.tp2OrderTx = signatures[idx++]
}
if (config.useDualStops && softStopPrice && hardStopPrice) {
if (idx < signatures.length) {
updateData.softStopOrderTx = signatures[idx++]
}
if (idx < signatures.length) {
updateData.hardStopOrderTx = signatures[idx++]
}
} else if (idx < signatures.length) {
updateData.slOrderTx = signatures[idx++]
}
const prisma = getPrismaClient()
await prisma.trade.update({ where: { id: trade.id }, data: updateData })
return { placed: true, message: 'Protective exits placed' }
} catch (error) {
return {
placed: false,
error: error instanceof Error ? error.message : String(error)
}
}
}
/**
* Check Position Manager health
*
@@ -101,6 +199,7 @@ async function autoSyncUntrackedPositions(): Promise<boolean> {
export async function checkPositionManagerHealth(): Promise<HealthCheckResult> {
const issues: string[] = []
const warnings: string[] = []
const config = getMergedConfig()
try {
// Get database open trades
@@ -169,26 +268,29 @@ export async function checkPositionManagerHealth(): Promise<HealthCheckResult> {
}
// Check for unprotected positions
// NOTE: Synced/placeholder positions (signalSource='autosync') have NULL signatures in DB
// but orders exist on Drift. Position Manager monitoring provides backup protection.
let unprotectedPositions = 0
for (const trade of dbTrades) {
const hasDbSignatures = !!(trade.slOrderTx || trade.softStopOrderTx || trade.hardStopOrderTx)
const isSyncedPosition = trade.signalSource === 'autosync' || trade.timeframe === 'sync'
if (!hasDbSignatures && !isSyncedPosition) {
// This is NOT a synced position but has no SL orders - CRITICAL
if (!hasDbSignatures) {
unprotectedPositions++
issues.push(`❌ CRITICAL: Position ${trade.symbol} (${trade.id}) has NO STOP LOSS ORDERS!`)
issues.push(` Entry: $${trade.entryPrice}, Size: $${trade.positionSizeUSD}`)
issues.push(` This is the silent SL placement failure bug`)
issues.push(` Attempting automatic protective order placement...`)
const remediation = await ensureExitOrdersForTrade(trade, config)
if (remediation.placed) {
warnings.push(`✅ Auto-placed protective exit orders for ${trade.symbol} (${trade.id})`)
} else if (remediation.error) {
issues.push(` ❌ Failed to auto-place exits: ${remediation.error}`)
}
}
if (!trade.tp1OrderTx && !isSyncedPosition) {
if (!trade.tp1OrderTx) {
warnings.push(`⚠️ Position ${trade.symbol} missing TP1 order (not synced)`)
}
if (!trade.tp2OrderTx && !isSyncedPosition) {
if (!trade.tp2OrderTx) {
warnings.push(`⚠️ Position ${trade.symbol} missing TP2 order (not synced)`)
}
}

View File

@@ -199,6 +199,87 @@ export async function syncSinglePosition(driftPos: any, positionManager: any): P
console.log(`🔍 Querying Drift for existing orders on ${driftPos.symbol}...`)
const existingOrders = await discoverExistingOrders(driftPos.symbol, driftPos.marketIndex)
// If no stop orders exist, immediately place protective exit orders so the position is never unprotected
const hasStopOrders = !!(
existingOrders.slOrderTx ||
existingOrders.softStopOrderTx ||
existingOrders.hardStopOrderTx
)
const tp2SizePercentRaw = config.takeProfit2SizePercent ?? 0
const tp2SizePercent =
config.useTp2AsTriggerOnly && tp2SizePercentRaw <= 0 ? 0 : tp2SizePercentRaw
const orderRefs: any = { ...existingOrders }
if (!hasStopOrders) {
console.warn(`🛡️ No SL orders found for ${driftPos.symbol} - placing protective exits now`)
try {
const { placeExitOrders } = await import('../drift/orders')
const softStopPrice = config.useDualStops
? calculatePrice(entryPrice, config.softStopPercent, direction)
: undefined
const hardStopPrice = config.useDualStops
? calculatePrice(entryPrice, config.hardStopPercent, direction)
: undefined
const placeResult = await placeExitOrders({
symbol: driftPos.symbol,
positionSizeUSD,
entryPrice,
tp1Price,
tp2Price,
stopLossPrice,
tp1SizePercent: config.takeProfit1SizePercent,
tp2SizePercent,
direction,
useDualStops: config.useDualStops,
softStopPrice,
softStopBuffer: config.softStopBuffer,
hardStopPrice,
})
if (placeResult.success && placeResult.signatures?.length) {
const signatures = placeResult.signatures
const normalizedTp2Percent = tp2SizePercent === undefined
? 100
: Math.max(0, tp2SizePercent)
const tp1USD = (positionSizeUSD * config.takeProfit1SizePercent) / 100
const remainingAfterTP1 = positionSizeUSD - tp1USD
const tp2USD = (remainingAfterTP1 * normalizedTp2Percent) / 100
let idx = 0
if (tp1USD > 0 && idx < signatures.length) {
orderRefs.tp1OrderTx = signatures[idx++]
}
if (normalizedTp2Percent > 0 && idx < signatures.length) {
orderRefs.tp2OrderTx = signatures[idx++]
}
if (config.useDualStops && softStopPrice && hardStopPrice) {
if (idx < signatures.length) {
orderRefs.softStopOrderTx = signatures[idx++]
}
if (idx < signatures.length) {
orderRefs.hardStopOrderTx = signatures[idx++]
}
} else if (idx < signatures.length) {
orderRefs.slOrderTx = signatures[idx++]
}
console.log(`✅ Protective exit orders placed for ${driftPos.symbol}`)
} else {
console.error(
`❌ Failed to place protective exit orders for ${driftPos.symbol}: ${placeResult.error || 'unknown error'}`
)
}
} catch (placeError) {
console.error(`❌ Error placing protective exit orders for ${driftPos.symbol}:`, placeError)
}
}
const placeholderTrade = await prisma.trade.create({
data: {
positionId: syntheticPositionId,
@@ -220,16 +301,16 @@ export async function syncSinglePosition(driftPos: any, positionManager: any): P
status: 'open',
signalSource: 'autosync',
timeframe: 'sync',
// CRITICAL FIX (Dec 12, 2025): Record discovered order signatures
tp1OrderTx: existingOrders.tp1OrderTx || null,
tp2OrderTx: existingOrders.tp2OrderTx || null,
slOrderTx: existingOrders.slOrderTx || null,
softStopOrderTx: existingOrders.softStopOrderTx || null,
hardStopOrderTx: existingOrders.hardStopOrderTx || null,
// CRITICAL FIX (Dec 12, 2025): Record discovered (or newly placed) order signatures
tp1OrderTx: orderRefs.tp1OrderTx || null,
tp2OrderTx: orderRefs.tp2OrderTx || null,
slOrderTx: orderRefs.slOrderTx || null,
softStopOrderTx: orderRefs.softStopOrderTx || null,
hardStopOrderTx: orderRefs.hardStopOrderTx || null,
configSnapshot: {
source: 'health-monitor-autosync',
syncedAt: now.toISOString(),
discoveredOrders: existingOrders, // Store for debugging
discoveredOrders: orderRefs, // Store for debugging
positionManagerState: {
currentSize: positionSizeUSD,
tp1Hit: false,