AiDataTaskRunerMcp/Src/aidatataskruner_mcp.py
2026-04-23 21:16:29 -05:00

313 lines
No EOL
12 KiB
Python

import socket
import json
import threading
import argparse
from mcp.server.fastmcp import FastMCP
#+------------------------------------------------------------------+
#| Args |
#+------------------------------------------------------------------+
parser = argparse.ArgumentParser()
parser.add_argument("--host", type=str, default="127.0.0.1")
parser.add_argument("--port", type=int, default=9999)
args = parser.parse_args()
HOST = args.host
PORT = args.port
#+------------------------------------------------------------------+
#| General |
#+------------------------------------------------------------------+
mcp = FastMCP("AiDataTaskRunerMT5 MCP")
# Conexion de MT5 (se llena cuando MT5 conecta)
mt5_conn = None
def esperar_mt5():
global mt5_conn
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_sock.bind((HOST, PORT))
server_sock.listen(1)
mt5_conn, _ = server_sock.accept() # espera a que MT5 conecte
# Arranca en hilo separado, no bloquea el MCP
# La idea esq ue exista mientras que se termine por fin de conectar con mt5
threading.Thread(target=esperar_mt5, daemon=True).start()
#+------------------------------------------------------------------+
#| Send |
#+------------------------------------------------------------------+
def send(name: str, payload: str) -> str:
if mt5_conn is None:
return json.dumps({"ok": False, "error": "mt5_no_conectado"})
msg = json.dumps({"name": name, "data": json.loads(payload)}) + "\n"
mt5_conn.sendall(msg.encode("utf-8"))
return mt5_conn.recv(4096).decode("utf-8").strip()
#+------------------------------------------------------------------+
# Funciones
@mcp.tool()
def aidatataskrunner_add_task(payload: str) -> str:
"""
Agrega una nueva tarea de backtest a la tabla de AiDataTaskRunner.
Parametros (JSON):
- symbol (string, requerido): Simbolo de trading. Ej: "EURUSD", "XAUUSD"
- start_date (string, requerido): Fecha inicio. Formato: "YYYY.MM.DD HH:MM"
- end_date (string, requerido): Fecha fin. Formato: "YYYY.MM.DD HH:MM"
- timeframe (string, opcional): Temporalidad MQL5. Ej: "PERIOD_H1", "PERIOD_M15". Default: "_Period"
- set_file (string, opcional): Ruta al archivo .set de parametros del EA. Default: ""
- symbol_folder (string, opcional): Subcarpeta para agrupar datos. Ej: "XAUUSD". Default: ""
- label (string, opcional): Nombre de la estrategia. Ej: "BosChoch". Default: ""
- label_id (string, opcional): ID variante del label. Ej: "0", "1". Default: "0"
Retorna (JSON):
- ok (boolean): true si se agrego correctamente
- result (string): mensaje de confirmacion o error
Ejemplo llamada: {"symbol": "EURUSD", "start_date": "2023.01.01 00:00", "end_date": "2024.01.01 00:00"}
Ejemplo retorno: {"ok": true, "result": "Task added successfully"}
"""
return send("aidatataskrunner_add_task", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_get_task_total(payload: str) -> str:
"""
Retorna el numero total de tareas actualmente en la tabla.
Parametros: ninguno, pasar JSON vacio "{}"
Retorna (JSON):
- ok (boolean): true si la consulta fue exitosa
- result (integer como string): numero total de tareas
Ejemplo llamada: {}
Ejemplo retorno: {"ok": true, "result": "5"}
"""
return send("aidatataskrunner_get_task_total", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_get_task_by_index(payload: str) -> str:
"""
Retorna los detalles completos de una tarea segun su indice en la tabla.
Usar aidatataskrunner_get_task_total para conocer el total antes de llamar.
Parametros (JSON):
- index (integer, requerido): Indice base 0 de la tarea
Retorna (JSON):
- ok (boolean): true si el indice es valido
- result (string): datos de la tarea separados por | (timeframe|symbol|set_file|start|end|folder|label|label_id)
Ejemplo llamada: {"index": 0}
Ejemplo retorno: {"ok": true, "result": "16385|EURUSD||2023.01.01 00:00|2024.01.01 00:00|||0"}
"""
return send("aidatataskrunner_get_task_by_index", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_get_task_status(payload: str) -> str:
"""
Retorna el estado actual de una tarea segun su indice en la tabla.
Parametros (JSON):
- index (integer, requerido): Indice base 0 de la tarea
Retorna (JSON):
- ok (boolean): true si el indice es valido
- result (integer como string):
"0" = Procesando
"1" = En cola
"2" = Pendiente
"3" = Listo
"4" = Fallo
Ejemplo llamada: {"index": 0}
Ejemplo retorno: {"ok": true, "result": "2"}
"""
return send("aidatataskrunner_get_task_status", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_clean_all_tasks(payload: str) -> str:
"""
Elimina de la tabla todas las tareas que no esten en ejecucion ni en cola.
Las tareas con estado Procesando o En cola no se eliminan.
Parametros: ninguno, pasar JSON vacio "{}"
Retorna (JSON):
- ok (boolean): true si se limpio al menos una tarea
- result (string): mensaje de confirmacion o indicacion de que no habia nada que limpiar
Ejemplo llamada: {}
Ejemplo retorno: {"ok": true, "result": "All tasks cleaned"}
"""
return send("aidatataskrunner_clean_all_tasks", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_execute_all_tasks(payload: str) -> str:
"""
Pone en cola de ejecucion todas las tareas con estado Pendiente.
Operacion fire-and-forget: retorna inmediatamente sin esperar resultado.
Parametros: ninguno, pasar JSON vacio "{}"
Retorna (JSON):
- ok (boolean): siempre true
- result (string): "queued"
Ejemplo llamada: {}
Ejemplo retorno: {"ok": true, "result": "queued"}
"""
return send("aidatataskrunner_execute_all_tasks", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_save_tasks_to_file(payload: str) -> str:
"""
Guarda las tareas de la tabla en un archivo CSV.
La ruta es relativa a la carpeta de trabajo de MT5.
Consultar aidatataskrunner_is_in_commonfolder para saber si es Common\\Files\\ o MQL5\\Files\\.
Parametros (JSON):
- file_name (string, requerido): Ruta relativa del archivo .csv destino
- only_unfinished (boolean, requerido): true = guardar solo pendientes/en cola/procesando | false = guardar todas
Retorna (JSON):
- ok (boolean): true si se guardo correctamente
- result (string): mensaje de confirmacion o error
Ejemplo llamada: {"file_name": "tasks.csv", "only_unfinished": false}
Ejemplo retorno: {"ok": true, "result": "Successfully saving all tasks to file tasks.csv"}
"""
return send("aidatataskrunner_save_tasks_to_file", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_load_tasks_from_file(payload: str) -> str:
"""
Carga tareas desde un archivo CSV y las agrega a la tabla.
La ruta es relativa a la carpeta de trabajo de MT5.
Consultar aidatataskrunner_is_in_commonfolder para saber si es Common\\Files\\ o MQL5\\Files\\.
Parametros (JSON):
- file_name (string, requerido): Ruta relativa del archivo .csv a cargar
Retorna (JSON):
- ok (boolean): true si se cargo correctamente
- result (string): mensaje de confirmacion o error
Ejemplo llamada: {"file_name": "tasks.csv"}
Ejemplo retorno: {"ok": true, "result": "Tasks loaded successfully"}
"""
return send("aidatataskrunner_load_tasks_from_file", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_load_config(payload: str) -> str:
"""
Carga la configuracion del tab de generacion de datos desde un archivo .txt.
La ruta es relativa a la carpeta de trabajo de MT5.
Consultar aidatataskrunner_is_in_commonfolder para saber si es Common\\Files\\ o MQL5\\Files\\.
Parametros (JSON):
- file_name (string, requerido): Ruta relativa del archivo .txt a cargar
Retorna (JSON):
- ok (boolean): true si se cargo correctamente
- result (string): mensaje de confirmacion o error
Ejemplo llamada: {"file_name": "config.txt"}
Ejemplo retorno: {"ok": true, "result": "Config loaded successfully"}
"""
return send("aidatataskrunner_load_config", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_save_config(payload: str) -> str:
"""
Guarda la configuracion actual del tab de generacion de datos en un archivo .txt.
La ruta es relativa a la carpeta de trabajo de MT5.
Consultar aidatataskrunner_is_in_commonfolder para saber si es Common\\Files\\ o MQL5\\Files\\.
Parametros (JSON):
- file_name (string, requerido): Ruta relativa del archivo .txt destino
Retorna (JSON):
- ok (boolean): true si se guardo correctamente
- result (string): mensaje de confirmacion o error
Ejemplo llamada: {"file_name": "config.txt"}
Ejemplo retorno: {"ok": true, "result": "Config saved successfully"}
"""
return send("aidatataskrunner_save_config", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_get_main_folder(payload: str) -> str:
"""
Retorna la ruta de la carpeta base principal usada por AiDataTaskRunner.
Relativa a Common\\Files\\ o MQL5\\Files\\ segun aidatataskrunner_is_in_commonfolder.
Parametros: ninguno, pasar JSON vacio "{}"
Retorna (JSON):
- ok (boolean): true siempre
- result (string): ruta de la carpeta principal
Ejemplo llamada: {}
Ejemplo retorno: {"ok": true, "result": "AiDataTaskRunerPro\\"}
"""
return send("aidatataskrunner_get_main_folder", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_get_task_folder(payload: str) -> str:
"""
Retorna la ruta de la carpeta donde se almacenan los archivos de tareas.
Relativa a Common\\Files\\ o MQL5\\Files\\ segun aidatataskrunner_is_in_commonfolder.
Parametros: ninguno, pasar JSON vacio "{}"
Retorna (JSON):
- ok (boolean): true siempre
- result (string): ruta de la carpeta de tareas
Ejemplo llamada: {}
Ejemplo retorno: {"ok": true, "result": "AiDataTaskRunerPro\\Tasks\\"}
"""
return send("aidatataskrunner_get_task_folder", payload)
#+------------------------------------------------------------------+
@mcp.tool()
def aidatataskrunner_is_in_commonfolder(payload: str) -> str:
"""
Indica si AiDataTaskRunner esta operando sobre la carpeta comun de MT5 (Common\\Files\\)
o sobre la carpeta local del terminal (MQL5\\Files\\).
Util para construir rutas correctas antes de llamar a otras funciones.
Parametros: ninguno, pasar JSON vacio "{}"
Retorna (JSON):
- ok (boolean): true siempre
- result (string): "1" = usa Common\\Files\\ | "0" = usa MQL5\\Files\\
Ejemplo llamada: {}
Ejemplo retorno: {"ok": true, "result": "1"}
"""
return send("aidatataskrunner_is_in_commonfolder", payload)
#+------------------------------------------------------------------+
#| Main |
#+------------------------------------------------------------------+
def main():
mcp.run()
if __name__ == "__main__":
main()