feat: Enhanced timeframe extraction for multi-timeframe data collection
Updated n8n Parse Signal Enhanced to support multiple timeframe formats: - 5m, 15m timeframes (buy 5, buy 15) - Hourly: buy 60, buy 1h → 60 - 4-hour: buy 240, buy 4h → 240 - Daily: buy D, buy 1d → D - Weekly/Monthly: buy W, buy M Fixes: - Default timeframe changed from '15' to '5' (5min is production) - Added indicator version extraction (IND:v8) - Proper conversion of hour/day notation to minutes - Case-insensitive matching for D/W/M Related: Multi-timeframe data collection system (execute endpoint saves non-5min signals to BlockedSignal for cross-timeframe analysis). Now 15min signals from TradingView will be properly parsed and saved. Files: - workflows/trading/parse_signal_enhanced.json (updated regex + conversion) - .github/copilot-instructions.md (documented supported formats)
This commit is contained in:
9
.github/copilot-instructions.md
vendored
9
.github/copilot-instructions.md
vendored
@@ -2733,7 +2733,7 @@ See `POSITION_SCALING_ROADMAP.md` for planned position management optimizations:
|
||||
|
||||
**Blocked Signals Tracking (Nov 11, 2025):** System now automatically saves all blocked signals to database for data-driven optimization. See `BLOCKED_SIGNALS_TRACKING.md` for SQL queries and analysis workflows.
|
||||
|
||||
**Multi-Timeframe Data Collection (Nov 18, 2025):** Execute endpoint now supports parallel data collection across timeframes:
|
||||
**Multi-Timeframe Data Collection (Nov 18-19, 2025):** Execute endpoint now supports parallel data collection across timeframes:
|
||||
- **5min signals:** Execute trades (production)
|
||||
- **15min/1H/4H/Daily signals:** Save to BlockedSignal table with `blockReason='DATA_COLLECTION_ONLY'`
|
||||
- Enables cross-timeframe performance comparison (which timeframe has best win rate?)
|
||||
@@ -2741,6 +2741,13 @@ See `POSITION_SCALING_ROADMAP.md` for planned position management optimizations:
|
||||
- TradingView alerts on multiple timeframes → n8n passes `timeframe` field → bot routes accordingly
|
||||
- After 50+ trades: SQL analysis to determine optimal timeframe for live trading
|
||||
- Implementation: `app/api/trading/execute/route.ts` lines 106-145
|
||||
- **n8n Parse Signal Enhanced (Nov 19):** Supports multiple timeframe formats:
|
||||
- `"buy 5"` → `"5"` (5 minutes)
|
||||
- `"buy 15"` → `"15"` (15 minutes)
|
||||
- `"buy 60"` or `"buy 1h"` → `"60"` (1 hour)
|
||||
- `"buy 240"` or `"buy 4h"` → `"240"` (4 hours)
|
||||
- `"buy D"` or `"buy 1d"` → `"D"` (daily)
|
||||
- Extracts indicator version from `IND:v8` format
|
||||
|
||||
**Data-driven approach:** Each phase requires validation through SQL analysis before implementation. No premature optimization.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "// Get the body - it might be a string or nested in an object\nlet body = $json.body || $json.query?.body || JSON.stringify($json);\n\n// If body is an object, stringify it\nif (typeof body === 'object') {\n body = JSON.stringify(body);\n}\n\n// Parse basic signal (existing logic)\nconst symbolMatch = body.match(/\\b(SOL|BTC|ETH)\\b/i);\nconst symbol = symbolMatch ? symbolMatch[1].toUpperCase() + '-PERP' : 'SOL-PERP';\n\nconst direction = body.match(/\\b(sell|short)\\b/i) ? 'short' : 'long';\n\n// Updated regex to match new format: \"ETH buy 15\" (no .P)\nconst timeframeMatch = body.match(/\\b(buy|sell)\\s+(\\d+|D|W|M)\\b/i);\nconst timeframe = timeframeMatch ? timeframeMatch[2] : '15';\n\n// Parse new context metrics from enhanced format:\n// \"ETH buy 15 | ATR:1.85 | ADX:28.3 | RSI:62.5 | VOL:1.45 | POS:75.3\"\nconst atrMatch = body.match(/ATR:([\\d.]+)/);\nconst atr = atrMatch ? parseFloat(atrMatch[1]) : 0;\n\nconst adxMatch = body.match(/ADX:([\\d.]+)/);\nconst adx = adxMatch ? parseFloat(adxMatch[1]) : 0;\n\nconst rsiMatch = body.match(/RSI:([\\d.]+)/);\nconst rsi = rsiMatch ? parseFloat(rsiMatch[1]) : 0;\n\nconst volumeMatch = body.match(/VOL:([\\d.]+)/);\nconst volumeRatio = volumeMatch ? parseFloat(volumeMatch[1]) : 0;\n\nconst pricePositionMatch = body.match(/POS:([\\d.]+)/);\nconst pricePosition = pricePositionMatch ? parseFloat(pricePositionMatch[1]) : 0;\n\nreturn {\n rawMessage: body,\n symbol,\n direction,\n timeframe,\n // New context fields\n atr,\n adx,\n rsi,\n volumeRatio,\n pricePosition\n};"
|
||||
"jsCode": "// Get the body - it might be a string or nested in an object\nlet body = $json.body || $json.query?.body || JSON.stringify($json);\n\n// If body is an object, stringify it\nif (typeof body === 'object') {\n body = JSON.stringify(body);\n}\n\n// Parse basic signal (existing logic)\nconst symbolMatch = body.match(/\\b(SOL|BTC|ETH)\\b/i);\nconst symbol = symbolMatch ? symbolMatch[1].toUpperCase() + '-PERP' : 'SOL-PERP';\n\nconst direction = body.match(/\\b(sell|short)\\b/i) ? 'short' : 'long';\n\n// Enhanced timeframe extraction supporting multiple formats:\n// - \"buy 5\" → \"5\"\n// - \"buy 15\" → \"15\"\n// - \"buy 60\" or \"buy 1h\" → \"60\"\n// - \"buy 240\" or \"buy 4h\" → \"240\"\n// - \"buy D\" or \"buy 1d\" → \"D\"\n// - \"buy W\" → \"W\"\nconst timeframeMatch = body.match(/\\b(buy|sell)\\s+(\\d+|D|W|M|1h|4h|1d)\\b/i);\nlet timeframe = '5'; // Default to 5min\n\nif (timeframeMatch) {\n const tf = timeframeMatch[2];\n // Convert hour/day notation to minutes\n if (tf === '1h' || tf === '60') {\n timeframe = '60';\n } else if (tf === '4h' || tf === '240') {\n timeframe = '240';\n } else if (tf === '1d' || tf.toUpperCase() === 'D') {\n timeframe = 'D';\n } else if (tf.toUpperCase() === 'W') {\n timeframe = 'W';\n } else if (tf.toUpperCase() === 'M') {\n timeframe = 'M';\n } else {\n timeframe = tf;\n }\n}\n\n// Parse new context metrics from enhanced format:\n// \"SOLT.P buy 15 | ATR:0.65 | ADX:14.3 | RSI:51.3 | VOL:0.87 | POS:59.3 | IND:v8\"\nconst atrMatch = body.match(/ATR:([\\d.]+)/);\nconst atr = atrMatch ? parseFloat(atrMatch[1]) : 0;\n\nconst adxMatch = body.match(/ADX:([\\d.]+)/);\nconst adx = adxMatch ? parseFloat(adxMatch[1]) : 0;\n\nconst rsiMatch = body.match(/RSI:([\\d.]+)/);\nconst rsi = rsiMatch ? parseFloat(rsiMatch[1]) : 0;\n\nconst volumeMatch = body.match(/VOL:([\\d.]+)/);\nconst volumeRatio = volumeMatch ? parseFloat(volumeMatch[1]) : 0;\n\nconst pricePositionMatch = body.match(/POS:([\\d.]+)/);\nconst pricePosition = pricePositionMatch ? parseFloat(pricePositionMatch[1]) : 0;\n\n// Parse indicator version (optional, backward compatible)\nconst indicatorVersionMatch = body.match(/IND:(v\\d+)/i);\nconst indicatorVersion = indicatorVersionMatch ? indicatorVersionMatch[1] : 'v5';\n\nreturn {\n rawMessage: body,\n symbol,\n direction,\n timeframe,\n // Context fields\n atr,\n adx,\n rsi,\n volumeRatio,\n pricePosition,\n // Version tracking (defaults to v5 for backward compatibility)\n indicatorVersion\n};"
|
||||
},
|
||||
"id": "parse-signal-enhanced",
|
||||
"name": "Parse Signal Enhanced",
|
||||
|
||||
Reference in New Issue
Block a user