From 61a96d490c008a71f484ab5c83e0a101c064390c Mon Sep 17 00:00:00 2001 From: Garfield Date: Mon, 30 Mar 2026 15:38:05 -0400 Subject: [PATCH] Add alternative validation methods: SSH, HTTP, and Python script with Telegram --- AGENTS.md | 5 ++ n8n-workflow-http.json | 89 +++++++++++++++++++ n8n-workflow-ssh.json | 98 ++++++++++++++++++++ scripts/setup-cron.sh | 27 ++++++ scripts/validate-and-notify.py | 157 +++++++++++++++++++++++++++++++++ 5 files changed, 376 insertions(+) create mode 100644 n8n-workflow-http.json create mode 100644 n8n-workflow-ssh.json create mode 100755 scripts/setup-cron.sh create mode 100755 scripts/validate-and-notify.py diff --git a/AGENTS.md b/AGENTS.md index b0f02e7..c51e59e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -131,6 +131,10 @@ tail -100 ~/mt5-docker/config/.wine/drive_c/Program\ Files/MetaTrader\ 5/MQL5/Lo - **Largest Win:** $8,091 - **Largest Loss:** -$805 +## DevOps / Infrastructure + +Server infrastructure details saved in: `/home/garfield/devops/INFRASTRUCTURE.md` + ## Conversation History Before working on this codebase, read: @@ -138,6 +142,7 @@ Before working on this codebase, read: conversation-history/2026-03-21-mql-trading-bots.md conversation-history/2026-03-29-session-save.md conversation-history/2026-03-30-weekend-gap-short-signal-fix.md +conversation-history/2026-03-30-dns-whitelist.md ``` ## Notes for AI Agents diff --git a/n8n-workflow-http.json b/n8n-workflow-http.json new file mode 100644 index 0000000..ea07fb6 --- /dev/null +++ b/n8n-workflow-http.json @@ -0,0 +1,89 @@ +{ + "name": "MQL Settings Monitor - HTTP Version", + "nodes": [ + { + "parameters": { + "rule": { + "interval": [ + { + "field": "hours", + "hoursInterval": 6 + } + ] + } + }, + "name": "Every 6 Hours", + "type": "n8n-nodes-base.scheduleTrigger", + "typeVersion": 1, + "position": [250, 300] + }, + { + "parameters": { + "requestMethod": "POST", + "url": "http://localhost:8080/validate", + "options": { + "timeout": 30000 + } + }, + "name": "Call Validation API", + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 1, + "position": [450, 300] + }, + { + "parameters": { + "conditions": { + "string": [ + { + "value1": "={{ $json.status }}", + "operation": "equal", + "value2": "error" + } + ] + } + }, + "name": "Has Issues?", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [650, 300] + }, + { + "parameters": { + "chatId": "={{ $env.TELEGRAM_CHAT_ID }}", + "text": "=🚨 MQL Settings Alert:\n\n{{ $json.message }}\n\nIssues: {{ $json.issues }}\n\nā° {{ new Date().toISOString() }}" + }, + "name": "Send Telegram", + "type": "n8n-nodes-base.telegram", + "typeVersion": 1, + "position": [850, 200] + }, + { + "parameters": {}, + "name": "No Action", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [850, 400] + } + ], + "connections": { + "Every 6 Hours": { + "main": [ + [{ "node": "Call Validation API", "type": "main", "index": 0 }] + ] + }, + "Call Validation API": { + "main": [ + [{ "node": "Has Issues?", "type": "main", "index": 0 }] + ] + }, + "Has Issues?": { + "main": [ + [{ "node": "Send Telegram", "type": "main", "index": 0 }], + [{ "node": "No Action", "type": "main", "index": 0 }] + ] + } + }, + "settings": {}, + "staticData": null, + "tags": [] +} diff --git a/n8n-workflow-ssh.json b/n8n-workflow-ssh.json new file mode 100644 index 0000000..92f98ad --- /dev/null +++ b/n8n-workflow-ssh.json @@ -0,0 +1,98 @@ +{ + "name": "MQL Settings Monitor - SSH Version", + "nodes": [ + { + "parameters": { + "rule": { + "interval": [ + { + "field": "hours", + "hoursInterval": 6 + } + ] + } + }, + "name": "Every 6 Hours", + "type": "n8n-nodes-base.scheduleTrigger", + "typeVersion": 1, + "position": [250, 300] + }, + { + "parameters": { + "command": "/home/garfield/mql-trading-bots/scripts/validate-settings.sh", + "cwd": "/home/garfield/mql-trading-bots" + }, + "name": "Run Validation Script", + "type": "n8n-nodes-base.ssh", + "typeVersion": 1, + "position": [450, 300], + "credentials": { + "sshPassword": { + "id": "local-ssh", + "name": "Local SSH" + } + } + }, + { + "parameters": { + "conditions": { + "number": [ + { + "value1": "={{ $json.code }}", + "operation": "notEqual", + "value2": 0 + } + ] + } + }, + "name": "Issues Found?", + "type": "n8n-nodes-base.if", + "typeVersion": 1, + "position": [650, 300] + }, + { + "parameters": { + "chatId": "={{ $env.TELEGRAM_CHAT_ID }}", + "text": "=🚨 MQL Settings Issues:\n\n{{ $json.stdout }}\n\nā° {{ new Date().toISOString() }}" + }, + "name": "Send Telegram Alert", + "type": "n8n-nodes-base.telegram", + "typeVersion": 1, + "position": [850, 200], + "credentials": { + "telegramApi": { + "id": "telegram-bot", + "name": "Telegram Bot" + } + } + }, + { + "parameters": {}, + "name": "No Issues", + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [850, 400] + } + ], + "connections": { + "Every 6 Hours": { + "main": [ + [{ "node": "Run Validation Script", "type": "main", "index": 0 }] + ] + }, + "Run Validation Script": { + "main": [ + [{ "node": "Issues Found?", "type": "main", "index": 0 }] + ] + }, + "Issues Found?": { + "main": [ + [{ "node": "Send Telegram Alert", "type": "main", "index": 0 }], + [{ "node": "No Issues", "type": "main", "index": 0 }] + ] + } + }, + "settings": {}, + "staticData": null, + "tags": [] +} diff --git a/scripts/setup-cron.sh b/scripts/setup-cron.sh new file mode 100755 index 0000000..10ddcd9 --- /dev/null +++ b/scripts/setup-cron.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Setup cron job for MQL settings monitoring + +echo "Setting up cron job for MQL settings monitoring..." + +# Check if python3 is available +if ! command -v python3 &> /dev/null; then + echo "ERROR: python3 not found" + exit 1 +fi + +# Create cron entry +CRON_CMD="0 */6 * * * cd /home/garfield/mql-trading-bots && python3 scripts/validate-and-notify.py >> /tmp/mql-validate.log 2>&1" + +# Add to crontab +(crontab -l 2>/dev/null; echo "$CRON_CMD") | crontab - + +echo "Cron job added: Runs every 6 hours" +echo "Logs: /tmp/mql-validate.log" +echo "" +echo "To configure Telegram notifications, set these environment variables:" +echo " export TELEGRAM_BOT_TOKEN='your_bot_token'" +echo " export TELEGRAM_CHAT_ID='your_chat_id'" +echo "" +echo "Add these to your ~/.bashrc to persist:" +echo " echo 'export TELEGRAM_BOT_TOKEN=\"your_token\"' >> ~/.bashrc" +echo " echo 'export TELEGRAM_CHAT_ID=\"your_id\"' >> ~/.bashrc" diff --git a/scripts/validate-and-notify.py b/scripts/validate-and-notify.py new file mode 100755 index 0000000..a288251 --- /dev/null +++ b/scripts/validate-and-notify.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +""" +MQL Settings Validator with Telegram Notifications +Run via cron: python3 validate-and-notify.py +""" + +import os +import re +import subprocess +import sys +from datetime import datetime + +try: + import requests +except ImportError: + print("Installing requests...") + subprocess.run([sys.executable, "-m", "pip", "install", "requests", "-q"]) + import requests + +# Configuration +TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "") +TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "") +REPO_DIR = "/home/garfield/mql-trading-bots" + +def send_telegram(message): + """Send message to Telegram""" + if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID: + print("Telegram not configured. Message:") + print(message) + return False + + url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage" + payload = { + "chat_id": TELEGRAM_CHAT_ID, + "text": message, + "parse_mode": "HTML" + } + + try: + response = requests.post(url, json=payload, timeout=10) + return response.status_code == 200 + except Exception as e: + print(f"Failed to send Telegram: {e}") + return False + +def check_set_files(): + """Validate all .set files""" + issues = [] + warnings = [] + + os.chdir(REPO_DIR) + + # Check confluence files + confluence_files = [f for f in os.listdir(".") if f.startswith("confluence-") and f.endswith(".set")] + grid_files = [f for f in os.listdir(".") if f.startswith("grid-") and f.endswith(".set")] + + print(f"Found {len(confluence_files)} confluence files, {len(grid_files)} grid files") + + # Check for invalid variable names + invalid_pattern = re.compile(r'InpMinConfluenceStrength|MinConfluenceStrength', re.IGNORECASE) + for f in confluence_files: + with open(f, 'r') as file: + content = file.read() + if invalid_pattern.search(content): + issues.append(f"āŒ Invalid variable in {f}: InpMinConfluenceStrength (should be InpMinStrength)") + + # Check for missing critical parameters + critical_params_conf = ['InpMinConfluence', 'InpMinStrength', 'InpMagicNumber', 'InpCloseBeforeWeekend'] + critical_params_grid = ['InpCloseBeforeWeekend', 'InpMaxDailyDrawdown'] + + for f in confluence_files: + with open(f, 'r') as file: + content = file.read() + for param in critical_params_conf: + if param not in content: + issues.append(f"āŒ Missing {param} in {f}") + + for f in grid_files: + with open(f, 'r') as file: + content = file.read() + for param in critical_params_grid: + if param not in content: + issues.append(f"āŒ Missing {param} in {f}") + + # Check for high strength thresholds (warnings) + for f in confluence_files: + with open(f, 'r') as file: + content = file.read() + match = re.search(r'InpMinStrength=(\d+\.?\d*)', content) + if match: + strength = float(match.group(1)) + if strength >= 0.80: + warnings.append(f"āš ļø High threshold in {f}: {strength} (may block trades)") + + # Check debug mode + debug_off = 0 + for f in confluence_files + grid_files: + with open(f, 'r') as file: + content = file.read() + if 'InpDebugMode=false' in content: + debug_off += 1 + + if debug_off > 5: + warnings.append(f"āš ļø Debug disabled in {debug_off} files - you won't see trade decisions") + + return issues, warnings, len(confluence_files), len(grid_files) + +def main(): + """Main function""" + print("=" * 50) + print("MQL Settings Validator") + print(f"Time: {datetime.now().isoformat()}") + print("=" * 50) + + issues, warnings, conf_count, grid_count = check_set_files() + + # Build report + report_lines = [ + "šŸ“Š MQL Settings Check", + f"Confluence files: {conf_count}", + f"Grid files: {grid_count}", + "" + ] + + if issues: + report_lines.append("āŒ Issues Found:") + report_lines.extend(issues[:10]) # Limit to 10 issues + if len(issues) > 10: + report_lines.append(f"... and {len(issues) - 10} more issues") + report_lines.append("") + + if warnings: + report_lines.append("āš ļø Warnings:") + report_lines.extend(warnings[:5]) + report_lines.append("") + + if not issues and not warnings: + report_lines.append("āœ… All checks passed!") + report_lines.append("No issues found.") + + report_lines.append(f"\nā° {datetime.now().strftime('%Y-%m-%d %H:%M')}") + + report = "\n".join(report_lines) + print("\n" + report.replace('', '').replace('', '')) + + # Send notification if issues found or if it's a scheduled check + if issues or warnings: + send_telegram(report) + return 1 + else: + # Only send success message on first run of the day + if datetime.now().hour == 9: + send_telegram(report) + return 0 + +if __name__ == "__main__": + sys.exit(main())