Add alternative validation methods: SSH, HTTP, and Python script with Telegram
This commit is contained in:
@@ -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
|
||||
|
||||
89
n8n-workflow-http.json
Normal file
89
n8n-workflow-http.json
Normal file
@@ -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": []
|
||||
}
|
||||
98
n8n-workflow-ssh.json
Normal file
98
n8n-workflow-ssh.json
Normal file
@@ -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": []
|
||||
}
|
||||
27
scripts/setup-cron.sh
Executable file
27
scripts/setup-cron.sh
Executable file
@@ -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"
|
||||
157
scripts/validate-and-notify.py
Executable file
157
scripts/validate-and-notify.py
Executable file
@@ -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"❌ <b>Invalid variable</b> 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"❌ <b>Missing {param}</b> 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"❌ <b>Missing {param}</b> 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"⚠️ <b>High threshold</b> 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"⚠️ <b>Debug disabled</b> 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 = [
|
||||
"<b>📊 MQL Settings Check</b>",
|
||||
f"Confluence files: {conf_count}",
|
||||
f"Grid files: {grid_count}",
|
||||
""
|
||||
]
|
||||
|
||||
if issues:
|
||||
report_lines.append("<b>❌ Issues Found:</b>")
|
||||
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("<b>⚠️ Warnings:</b>")
|
||||
report_lines.extend(warnings[:5])
|
||||
report_lines.append("")
|
||||
|
||||
if not issues and not warnings:
|
||||
report_lines.append("✅ <b>All checks passed!</b>")
|
||||
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('<b>', '').replace('</b>', ''))
|
||||
|
||||
# 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())
|
||||
Reference in New Issue
Block a user