fix: restore 1min market data payload

This commit is contained in:
mindesbunister
2025-12-11 14:44:08 +01:00
parent 0ee49703ae
commit 7ff5c5b3a4
2 changed files with 25 additions and 13 deletions

View File

@@ -933,6 +933,7 @@ When you have high-resolution data (1 minute), use it immediately. Arbitrary del
- Safety bounds: MIN/MAX caps prevent extremes - Safety bounds: MIN/MAX caps prevent extremes
- Falls back to fixed % if ATR unavailable - Falls back to fixed % if ATR unavailable
- **Runner:** 40% remaining after TP1 (configurable via `TAKE_PROFIT_1_SIZE_PERCENT=60`) - **Runner:** 40% remaining after TP1 (configurable via `TAKE_PROFIT_1_SIZE_PERCENT=60`)
- **TP2 trigger-only:** TP2 is now a software trigger only; no on-chain TP2 order is placed. On-chain protection = TP1 + SL, and trailing activates when price reaches TP2.
- **Runner SL after TP1:** ADX-based adaptive positioning (Nov 19, 2025): - **Runner SL after TP1:** ADX-based adaptive positioning (Nov 19, 2025):
- ADX < 20: SL at 0% (breakeven) - Weak trend, preserve TP1 profit - ADX < 20: SL at 0% (breakeven) - Weak trend, preserve TP1 profit
- ADX 20-25: SL at -0.3% - Moderate trend, some retracement room - ADX 20-25: SL at -0.3% - Moderate trend, some retracement room
@@ -1031,6 +1032,7 @@ Frequency penalties (overtrading / flip-flop / alternating) now ignore 1-minute
- **Implementation:** `app/api/trading/execute/route.ts` line ~237-242 (commit 0982578, Dec 4, 2025) - **Implementation:** `app/api/trading/execute/route.ts` line ~237-242 (commit 0982578, Dec 4, 2025)
- **Behavior:** Manual trades execute regardless of ADX/ATR/RSI/quality score - **Behavior:** Manual trades execute regardless of ADX/ATR/RSI/quality score
- **--force flag:** No longer needed (all manual trades bypass by default) - **--force flag:** No longer needed (all manual trades bypass by default)
- **Fresh data wait:** Telegram manual trades wait up to 90 seconds for the next 1-minute market data update. First datapoint with age ≤15s is accepted immediately to avoid timeouts; if no new data arrives within 90s, the bot falls back to preset manual metrics (ATR/ADX/RSI defaults).
**Re-Entry Analytics System (OPTIONAL VALIDATION):** Manual trades CAN be validated before execution using fresh TradingView data: **Re-Entry Analytics System (OPTIONAL VALIDATION):** Manual trades CAN be validated before execution using fresh TradingView data:
- Market data cached from TradingView signals (5min expiry) - Market data cached from TradingView signals (5min expiry)
@@ -1076,7 +1078,7 @@ tests/
└── pure-runner-profit-widening.test.ts # Profit-based trailing widening after TP2 └── pure-runner-profit-widening.test.ts # Profit-based trailing widening after TP2
``` ```
**Total:** 13 test files, 162 tests (full suite green as of Dec 10, 2025 before rebuild) **Total:** 13 test files, 164 tests (full suite green as of Dec 11, 2025; if Jest lingers on exit, rerun with `npm test -- --detectOpenHandles` to spot open handles)
**Test Configuration:** **Test Configuration:**
- **Framework:** Jest + ts-jest - **Framework:** Jest + ts-jest
@@ -1528,6 +1530,8 @@ Before marking feature complete:
- [ ] Documentation updated (including Common Pitfalls if applicable) - [ ] Documentation updated (including Common Pitfalls if applicable)
- [ ] User notified of what to verify during first real trade - [ ] User notified of what to verify during first real trade
**Latest verification (Dec 11, 2025):** trading-bot-v4 container started 2025-12-11T09:21:55Z; latest commit 0ee4970 (2025-12-10 15:39 CET) → container is newer, code deployed.
### When to Escalate to User ### When to Escalate to User
**Don't say "it's working" if:** **Don't say "it's working" if:**
@@ -1914,6 +1918,7 @@ WHERE "blockReason" = 'QUALITY_SCORE_TOO_LOW'
- Commit: dbada47 "feat: Calculate quality scores for all timeframes (not just 5min)" - Commit: dbada47 "feat: Calculate quality scores for all timeframes (not just 5min)"
- ✅ TradingView alerts configured for 15min and 1H - ✅ TradingView alerts configured for 15min and 1H
- ✅ Background tracker runs every 5 minutes autonomously - ✅ Background tracker runs every 5 minutes autonomously
- ✅ **Dec 11, 2025 PineScript fix:** `1min_market_data_feed.pinescript` now uses @version=6, destructures `ta.dmi()` to `adxVal`, and builds the JSON payload with `alert()` on `barstate.isconfirmed` (keeps `alertcondition` static). This prevents the “series string” compile error and restores full ATR/ADX/RSI/volumeRatio/pricePosition payloads to `/api/trading/market-data`.
- 📊 **Data collection:** Multi-timeframe (50+ per timeframe) + quality-blocked (20-30 signals) - 📊 **Data collection:** Multi-timeframe (50+ per timeframe) + quality-blocked (20-30 signals)
- 🎯 **Dual goals:** - 🎯 **Dual goals:**
1. Determine which timeframe has best win rate (now with quality filtering capability) 1. Determine which timeframe has best win rate (now with quality filtering capability)
@@ -2109,6 +2114,7 @@ scoreSignalQuality({
- Starts automatically after Drift state verifier - Starts automatically after Drift state verifier
- Runs alongside: data cleanup, blocked signals, stop hunt, smart validation - Runs alongside: data cleanup, blocked signals, stop hunt, smart validation
- No manual intervention needed - No manual intervention needed
- Auto-recovery: If DB shows open trades but Position Manager monitoring is off, the health monitor now forces a Position Manager reinitialize from the database before raising a critical alert (avoids container restarts when possible).
**Test Suite:** **Test Suite:**
- File: `tests/integration/position-manager/monitoring-verification.test.ts` (201 lines) - File: `tests/integration/position-manager/monitoring-verification.test.ts` (201 lines)
@@ -2551,6 +2557,8 @@ if (!enabled) {
} }
``` ```
**Debug sizing (optional):** Set `ENABLE_SIZE_TRACE_LOGS=true` in `.env` to emit per-trade sizing traces (symbol, percentage vs fixed, safety buffer usage, free collateral, final size, leverage, notional). Default is false to avoid noisy logs.
**Symbol normalization:** TradingView sends "SOLUSDT" → must convert to "SOL-PERP" for Drift **Symbol normalization:** TradingView sends "SOLUSDT" → must convert to "SOL-PERP" for Drift
```typescript ```typescript
const driftSymbol = normalizeTradingViewSymbol(body.symbol) const driftSymbol = normalizeTradingViewSymbol(body.symbol)
@@ -2588,7 +2596,7 @@ const { size, leverage } = getActualPositionSizeForSymbol(driftSymbol, config, q
- `/api/trading/check-risk` - Pre-execution validation (duplicate check, quality score ≥91, **per-symbol cooldown**, rate limits, **symbol enabled check**, **saves blocked signals automatically**) - `/api/trading/check-risk` - Pre-execution validation (duplicate check, quality score ≥91, **per-symbol cooldown**, rate limits, **symbol enabled check**, **saves blocked signals automatically**)
- `/api/trading/test` - Test trades from settings UI (no auth required, **respects symbol enable/disable**) - `/api/trading/test` - Test trades from settings UI (no auth required, **respects symbol enable/disable**)
- `/api/trading/close` - Manual position closing (requires symbol normalization) - `/api/trading/close` - Manual position closing (requires symbol normalization)
- `/api/trading/sync-positions` - **Force Position Manager sync with Drift** (POST, requires auth) - restores tracking for orphaned positions - `/api/trading/sync-positions` - **Force Position Manager sync with Drift** (POST, requires auth) - restores tracking for orphaned positions; now reattaches to the existing open Trade in the database when present and creates a placeholder Trade before adding to Position Manager when missing (prevents Prisma P2025 errors from synthetic IDs)
- `/api/trading/cancel-orders` - **Manual order cleanup** (for stuck/ghost orders after rate limit failures) - `/api/trading/cancel-orders` - **Manual order cleanup** (for stuck/ghost orders after rate limit failures)
- `/api/trading/positions` - Query open positions from Drift - `/api/trading/positions` - Query open positions from Drift
- `/api/trading/market-data` - Webhook for TradingView market data updates (GET for debug, POST for data) - `/api/trading/market-data` - Webhook for TradingView market data updates (GET for debug, POST for data)

View File

@@ -1,4 +1,4 @@
// @version=5 // @version=6
indicator("1-Minute Market Data Feed", overlay=false) indicator("1-Minute Market Data Feed", overlay=false)
// ============================================================================ // ============================================================================
@@ -9,13 +9,13 @@ indicator("1-Minute Market Data Feed", overlay=false)
// Calculate indicators // Calculate indicators
atr = ta.atr(14) atr = ta.atr(14)
adx = ta.dmi(14, 14) [diPlus, diMinus, adxVal] = ta.dmi(14, 14)
rsi = ta.rsi(close, 14) rsi = ta.rsi(close, 14)
volumeRatio = volume / ta.sma(volume, 20) volumeRatio = volume / ta.sma(volume, 20)
pricePosition = (close - ta.lowest(low, 100)) / (ta.highest(high, 100) - ta.lowest(low, 100)) * 100 pricePosition = (close - ta.lowest(low, 100)) / (ta.highest(high, 100) - ta.lowest(low, 100)) * 100
// Plot for visual confirmation (optional) // Plot for visual confirmation (optional)
plot(adx, title="ADX", color=color.blue, linewidth=2) plot(adxVal, title="ADX", color=color.blue, linewidth=2)
hline(20, "ADX 20", color=color.orange, linestyle=hline.style_dotted) hline(20, "ADX 20", color=color.orange, linestyle=hline.style_dotted)
hline(25, "ADX 25", color=color.red, linestyle=hline.style_dotted) hline(25, "ADX 25", color=color.red, linestyle=hline.style_dotted)
@@ -26,8 +26,8 @@ if barstate.islast
table.cell(infoTable, 1, 0, "Value", bgcolor=color.new(color.gray, 70), text_color=color.white) table.cell(infoTable, 1, 0, "Value", bgcolor=color.new(color.gray, 70), text_color=color.white)
table.cell(infoTable, 0, 1, "ADX", text_color=color.white) table.cell(infoTable, 0, 1, "ADX", text_color=color.white)
table.cell(infoTable, 1, 1, str.tostring(adx, "#.##"), table.cell(infoTable, 1, 1, str.tostring(adxVal, "#.##"),
bgcolor=adx > 25 ? color.new(color.green, 80) : adx > 20 ? color.new(color.orange, 80) : color.new(color.red, 80), bgcolor=adxVal > 25 ? color.new(color.green, 80) : adxVal > 20 ? color.new(color.orange, 80) : color.new(color.red, 80),
text_color=color.white) text_color=color.white)
table.cell(infoTable, 0, 2, "ATR", text_color=color.white) table.cell(infoTable, 0, 2, "ATR", text_color=color.white)
@@ -46,15 +46,16 @@ if barstate.islast
table.cell(infoTable, 0, 5, "Price Pos", text_color=color.white) table.cell(infoTable, 0, 5, "Price Pos", text_color=color.white)
table.cell(infoTable, 1, 5, str.tostring(pricePosition, "#.#") + "%", text_color=color.white) table.cell(infoTable, 1, 5, str.tostring(pricePosition, "#.#") + "%", text_color=color.white)
// Alert condition: Fire every bar close (1 minute) // Alert condition to allow creating the alert in TradingView UI
// This sends data regardless of market conditions alertcondition(true, title="1min Market Data")
alertcondition(true, title="1min Market Data", message=
'{' + // Build dynamic payload and emit an alert once per bar close
alertPayload = '{' +
'"action": "market_data_1min",' + '"action": "market_data_1min",' +
'"symbol": "{{ticker}}",' + '"symbol": "{{ticker}}",' +
'"timeframe": "1",' + '"timeframe": "1",' +
'"atr": ' + str.tostring(atr, "#.########") + ',' + '"atr": ' + str.tostring(atr, "#.########") + ',' +
'"adx": ' + str.tostring(adx, "#.########") + ',' + '"adx": ' + str.tostring(adxVal, "#.########") + ',' +
'"rsi": ' + str.tostring(rsi, "#.########") + ',' + '"rsi": ' + str.tostring(rsi, "#.########") + ',' +
'"volumeRatio": ' + str.tostring(volumeRatio, "#.########") + ',' + '"volumeRatio": ' + str.tostring(volumeRatio, "#.########") + ',' +
'"pricePosition": ' + str.tostring(pricePosition, "#.########") + ',' + '"pricePosition": ' + str.tostring(pricePosition, "#.########") + ',' +
@@ -62,7 +63,10 @@ alertcondition(true, title="1min Market Data", message=
'"timestamp": "{{timenow}}",' + '"timestamp": "{{timenow}}",' +
'"exchange": "{{exchange}}",' + '"exchange": "{{exchange}}",' +
'"indicatorVersion": "v9"' + '"indicatorVersion": "v9"' +
'}') '}'
if barstate.isconfirmed
alert(alertPayload, alert.freq_once_per_bar_close)
// ============================================================================ // ============================================================================
// ALERT SETUP INSTRUCTIONS: // ALERT SETUP INSTRUCTIONS: