AFML / afml /cache /startup_script.py
akshayboora's picture
Upload 940 files
669d6a1 verified
"""
Complete startup script for AFML Cache + MQL5 Integration
Handles proper initialization order and connection verification.
"""
import sys
import time
from datetime import datetime
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[2]
if __package__ in (None, "") and str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
import numpy as np
import pandas as pd
from loguru import logger
try:
from . import cacheable, initialize_cache_system
from .mql5_bridge import MQL5Bridge, SignalPacket
except ImportError:
from afml.cache import cacheable, initialize_cache_system
from afml.cache.mql5_bridge import MQL5Bridge, SignalPacket
# Configure logging
logger.remove()
logger.add(
sys.stderr,
level="INFO",
format="<green>{time:YYYY-MM-DD HH:mm:ss:ms}</green> | "
"<level>{level}</level> | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
"<level>{message}</level>",
colorize=True,
enqueue=True,
)
logger.add(
Path("logs", "mql5_bridge_{time:YYYY-MM-DD}.log"),
rotation="1 day",
retention="7 days",
level="DEBUG",
enqueue=True,
)
def wait_for_connection(bridge: MQL5Bridge, timeout: int = 60) -> bool:
"""
Wait for MQL5 to connect with timeout.
Args:
bridge: MQL5Bridge instance
timeout: Maximum seconds to wait
Returns:
True if connected, False if timeout
"""
logger.info(f"Waiting for MQL5 connection (timeout: {timeout}s)...")
start_time = time.time()
last_message = time.time()
while time.time() - start_time < timeout:
if bridge.client_socket is not None:
logger.success("[OK] MQL5 client connected!")
return True
# Print waiting message every 5 seconds
if time.time() - last_message >= 5:
elapsed = int(time.time() - start_time)
logger.info(f"Still waiting... ({elapsed}s elapsed)")
last_message = time.time()
time.sleep(0.5)
logger.error(f"[ERROR] Connection timeout after {timeout}s")
return False
def verify_server_listening(host: str, port: int) -> bool:
"""
Verify that the Python server is actually listening on the port.
Args:
host: Server host
port: Server port
Returns:
True if server is listening
"""
import socket
try:
# Try to connect to ourselves to verify
test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
test_socket.settimeout(2)
# This should succeed if server is listening
result = test_socket.connect_ex((host, port))
test_socket.close()
if result == 0:
logger.success(f"[OK] Server is listening on {host}:{port}")
return True
else:
logger.error(f"[ERROR] Server not listening on {host}:{port} (error: {result})")
return False
except Exception as e:
logger.error(f"[ERROR] Failed to verify server: {e}")
return False
def check_port_available(port: int) -> bool:
"""
Check if port is available before starting server.
Args:
port: Port to check
Returns:
True if available, False if in use
"""
import socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("127.0.0.1", port))
s.close()
logger.success(f"[OK] Port {port} is available")
return True
except OSError as e:
logger.error(f"[ERROR] Port {port} is in use: {e}")
return False
@cacheable()
def generate_test_features(data: pd.DataFrame) -> pd.DataFrame:
"""Generate test features (cached)."""
features = data.copy()
features["sma_20"] = data["close"].rolling(20).mean()
features["rsi"] = 50 + np.random.randn(len(data)) * 10 # Simplified
return features.dropna()
def generate_test_signal(data: pd.DataFrame) -> SignalPacket:
"""Generate a test trading signal."""
latest_price = data["close"].iloc[-1]
return SignalPacket(
timestamp=datetime.now().isoformat(),
symbol="EURUSD",
signal_type="BUY",
entry_price=latest_price,
stop_loss=latest_price - 0.0050,
take_profit=latest_price + 0.0100,
position_size=0.01,
confidence=0.75,
strategy_name="test_strategy",
metadata={"test": True, "generated_at": datetime.now().isoformat()},
)
def run_startup_checks(port: int = 5056) -> bool:
"""
Run all startup checks before starting bridge.
Returns:
True if all checks pass
"""
logger.info("Running startup checks...")
# Check 1: Cache system
try:
initialize_cache_system()
logger.success("[OK] Cache system initialized")
except Exception as e:
logger.error(f"[ERROR] Cache initialization failed: {e}")
return False
# Check 2: Port availability
if not check_port_available(port):
logger.error(f"Port {port} is not available")
logger.info(
f"Try: netstat -ano | findstr :{port} (Windows)"
)
return False
# Check 3: Test cache functionality
try:
test_data = pd.DataFrame({"close": np.random.randn(100) + 1.1000})
generate_test_features(test_data)
logger.success("[OK] Cache functionality verified")
except Exception as e:
logger.error(f"[ERROR] Cache test failed: {e}")
return False
logger.success("[OK] All startup checks passed")
return True
def print_startup_instructions(host: str = "127.0.0.1", port: int = 5056):
"""Print clear instructions for user."""
print("\n" + "=" * 70)
print("MQL5 CONNECTION INSTRUCTIONS")
print("=" * 70)
print("\nFollow these steps IN ORDER:\n")
print("1. [OK] Python server is now running (you're seeing this message)")
print("2. Open MetaTrader 5")
print("3. Open any chart (e.g., EURUSD, M5)")
print("4. Drag 'PythonBridgeEA' from Navigator -> Expert Advisors")
print("5. In the EA settings, verify:")
print(f" - PythonHost: {host}")
print(f" - PythonPort: {port}")
print(" - EnableTrading: false (for testing)")
print("6. [OK] Click OK")
print("7. Enable AutoTrading (Ctrl+E or click AutoTrading button)")
print("8. Watch the 'Experts' tab for connection message")
print("\n" + "=" * 70)
print("Waiting for MQL5 to connect...")
print("=" * 70 + "\n")
def main_live_mode(
host: str = "127.0.0.1", port: int = 5056, send_test_signal: bool = False
):
"""
Main function for live trading mode with proper connection handling.
"""
logger.info("=" * 70)
logger.info("AFML Cache + MQL5 Integration - LIVE MODE")
logger.info("=" * 70)
# Step 1: Run startup checks
if not run_startup_checks(port):
logger.error("Startup checks failed. Exiting.")
return
# Step 2: Create and start bridge
logger.info("\nStarting MQL5 bridge...")
bridge = MQL5Bridge(host=host, port=port, mode="live")
try:
bridge.start_server()
time.sleep(1) # Give server time to bind
# Step 3: Server bind succeeded. Do not self-connect here because the
# bridge accepts only one active MT5-style client at a time.
logger.success(f"[OK] Server is listening on {host}:{port}")
# Step 4: Print instructions
print_startup_instructions(host, port)
# Step 5: Wait for connection
if not wait_for_connection(bridge, timeout=3600):
logger.error("\n[ERROR] MQL5 did not connect within timeout period")
logger.info("\nTroubleshooting:")
logger.info("1. Is MetaTrader 5 running?")
logger.info("2. Is the EA attached to a chart?")
logger.info("3. Is AutoTrading enabled? (Ctrl+E)")
logger.info("4. Check the 'Experts' tab for error messages")
logger.info("5. Check Windows Firewall settings")
return
# Step 6: Connection established
logger.info("\n" + "=" * 70)
logger.info("CONNECTION ESTABLISHED")
logger.info("=" * 70)
time.sleep(2) # Let MQL5 settle
if send_test_signal:
# Generate and send an explicit opt-in smoke signal.
test_data = pd.DataFrame(
{
"close": 1.1000 + np.random.randn(100) * 0.0010,
"high": 1.1000 + np.random.randn(100) * 0.0010 + 0.0005,
"low": 1.1000 + np.random.randn(100) * 0.0010 - 0.0005,
}
)
logger.info("Sending test signal...")
test_signal = generate_test_signal(test_data)
success = bridge.send_signal(test_signal)
if success:
logger.success("[OK] Test signal sent successfully!")
else:
logger.warning("[WARN] Test signal queued (will send when MQL5 requests)")
else:
logger.info("Test signal disabled. Use --send-test-signal to send one.")
time.sleep(2)
# Step 7: Print stats
stats = bridge.get_performance_stats()
logger.info("\n" + "=" * 70)
logger.info("BRIDGE STATUS")
logger.info("=" * 70)
logger.info(f"Connected: {stats['connected']}")
logger.info(f"Signals Sent: {stats['signals_sent']}")
logger.info(f"Pending Signals: {stats['pending_signals']}")
logger.info(f"Uptime: {stats['uptime_seconds']:.0f}s")
logger.info("=" * 70)
# Step 8: Keep running
logger.info("\n[OK] Bridge is running. Press Ctrl+C to stop.\n")
# Main loop - keep bridge alive and send periodic updates
update_counter = 0
try:
while True:
time.sleep(10)
update_counter += 1
# Print status every 60 seconds
if update_counter % 6 == 0:
stats = bridge.get_performance_stats()
logger.info(
f"Status: Connected={stats['connected']}, "
f"Signals={stats['signals_sent']}, "
f"Executed={stats['signals_executed']}"
)
# Check if we lost connection
if not bridge.client_socket:
logger.warning(
"[WARN] Lost connection to MQL5. Waiting for reconnect..."
)
if wait_for_connection(bridge, timeout=30):
logger.success("[OK] Reconnected to MQL5")
except KeyboardInterrupt:
logger.info("\n\nShutting down gracefully...")
except Exception as e:
logger.error(f"Error in main loop: {e}")
import traceback
logger.error(traceback.format_exc())
finally:
bridge.stop()
logger.info("Bridge stopped. Goodbye!")
def main_test_mode(host: str = "127.0.0.1", port: int = 5056):
"""
Test mode - just verify everything works without waiting for MQL5.
"""
logger.info("=" * 70)
logger.info("QUICK TEST MODE")
logger.info("=" * 70)
# Run checks
if not run_startup_checks(port):
return
# Start bridge
logger.info("\nStarting bridge...")
bridge = MQL5Bridge(host=host, port=port, mode="live")
bridge.start_server()
time.sleep(1)
# Server bind succeeded. Avoid a dummy self-connection that looks like MT5.
logger.success(f"[OK] Server is listening on {host}:{port}")
# Try sending a test signal (will be queued)
logger.info("\nTesting signal generation...")
test_data = pd.DataFrame({"close": 1.1000 + np.random.randn(100) * 0.0010})
test_signal = generate_test_signal(test_data)
bridge.send_signal(test_signal)
stats = bridge.get_performance_stats()
logger.info(f"\nBridge Stats: {stats}")
logger.success("\n[OK] Test completed successfully!")
logger.info("Run with --live flag to wait for MQL5 connection")
bridge.stop()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="AFML MQL5 Bridge")
parser.add_argument(
"--mode",
choices=["live", "test"],
default="test",
help="Run mode: 'live' waits for MQL5, 'test' just verifies setup",
)
parser.add_argument("--host", default="127.0.0.1", help="Bridge host")
parser.add_argument("--port", type=int, default=5056, help="Bridge port")
parser.add_argument(
"--send-test-signal",
action="store_true",
help="Send the built-in EURUSD smoke signal after MQL5 connects.",
)
args = parser.parse_args()
if args.mode == "live":
main_live_mode(
host=args.host, port=args.port, send_test_signal=args.send_test_signal
)
else:
main_test_mode(host=args.host, port=args.port)