Files
trading_bot_v4/workflows/analytics/n8n-daily-report.json
mindesbunister 14d5de2c64 chore: Organize workspace structure - move docs, workflows, scripts to subdirectories
Organization:
- Created docs/ with setup/, guides/, history/ subdirectories
- Created workflows/ with trading/, analytics/, telegram/, archive/ subdirectories
- Created scripts/ with docker/, setup/, testing/ subdirectories
- Created tests/ for TypeScript test files
- Created archive/ for unused reference files

Moved files:
- 17 documentation files → docs/
- 16 workflow JSON files → workflows/
- 10 shell scripts → scripts/
- 4 test files → tests/
- 5 unused files → archive/

Updated:
- README.md with new file structure and documentation paths

Deleted:
- data/ (empty directory)
- screenshots/ (empty directory)

Critical files remain in root:
- telegram_command_bot.py (active bot - used by Dockerfile)
- watch-restart.sh (systemd service dependency)
- All Dockerfiles and docker-compose files
- All environment files

Validation:
 Containers running (trading-bot-v4, telegram-trade-bot, postgres)
 API responding (positions endpoint tested)
 Telegram bot functional (/status command tested)
 All critical files present in root

No code changes - purely organizational.
System continues running without interruption.

Recovery: git revert HEAD or git reset --hard cleanup-before
2025-10-27 12:59:25 +01:00

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"
}