fix: restore 1min market data payload
This commit is contained in:
12
.github/copilot-instructions.md
vendored
12
.github/copilot-instructions.md
vendored
@@ -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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user