MT5的EA脚本获取5分钟实时行情脚本

MT5的EA脚本获取5分钟实时行情脚本

//+------------------------------------------------------------------+
//|                                           Market Ticker v1.04    |
//|                    Sends new bar data to an external API         |
//|                         Copyright 2025, Your Name                |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Your Name"
#property link      "https://your.website.com"
#property version   "1.04"
#property script_show_inputs // Allows setting params from EA properties

//--- EA Inputs (外部参数,方便在加载EA时修改) ---
input string InpApiUrl = "http://127.0.0.1:8000/api/ainance/mt/receive_data"; // 您的API接口地址
input int    InpTimerSeconds = 300; // 定时器触发间隔(秒),300秒 = 5分钟

//--- Global Variables (全局变量) ---
string symbols[] = {"EURUSD", "GBPUSD", "USDJPY", "AUDUSD", "USDCAD"}; // 您要监控的交易对列表
ENUM_TIMEFRAMES timeframe = PERIOD_M5; // 您要监控的K线周期

datetime lastBarTimes[]; // 用于存储每个交易对最后处理的K线时间

//+------------------------------------------------------------------+
//| EA初始化函数 (加载到图表时执行一次)                                |
//+------------------------------------------------------------------+
void OnInit()
{
   // 根据交易对数量,初始化 lastBarTimes 数组
   ArrayResize(lastBarTimes, ArraySize(symbols));
   for(int i = 0; i < ArraySize(symbols); i++)
   {
      lastBarTimes[i] = 0; // 初始化为0,代表还未处理过任何K线
   }

   // 设置并启动定时器
   EventSetTimer(InpTimerSeconds);
   PrintFormat("EA 初始化成功。定时器已启动,将每 %d 秒检查一次 %s K线。", InpTimerSeconds, EnumToString(timeframe));
}

//+------------------------------------------------------------------+
//| EA终止函数 (从图表移除时执行一次)                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // 停止并销毁定时器,释放资源
   EventKillTimer();
   Print("EA 已终止,定时器已停止。");
}

//+------------------------------------------------------------------+
//| EA报价事件函数 (每个报价到来时执行)                                |
//| ★★★ 这个函数必须存在,以确保文件被识别为EA ★★★                   |
//+------------------------------------------------------------------+
void OnTick()
{
   //--- 此函数被有意留空 ---
   //--- 所有的核心逻辑都由 OnTimer() 事件处理 ---
}

//+------------------------------------------------------------------+
//| 定时器事件函数 (每隔 InpTimerSeconds 秒执行一次)                     |
//+------------------------------------------------------------------+
void OnTimer()
{
   // 遍历所有交易对,检查是否有新的K线生成
   for(int i = 0; i < ArraySize(symbols); i++)
   {
      CheckForNewBar(InpApiUrl, symbols[i], timeframe, i);
   }
}

//+------------------------------------------------------------------+
//| 检查新K线并发送数据                                               |
//+------------------------------------------------------------------+
void CheckForNewBar(string url, string symbol, ENUM_TIMEFRAMES tf, int index)
{
   MqlRates rates[];
   // 尝试获取最新的2根K线数据
   int copied = CopyRates(symbol, tf, 0, 2, rates);

   if(copied < 1) // 如果连1根K线都获取不到,则无法继续
   {
      PrintFormat("无法获取 %s 的K线数据! 错误代码: %d", symbol, GetLastError());
      return;
   }

   // 最新的K线总是在数组的第0个位置
   datetime latestBarTime = rates[0].time;

   // 如果最新的K线时间大于我们上次记录的时间,说明是新K线
   if(latestBarTime > lastBarTimes[index])
   {
      PrintFormat("检测到 %s 的新K线, 时间: %s", symbol, TimeToString(latestBarTime, TIME_DATE | TIME_MINUTES));

      // 更新该交易对的最后处理时间
      lastBarTimes[index] = latestBarTime;

      // 将这根新K线的数据转换为JSON格式
      string jsonData = ConvertSingleRateToJson(rates[0], symbol, tf);

      // 通过HTTP POST请求将JSON数据发送到您的API
      SendSingleRateToAPI(url, jsonData);
   }
}

//+------------------------------------------------------------------+
//| 将单根K线数据(MqlRates)转换为JSON字符串                          |
//+------------------------------------------------------------------+
string ConvertSingleRateToJson(const MqlRates &rate, string symbol, ENUM_TIMEFRAMES tf)
{
   MqlDateTime dt;
   TimeToStruct(rate.time, dt);

   // 将时间格式化为标准的 ISO 8601 格式 (例如: "2025-07-30T10:05:00Z")
   // 这部分保持不变,因为服务器端 'time' 字段是字符串
   string timeStr = StringFormat("%04d-%02d-%02dT%02d:%02d:%02dZ",
                                 dt.year, dt.mon, dt.day,
                                 dt.hour, dt.min, dt.sec);

   // 获取交易品种的小数位数
   int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);

   string jsonData = StringFormat(
      "{\"symbol\":\"%s\",\"time\":\"%s\","
      "\"open\":%.*f,\"high\":%.*f,\"low\":%.*f,\"close\":%.*f,\"volume\":%d}",
      symbol,
      timeStr,
      digits, rate.open,      // "%.*f" 的第一个参数和值
      digits, rate.high,     // "%.*f" 的第二个参数和值
      digits, rate.low,      // "%.*f" 的第三个参数和值
      digits, rate.close,    // "%.*f" 的第四个参数和值
      (long)rate.tick_volume // "%d" 的值
   );

   return jsonData;
}

//+------------------------------------------------------------------+
//| 将单根K线数据发送到指定的API地址 (最终釜底抽薪版)                |
//+------------------------------------------------------------------+
void SendSingleRateToAPI(string url, string jsonData)
{
   // 打印即将发送的 JSON 内容
   Print("即将发送的 JSON: ", jsonData);

   // ★★★★★ 核心修改:我们不再依赖 ArraySize-1,而是从源头根除 \0 ★★★★★
   
   // 1. 获取纯净JSON字符串的长度(不包含任何结束符)
   int jsonLength = StringLen(jsonData);

   // 2. 创建一个 char 数组,其大小与纯净JSON的长度完全相等
   char postData[];
   ArrayResize(postData, jsonLength);

   // 3. 将JSON字符串复制到 char 数组中,明确指定只复制 jsonLength 个字符。
   //    这样,就没有空间留给自动添加的 \0 结束符了。
   StringToCharArray(jsonData, postData, 0, jsonLength, CP_UTF8);

   // ★★★★★ 修改结束 ★★★★★

   // 设置HTTP请求头
   string headers = "Content-Type: application/json";
   int timeout = 5000; // 5秒超时

   // 准备接收返回结果的变量
   char result[];
   string resultHeaders;
   
   // 发起HTTP POST请求
   // 现在 postData 的大小就是纯净JSON的长度,所以直接使用 ArraySize(postData)
   int responseCode = WebRequest("POST", url, headers, NULL, timeout, postData, ArraySize(postData), result, resultHeaders);

   if(responseCode == 200)
   {
      string response = (ArraySize(result) > 0) ? CharArrayToString(result, 0, -1, CP_UTF8) : "[服务器返回空内容]";
      Print("数据发送成功. 后端返回: ", response);
   }
   else
   {
      PrintFormat("HTTP请求失败! 响应代码: %d, 错误代码: %d", responseCode, GetLastError());
      string serverErrorResponse = (ArraySize(result) > 0) ? CharArrayToString(result, 0, -1, CP_UTF8) : "[服务器未返回任何错误详情]";
      Print("服务器返回的错误详情: ", serverErrorResponse);
   }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容