- Add Position Manager state persistence to survive restarts - Auto-restore open trades from database on startup - Save state after TP1, SL adjustments, profit locks - Persist to configSnapshot JSON field - Add automatic order cancellation - Cancel all TP/SL orders when position fully closed - New cancelAllOrders() function in drift/orders.ts - Prevents orphaned orders after manual closes - Improve stop loss management - Move SL to +0.35% after TP1 (was +0.15%) - Gives more breathing room for retracements - Still locks in half of TP1 profit - Add database sync when Position Manager closes trades - Auto-update Trade record with exit data - Save P&L, exit reason, hold time - Fix analytics showing stale data - Add trade state management functions - updateTradeState() for Position Manager persistence - getOpenTrades() for startup restoration - getInitializedPositionManager() for async init - Create n8n database analytics workflows - Daily report workflow (automated at midnight) - Pattern analysis (hourly/daily performance) - Stop loss effectiveness analysis - Database analytics query workflow - Complete setup guide (N8N_DATABASE_SETUP.md)
172 lines
5.8 KiB
JSON
172 lines
5.8 KiB
JSON
{
|
|
"name": "Daily Trading Report",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"rule": {
|
|
"interval": [
|
|
{
|
|
"triggerAtHour": 0,
|
|
"triggerAtMinute": 5
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
|
|
"name": "Every Day at Midnight",
|
|
"type": "n8n-nodes-base.scheduleTrigger",
|
|
"typeVersion": 1.2,
|
|
"position": [240, 300]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "-- Get yesterday's trading activity\nSELECT \n COUNT(*) as total_trades,\n COUNT(CASE WHEN \"realizedPnL\" > 0 THEN 1 END) as winning_trades,\n COUNT(CASE WHEN \"realizedPnL\" < 0 THEN 1 END) as losing_trades,\n SUM(\"realizedPnL\") as total_pnl,\n AVG(\"realizedPnL\") as avg_pnl,\n MAX(\"realizedPnL\") as best_trade,\n MIN(\"realizedPnL\") as worst_trade,\n AVG(\"holdTimeSeconds\") / 60 as avg_hold_time_minutes\nFROM \"Trade\"\nWHERE status = 'closed'\n AND \"isTestTrade\" = false\n AND \"exitTime\" >= CURRENT_DATE - INTERVAL '1 day'\n AND \"exitTime\" < CURRENT_DATE;"
|
|
},
|
|
"id": "2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e",
|
|
"name": "Query Yesterday Stats",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.4,
|
|
"position": [460, 300],
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "1",
|
|
"name": "Trading Bot Database"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "-- Get breakdown by symbol\nSELECT \n symbol,\n COUNT(*) as trades,\n SUM(\"realizedPnL\") as pnl\nFROM \"Trade\"\nWHERE status = 'closed'\n AND \"isTestTrade\" = false\n AND \"exitTime\" >= CURRENT_DATE - INTERVAL '1 day'\n AND \"exitTime\" < CURRENT_DATE\nGROUP BY symbol\nORDER BY pnl DESC;"
|
|
},
|
|
"id": "3c4d5e6f-7a8b-9c0d-1e2f-3a4b5c6d7e8f",
|
|
"name": "Query Symbol Breakdown",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.4,
|
|
"position": [460, 500],
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "1",
|
|
"name": "Trading Bot Database"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "insert",
|
|
"schema": {
|
|
"__rl": true,
|
|
"value": "public",
|
|
"mode": "list",
|
|
"cachedResultName": "public"
|
|
},
|
|
"table": {
|
|
"__rl": true,
|
|
"value": "DailyStats",
|
|
"mode": "list",
|
|
"cachedResultName": "DailyStats"
|
|
},
|
|
"columns": {
|
|
"mappingMode": "defineBelow",
|
|
"value": {
|
|
"date": "={{ $json.date }}",
|
|
"tradesCount": "={{ $json.total_trades }}",
|
|
"winningTrades": "={{ $json.winning_trades }}",
|
|
"losingTrades": "={{ $json.losing_trades }}",
|
|
"totalPnL": "={{ $json.total_pnl }}",
|
|
"totalPnLPercent": "0",
|
|
"winRate": "={{ $json.win_rate }}",
|
|
"avgWin": "={{ $json.avg_win }}",
|
|
"avgLoss": "={{ $json.avg_loss }}",
|
|
"profitFactor": "={{ $json.profit_factor }}",
|
|
"maxDrawdown": "0",
|
|
"sharpeRatio": "0"
|
|
}
|
|
}
|
|
},
|
|
"id": "4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f9a",
|
|
"name": "Save Daily Stats",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.4,
|
|
"position": [900, 300],
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "1",
|
|
"name": "Trading Bot Database"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const stats = $('Query Yesterday Stats').first().json;\nconst symbols = $('Query Symbol Breakdown').all();\n\nconst winRate = stats.total_trades > 0 ? (stats.winning_trades / stats.total_trades) * 100 : 0;\nconst avgWin = stats.winning_trades > 0 ? stats.total_pnl / stats.winning_trades : 0;\nconst avgLoss = stats.losing_trades > 0 ? Math.abs(stats.total_pnl / stats.losing_trades) : 0;\nconst profitFactor = avgLoss !== 0 ? avgWin / avgLoss : 0;\n\nconst yesterday = new Date();\nyesterday.setDate(yesterday.getDate() - 1);\nyesterday.setHours(0, 0, 0, 0);\n\nreturn [{\n json: {\n date: yesterday.toISOString(),\n total_trades: parseInt(stats.total_trades) || 0,\n winning_trades: parseInt(stats.winning_trades) || 0,\n losing_trades: parseInt(stats.losing_trades) || 0,\n total_pnl: parseFloat(stats.total_pnl) || 0,\n win_rate: winRate,\n avg_win: avgWin,\n avg_loss: avgLoss,\n profit_factor: profitFactor,\n symbols: symbols.map(s => s.json)\n }\n}];"
|
|
},
|
|
"id": "5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b",
|
|
"name": "Process Data",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [680, 300]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Every Day at Midnight": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Query Yesterday Stats",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Query Symbol Breakdown",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Query Yesterday Stats": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Process Data",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Query Symbol Breakdown": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Process Data",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Process Data": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Save Daily Stats",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"pinData": {},
|
|
"settings": {
|
|
"executionOrder": "v1"
|
|
},
|
|
"staticData": null,
|
|
"tags": [],
|
|
"triggerCount": 0,
|
|
"updatedAt": "2025-10-27T00:00:00.000Z",
|
|
"versionId": "1"
|
|
}
|