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)`)
}
}