| 发表于 3 天前 | 显示全部楼层 |复制链接

  1. //+------------------------------------------------------------------+
  2. //|                                              蓝小凡精准狙击.mq5    |
  3. //|                                    Copyright 2026, 蓝小凡精准狙击  |
  4. //|                                             https://www.mql5.com |
  5. //+------------------------------------------------------------------+
  6. #property copyright "Copyright 2026, 蓝小凡精准狙击"
  7. #property link      "https://www.mql5.com"
  8. #property version   "1.09"
  9. //+------------------------------------------------------------------+
  10. //| 输入参数                                                          |
  11. //+------------------------------------------------------------------+
  12. input ENUM_TIMEFRAMES InpBoxTimeframe = PERIOD_H1;     // 箱体K线周期
  13. input int             InpBoxBars      = 20;            // 箱体K线数量
  14. input double   InpShortOffset     = 2.0;               // 做空偏移(最高价-n美金)
  15. input double   InpLongOffset      = 2.0;               // 做多偏移(最高价+n美金)
  16. input string   InpMartingaleSeries = "0.01,0.01,0.02,0.04,0.08,0.16"; // 倍投序列
  17. input int      InpMinGapMinutes   = 5;                 // 加仓最小间隔(分钟)
  18. input double   InpMonitorStart    = 5.0;               // 开始监控盈利(美金)
  19. input double   InpMonitorStep     = 2.0;               // 监控步进(美金)
  20. input double   InpDrawbackPercent = 30.0;              // 回撤平仓百分比(%)
  21. input double   InpHardStopLoss    = 50.0;              // 硬止损(美金)
  22. input double   InpHardTakeProfit  = 100.0;             // 硬止盈(美金)
  23. input bool     InpEnableReverseClose = true;           // 启用反转平仓
  24. input int      InpRSIPeriod          = 14;             // RSI周期
  25. input int      InpRSIOverbought      = 70;             // RSI超买线
  26. input int      InpRSIOversold        = 30;             // RSI超卖线
  27. input color   InpUIColor           = clrDodgerBlue;    // 保留未使用
  28. input int     InpFontSize          = 14;               // 字体大小
  29. //+------------------------------------------------------------------+
  30. //| 全局变量                                                          |
  31. //+------------------------------------------------------------------+
  32. string         g_Symbol;
  33. long           g_MagicNumber = 20260805;
  34. string         g_EAComment = "LXF_JZ_SNIPE";
  35. double         g_BoxHigh = 0, g_BoxLow = 0;
  36. datetime       g_BoxStartTime = 0, g_BoxEndTime = 0;
  37. bool           g_BoxValid = false;
  38. double         g_MartingaleArray[];
  39. datetime       g_LastAddTime = 0;
  40. int            g_AddCount = 0;
  41. double         g_MonitorLevel = 0, g_PeakProfit = 0;
  42. bool           g_IsMonitoring = false;
  43. string         g_UIPrefix = "LXF_UI_";
  44. datetime       g_LastLogTime = 0;
  45. //+------------------------------------------------------------------+
  46. //| 初始化                                                            |
  47. //+------------------------------------------------------------------+
  48. int OnInit()
  49. {
  50.    g_Symbol = Symbol();
  51.    Print("✅ EA运行品种: ", g_Symbol);
  52.    
  53.    if(!ParseMartingaleSeries(InpMartingaleSeries, g_MartingaleArray))
  54.    {
  55.       Print("错误: 倍投序列格式错误!");
  56.       return INIT_FAILED;
  57.    }
  58.    
  59.    CreateUI();
  60.    return INIT_SUCCEEDED;
  61. }
  62. //+------------------------------------------------------------------+
  63. //| 反初始化                                                          |
  64. //+------------------------------------------------------------------+
  65. void OnDeinit(const int reason)
  66. {
  67.    ObjectsDeleteAll(0, g_UIPrefix);
  68.    ObjectsDeleteAll(0, "LXF_BOX_");
  69. }
  70. //+------------------------------------------------------------------+
  71. //| Tick                                                              |
  72. //+------------------------------------------------------------------+
  73. void OnTick()
  74. {
  75.    static bool firstRun = true;
  76.    if(firstRun)
  77.    {
  78.       UpdateBox();
  79.       ChartRedraw(0);
  80.       firstRun = false;
  81.    }
  82.    
  83.    UpdateUI();
  84.    
  85.    static datetime lastBarTime = 0;
  86.    datetime currentBarTime = iTime(g_Symbol, InpBoxTimeframe, 0);
  87.    if(currentBarTime != lastBarTime)
  88.    {
  89.       UpdateBox();
  90.       ChartRedraw(0);
  91.       lastBarTime = currentBarTime;
  92.    }
  93.    
  94.    if(InpEnableReverseClose)
  95.       CheckReverseClose();
  96.    
  97.    CheckDynamicTakeProfit();
  98.    CheckHardSLTP();
  99.    CheckTradeConditions();
  100.    
  101.    if(TimeCurrent() - g_LastLogTime >= 10)
  102.    {
  103.       LogStatus();
  104.       g_LastLogTime = TimeCurrent();
  105.    }
  106. }
  107. //+------------------------------------------------------------------+
  108. //| 箱体绘制(标签移至右上角)                                        |
  109. //+------------------------------------------------------------------+
  110. void UpdateBox()
  111. {
  112.    MqlRates rates[];
  113.    int copied = CopyRates(g_Symbol, InpBoxTimeframe, 0, InpBoxBars, rates);
  114.    if(copied < InpBoxBars)
  115.    {
  116.       Print("箱体更新失败:K线数量不足,需要 ", InpBoxBars, " 根,实际获取 ", copied);
  117.       g_BoxValid = false;
  118.       return;
  119.    }
  120.    
  121.    double high = rates[0].high, low = rates[0].low;
  122.    datetime startTime = rates[0].time, endTime = rates[copied-1].time;
  123.    for(int i = 1; i < copied; i++)
  124.    {
  125.       if(rates[i].high > high) high = rates[i].high;
  126.       if(rates[i].low < low) low = rates[i].low;
  127.    }
  128.    
  129.    g_BoxHigh = high;
  130.    g_BoxLow = low;
  131.    g_BoxStartTime = startTime;
  132.    g_BoxEndTime = endTime;
  133.    g_BoxValid = true;
  134.    
  135.    Print("箱体更新成功: 高点=", DoubleToString(high, _Digits),
  136.          " 低点=", DoubleToString(low, _Digits),
  137.          " 时间范围=", TimeToString(startTime), " ~ ", TimeToString(endTime));
  138.    
  139.    string boxName = "LXF_BOX_MAIN";
  140.    ObjectDelete(0, boxName);
  141.    ObjectDelete(0, boxName + "_FILL");
  142.    
  143.    if(ObjectCreate(0, boxName + "_FILL", OBJ_RECTANGLE, 0, startTime, high, endTime, low))
  144.    {
  145.       ObjectSetInteger(0, boxName + "_FILL", OBJPROP_FILL, true);
  146.       ObjectSetInteger(0, boxName + "_FILL", OBJPROP_COLOR, clrDodgerBlue);
  147.       ObjectSetInteger(0, boxName + "_FILL", OBJPROP_STYLE, STYLE_SOLID);
  148.       ObjectSetInteger(0, boxName + "_FILL", OBJPROP_WIDTH, 1);
  149.       ObjectSetInteger(0, boxName + "_FILL", OBJPROP_BACK, true);
  150.       ObjectSetInteger(0, boxName + "_FILL", OBJPROP_SELECTABLE, false);
  151.       ObjectSetInteger(0, boxName + "_FILL", OBJPROP_HIDDEN, false);
  152.       ObjectSetInteger(0, boxName + "_FILL", OBJPROP_ZORDER, 0);
  153.    }
  154.    
  155.    ObjectCreate(0, boxName + "_TOP", OBJ_HLINE, 0, 0, high);
  156.    ObjectSetInteger(0, boxName + "_TOP", OBJPROP_COLOR, clrRed);
  157.    ObjectSetInteger(0, boxName + "_TOP", OBJPROP_STYLE, STYLE_SOLID);
  158.    ObjectSetInteger(0, boxName + "_TOP", OBJPROP_WIDTH, 2);
  159.    ObjectSetInteger(0, boxName + "_TOP", OBJPROP_BACK, false);
  160.    
  161.    ObjectCreate(0, boxName + "_BOTTOM", OBJ_HLINE, 0, 0, low);
  162.    ObjectSetInteger(0, boxName + "_BOTTOM", OBJPROP_COLOR, clrLime);
  163.    ObjectSetInteger(0, boxName + "_BOTTOM", OBJPROP_STYLE, STYLE_SOLID);
  164.    ObjectSetInteger(0, boxName + "_BOTTOM", OBJPROP_WIDTH, 2);
  165.    ObjectSetInteger(0, boxName + "_BOTTOM", OBJPROP_BACK, false);
  166.    
  167.    ObjectCreate(0, boxName + "_LEFT", OBJ_VLINE, 0, startTime, 0);
  168.    ObjectSetInteger(0, boxName + "_LEFT", OBJPROP_COLOR, clrGray);
  169.    ObjectSetInteger(0, boxName + "_LEFT", OBJPROP_STYLE, STYLE_DASH);
  170.    ObjectSetInteger(0, boxName + "_LEFT", OBJPROP_WIDTH, 1);
  171.    ObjectSetInteger(0, boxName + "_LEFT", OBJPROP_BACK, false);
  172.    
  173.    ObjectCreate(0, boxName + "_RIGHT", OBJ_VLINE, 0, endTime, 0);
  174.    ObjectSetInteger(0, boxName + "_RIGHT", OBJPROP_COLOR, clrGray);
  175.    ObjectSetInteger(0, boxName + "_RIGHT", OBJPROP_STYLE, STYLE_DASH);
  176.    ObjectSetInteger(0, boxName + "_RIGHT", OBJPROP_WIDTH, 1);
  177.    ObjectSetInteger(0, boxName + "_RIGHT", OBJPROP_BACK, false);
  178.    
  179.    // ===== 箱体高点/低点标签 → 移至右上角 =====
  180.    string labelHigh = boxName + "_LABEL_H";
  181.    string labelLow = boxName + "_LABEL_L";
  182.    
  183.    // 删除旧标签(避免残留)
  184.    ObjectDelete(0, labelHigh);
  185.    ObjectDelete(0, labelLow);
  186.    
  187.    // 创建新标签,锚点设为右上角
  188.    ObjectCreate(0, labelHigh, OBJ_LABEL, 0, 0, 0);
  189.    ObjectSetInteger(0, labelHigh, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);   // 右上角锚点
  190.    ObjectSetInteger(0, labelHigh, OBJPROP_XDISTANCE, 10);                // 距右边缘10像素
  191.    ObjectSetInteger(0, labelHigh, OBJPROP_YDISTANCE, 30);                // 距上边缘30像素
  192.    ObjectSetString(0, labelHigh, OBJPROP_TEXT, "箱体高点: " + DoubleToString(high, _Digits));
  193.    ObjectSetInteger(0, labelHigh, OBJPROP_COLOR, clrRed);
  194.    ObjectSetInteger(0, labelHigh, OBJPROP_FONTSIZE, 9);
  195.    ObjectSetString(0, labelHigh, OBJPROP_FONT, "Arial");
  196.    ObjectSetInteger(0, labelHigh, OBJPROP_BACK, false);
  197.    ObjectSetInteger(0, labelHigh, OBJPROP_SELECTABLE, false);
  198.    
  199.    ObjectCreate(0, labelLow, OBJ_LABEL, 0, 0, 0);
  200.    ObjectSetInteger(0, labelLow, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
  201.    ObjectSetInteger(0, labelLow, OBJPROP_XDISTANCE, 10);
  202.    ObjectSetInteger(0, labelLow, OBJPROP_YDISTANCE, 50);                // 第二行,与高点间隔20像素
  203.    ObjectSetString(0, labelLow, OBJPROP_TEXT, "箱体低点: " + DoubleToString(low, _Digits));
  204.    ObjectSetInteger(0, labelLow, OBJPROP_COLOR, clrLime);
  205.    ObjectSetInteger(0, labelLow, OBJPROP_FONTSIZE, 9);
  206.    ObjectSetString(0, labelLow, OBJPROP_FONT, "Arial");
  207.    ObjectSetInteger(0, labelLow, OBJPROP_BACK, false);
  208.    ObjectSetInteger(0, labelLow, OBJPROP_SELECTABLE, false);
  209.    
  210.    ChartRedraw(0);
  211. }
  212. //+------------------------------------------------------------------+
  213. //| 获取填充模式                                                      |
  214. //+------------------------------------------------------------------+
  215. int GetFillingMode()
  216. {
  217.    long fillingMode = SymbolInfoInteger(g_Symbol, SYMBOL_FILLING_MODE);
  218.    if(fillingMode & SYMBOL_FILLING_FOK) return ORDER_FILLING_FOK;
  219.    if(fillingMode & SYMBOL_FILLING_IOC) return ORDER_FILLING_IOC;
  220.    return ORDER_FILLING_RETURN;
  221. }
  222. //+------------------------------------------------------------------+
  223. //| 交易条件检查                                                      |
  224. //+------------------------------------------------------------------+
  225. void CheckTradeConditions()
  226. {
  227.    if(!g_BoxValid) return;
  228.    
  229.    double bid = SymbolInfoDouble(g_Symbol, SYMBOL_BID);
  230.    double ask = SymbolInfoDouble(g_Symbol, SYMBOL_ASK);
  231.    double point = SymbolInfoDouble(g_Symbol, SYMBOL_POINT);
  232.    double tickValue = SymbolInfoDouble(g_Symbol, SYMBOL_TRADE_TICK_VALUE);
  233.    double offsetPrice = InpShortOffset / tickValue * point;
  234.    double shortTriggerPrice = g_BoxHigh - offsetPrice;
  235.    double longTriggerPrice = g_BoxHigh + offsetPrice;
  236.    
  237.    int posCount = PositionsTotal();
  238.    bool hasPosition = false, hasBuy = false, hasSell = false;
  239.    double totalProfit = 0;
  240.    for(int i = 0; i < posCount; i++)
  241.    {
  242.       ulong ticket = PositionGetTicket(i);
  243.       if(PositionSelectByTicket(ticket))
  244.          if(PositionGetString(POSITION_SYMBOL) == g_Symbol && PositionGetInteger(POSITION_MAGIC) == g_MagicNumber)
  245.          {
  246.             hasPosition = true;
  247.             if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) hasBuy = true;
  248.             else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) hasSell = true;
  249.             totalProfit += PositionGetDouble(POSITION_PROFIT);
  250.          }
  251.    }
  252.    
  253.    if(!hasPosition)
  254.    {
  255.       if(bid <= shortTriggerPrice)
  256.       {
  257.          OpenPosition(ORDER_TYPE_SELL, g_MartingaleArray[0]);
  258.          g_AddCount = 1;
  259.          g_LastAddTime = TimeCurrent();
  260.          return;
  261.       }
  262.       if(ask >= longTriggerPrice)
  263.       {
  264.          OpenPosition(ORDER_TYPE_BUY, g_MartingaleArray[0]);
  265.          g_AddCount = 1;
  266.          g_LastAddTime = TimeCurrent();
  267.          return;
  268.       }
  269.    }
  270.    else
  271.    {
  272.       if(totalProfit < 0)
  273.       {
  274.          if((TimeCurrent() - g_LastAddTime) < InpMinGapMinutes * 60) return;
  275.          if(g_AddCount >= ArraySize(g_MartingaleArray)) return;
  276.          if(hasSell && !hasBuy && ask >= shortTriggerPrice)
  277.          {
  278.             double lot = g_MartingaleArray[g_AddCount];
  279.             OpenPosition(ORDER_TYPE_SELL, lot);
  280.             g_AddCount++;
  281.             g_LastAddTime = TimeCurrent();
  282.             Print("空单加仓: 手数=", lot, " 次数=", g_AddCount);
  283.          }
  284.          else if(hasBuy && !hasSell && bid <= longTriggerPrice)
  285.          {
  286.             double lot = g_MartingaleArray[g_AddCount];
  287.             OpenPosition(ORDER_TYPE_BUY, lot);
  288.             g_AddCount++;
  289.             g_LastAddTime = TimeCurrent();
  290.             Print("多单加仓: 手数=", lot, " 次数=", g_AddCount);
  291.          }
  292.       }
  293.    }
  294. }
  295. //+------------------------------------------------------------------+
  296. //| 开仓                                                              |
  297. //+------------------------------------------------------------------+
  298. void OpenPosition(ENUM_ORDER_TYPE type, double lot)
  299. {
  300.    MqlTradeRequest request = {};
  301.    MqlTradeResult result = {};
  302.    request.action = TRADE_ACTION_DEAL;
  303.    request.symbol = g_Symbol;
  304.    request.volume = lot;
  305.    request.type = type;
  306.    request.deviation = 10;
  307.    request.magic = g_MagicNumber;
  308.    request.comment = g_EAComment;
  309.    request.type_filling = GetFillingMode();
  310.    request.price = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(g_Symbol, SYMBOL_ASK) : SymbolInfoDouble(g_Symbol, SYMBOL_BID);
  311.    if(!OrderSend(request, result))
  312.       Print("开仓失败: ", result.retcode, " - ", result.comment);
  313.    else
  314.       Print("开仓成功: ", (type == ORDER_TYPE_BUY) ? "多" : "空", " 手数=", lot);
  315. }
  316. //+------------------------------------------------------------------+
  317. //| 动态止盈                                                          |
  318. //+------------------------------------------------------------------+
  319. void CheckDynamicTakeProfit()
  320. {
  321.    double totalProfit = GetTotalProfit();
  322.    if(totalProfit < InpMonitorStart)
  323.    {
  324.       g_IsMonitoring = false;
  325.       g_MonitorLevel = 0;
  326.       g_PeakProfit = 0;
  327.       return;
  328.    }
  329.    if(!g_IsMonitoring)
  330.    {
  331.       g_IsMonitoring = true;
  332.       g_MonitorLevel = InpMonitorStart;
  333.       g_PeakProfit = totalProfit;
  334.       Print("动态止盈启动: 盈利=", totalProfit);
  335.       return;
  336.    }
  337.    if(totalProfit > g_PeakProfit)
  338.    {
  339.       g_PeakProfit = totalProfit;
  340.       int newLevel = (int)(g_PeakProfit / InpMonitorStep) * InpMonitorStep;
  341.       if(newLevel > g_MonitorLevel)
  342.       {
  343.          g_MonitorLevel = newLevel;
  344.          Print("动态止盈升级: 监控=", g_MonitorLevel);
  345.       }
  346.    }
  347.    double drawback = (g_PeakProfit - totalProfit) / g_PeakProfit * 100;
  348.    if(drawback >= InpDrawbackPercent && g_PeakProfit > 0)
  349.    {
  350.       Print("触发动态止盈: 峰值=", g_PeakProfit, " 当前=", totalProfit, " 回撤=", drawback, "%");
  351.       CloseAllPositions();
  352.       g_IsMonitoring = false;
  353.       g_MonitorLevel = 0;
  354.       g_PeakProfit = 0;
  355.    }
  356. }
  357. //+------------------------------------------------------------------+
  358. //| 获取当前总盈利                                                    |
  359. //+------------------------------------------------------------------+
  360. double GetTotalProfit()
  361. {
  362.    double total = 0;
  363.    for(int i = PositionsTotal()-1; i >= 0; i--)
  364.    {
  365.       ulong ticket = PositionGetTicket(i);
  366.       if(PositionSelectByTicket(ticket))
  367.          if(PositionGetString(POSITION_SYMBOL) == g_Symbol && PositionGetInteger(POSITION_MAGIC) == g_MagicNumber)
  368.             total += PositionGetDouble(POSITION_PROFIT);
  369.    }
  370.    return total;
  371. }
  372. //+------------------------------------------------------------------+
  373. //| 硬止损止盈                                                        |
  374. //+------------------------------------------------------------------+
  375. void CheckHardSLTP()
  376. {
  377.    double totalProfit = GetTotalProfit();
  378.    if(totalProfit <= -InpHardStopLoss && InpHardStopLoss > 0)
  379.    {
  380.       Print("触发硬止损: ", totalProfit);
  381.       CloseAllPositions();
  382.       g_IsMonitoring = false;
  383.       g_MonitorLevel = 0;
  384.       g_PeakProfit = 0;
  385.    }
  386.    if(totalProfit >= InpHardTakeProfit && InpHardTakeProfit > 0)
  387.    {
  388.       Print("触发硬止盈: ", totalProfit);
  389.       CloseAllPositions();
  390.       g_IsMonitoring = false;
  391.       g_MonitorLevel = 0;
  392.       g_PeakProfit = 0;
  393.    }
  394. }
  395. //+------------------------------------------------------------------+
  396. //| 平仓所有                                                          |
  397. //+------------------------------------------------------------------+
  398. void CloseAllPositions()
  399. {
  400.    for(int i = PositionsTotal()-1; i >= 0; i--)
  401.    {
  402.       ulong ticket = PositionGetTicket(i);
  403.       if(PositionSelectByTicket(ticket))
  404.       {
  405.          if(PositionGetString(POSITION_SYMBOL) == g_Symbol && PositionGetInteger(POSITION_MAGIC) == g_MagicNumber)
  406.          {
  407.             MqlTradeRequest request = {};
  408.             MqlTradeResult result = {};
  409.             request.action = TRADE_ACTION_DEAL;
  410.             request.symbol = g_Symbol;
  411.             request.volume = PositionGetDouble(POSITION_VOLUME);
  412.             request.deviation = 10;
  413.             request.magic = g_MagicNumber;
  414.             request.comment = g_EAComment + "_close";
  415.             request.type_filling = GetFillingMode();
  416.             if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
  417.             {
  418.                request.type = ORDER_TYPE_SELL;
  419.                request.price = SymbolInfoDouble(g_Symbol, SYMBOL_BID);
  420.             }
  421.             else
  422.             {
  423.                request.type = ORDER_TYPE_BUY;
  424.                request.price = SymbolInfoDouble(g_Symbol, SYMBOL_ASK);
  425.             }
  426.             if(!OrderSend(request, result))
  427.                Print("平仓失败: ", result.retcode);
  428.          }
  429.       }
  430.    }
  431. }
  432. //+------------------------------------------------------------------+
  433. //| 反转平仓(RSI)                                                   |
  434. //+------------------------------------------------------------------+
  435. void CheckReverseClose()
  436. {
  437.    double rsiBuffer[];
  438.    ArraySetAsSeries(rsiBuffer, true);
  439.    int rsiHandle = iRSI(g_Symbol, PERIOD_CURRENT, InpRSIPeriod, PRICE_CLOSE);
  440.    if(rsiHandle == INVALID_HANDLE) return;
  441.    if(CopyBuffer(rsiHandle, 0, 0, 3, rsiBuffer) < 3) { IndicatorRelease(rsiHandle); return; }
  442.    
  443.    bool hasBuy = false, hasSell = false;
  444.    for(int i = PositionsTotal()-1; i >= 0; i--)
  445.    {
  446.       ulong ticket = PositionGetTicket(i);
  447.       if(PositionSelectByTicket(ticket))
  448.          if(PositionGetString(POSITION_SYMBOL) == g_Symbol && PositionGetInteger(POSITION_MAGIC) == g_MagicNumber)
  449.          {
  450.             if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) hasBuy = true;
  451.             else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) hasSell = true;
  452.          }
  453.    }
  454.    if(hasBuy && rsiBuffer[1] >= InpRSIOverbought && rsiBuffer[0] < InpRSIOverbought)
  455.    {
  456.       Print("RSI反转(超买下跌),平多");
  457.       CloseAllPositions();
  458.       g_IsMonitoring = false;
  459.       g_MonitorLevel = 0;
  460.       g_PeakProfit = 0;
  461.       IndicatorRelease(rsiHandle);
  462.       return;
  463.    }
  464.    if(hasSell && rsiBuffer[1] <= InpRSIOversold && rsiBuffer[0] > InpRSIOversold)
  465.    {
  466.       Print("RSI反转(超卖上涨),平空");
  467.       CloseAllPositions();
  468.       g_IsMonitoring = false;
  469.       g_MonitorLevel = 0;
  470.       g_PeakProfit = 0;
  471.       IndicatorRelease(rsiHandle);
  472.       return;
  473.    }
  474.    IndicatorRelease(rsiHandle);
  475. }
  476. //+------------------------------------------------------------------+
  477. //| 解析倍投序列                                                      |
  478. //+------------------------------------------------------------------+
  479. bool ParseMartingaleSeries(string series, double &output[])
  480. {
  481.    string parts[];
  482.    ushort sep = ',';
  483.    int count = StringSplit(series, sep, parts);
  484.    if(count <= 0) return false;
  485.    ArrayResize(output, count);
  486.    for(int i = 0; i < count; i++)
  487.    {
  488.       StringTrimLeft(parts[i]);
  489.       StringTrimRight(parts[i]);
  490.       output[i] = StringToDouble(parts[i]);
  491.       if(output[i] <= 0) return false;
  492.    }
  493.    return true;
  494. }
  495. //+------------------------------------------------------------------+
  496. //| UI创建(左上角仅文字,右上角箱体标签由UpdateBox管理)          |
  497. //+------------------------------------------------------------------+
  498. void CreateUI()
  499. {
  500.    // ========== 标题 LOGO(居中) ==========
  501.    string titleName = g_UIPrefix + "TITLE";
  502.    ObjectCreate(0, titleName, OBJ_LABEL, 0, 0, 0);
  503.    ObjectSetInteger(0, titleName, OBJPROP_ANCHOR, ANCHOR_CENTER);
  504.    ObjectSetInteger(0, titleName, OBJPROP_XDISTANCE, 0);
  505.    ObjectSetInteger(0, titleName, OBJPROP_YDISTANCE, 0);
  506.    ObjectSetString(0, titleName, OBJPROP_TEXT, "★ 蓝小凡精准狙击 ★");
  507.    ObjectSetInteger(0, titleName, OBJPROP_COLOR, clrGold);
  508.    ObjectSetInteger(0, titleName, OBJPROP_FONTSIZE, 24);
  509.    ObjectSetString(0, titleName, OBJPROP_FONT, "Arial Black");
  510.    ObjectSetInteger(0, titleName, OBJPROP_BACK, false);
  511.    ObjectSetInteger(0, titleName, OBJPROP_SELECTABLE, false);
  512.    ObjectSetInteger(0, titleName, OBJPROP_ZORDER, 100);
  513.    // ========== 左上角信息(仅文字,无背景,无边框) ==========
  514.    string posLabel = g_UIPrefix + "POS";
  515.    ObjectCreate(0, posLabel, OBJ_LABEL, 0, 0, 0);
  516.    ObjectSetInteger(0, posLabel, OBJPROP_XDISTANCE, 15);
  517.    ObjectSetInteger(0, posLabel, OBJPROP_YDISTANCE, 50);
  518.    ObjectSetString(0, posLabel, OBJPROP_TEXT, "持仓: 0");
  519.    ObjectSetInteger(0, posLabel, OBJPROP_COLOR, clrWhite);
  520.    ObjectSetInteger(0, posLabel, OBJPROP_FONTSIZE, InpFontSize);
  521.    ObjectSetString(0, posLabel, OBJPROP_FONT, "Arial");
  522.    ObjectSetInteger(0, posLabel, OBJPROP_BACK, false);
  523.    ObjectSetInteger(0, posLabel, OBJPROP_SELECTABLE, false);
  524.    
  525.    string profitLabel = g_UIPrefix + "PROFIT";
  526.    ObjectCreate(0, profitLabel, OBJ_LABEL, 0, 0, 0);
  527.    ObjectSetInteger(0, profitLabel, OBJPROP_XDISTANCE, 15);
  528.    ObjectSetInteger(0, profitLabel, OBJPROP_YDISTANCE, 75);
  529.    ObjectSetString(0, profitLabel, OBJPROP_TEXT, "当前盈利: $0.00");
  530.    ObjectSetInteger(0, profitLabel, OBJPROP_COLOR, clrWhite);
  531.    ObjectSetInteger(0, profitLabel, OBJPROP_FONTSIZE, InpFontSize);
  532.    ObjectSetString(0, profitLabel, OBJPROP_FONT, "Arial");
  533.    ObjectSetInteger(0, profitLabel, OBJPROP_BACK, false);
  534.    ObjectSetInteger(0, profitLabel, OBJPROP_SELECTABLE, false);
  535.    
  536.    string totalLabel = g_UIPrefix + "TOTAL";
  537.    ObjectCreate(0, totalLabel, OBJ_LABEL, 0, 0, 0);
  538.    ObjectSetInteger(0, totalLabel, OBJPROP_XDISTANCE, 15);
  539.    ObjectSetInteger(0, totalLabel, OBJPROP_YDISTANCE, 100);
  540.    ObjectSetString(0, totalLabel, OBJPROP_TEXT, "总计盈利: $0.00");
  541.    ObjectSetInteger(0, totalLabel, OBJPROP_COLOR, clrWhite);
  542.    ObjectSetInteger(0, totalLabel, OBJPROP_FONTSIZE, InpFontSize);
  543.    ObjectSetString(0, totalLabel, OBJPROP_FONT, "Arial");
  544.    ObjectSetInteger(0, totalLabel, OBJPROP_BACK, false);
  545.    ObjectSetInteger(0, totalLabel, OBJPROP_SELECTABLE, false);
  546.    
  547.    ChartRedraw(0);
  548. }
  549. //+------------------------------------------------------------------+
  550. //| UI更新(标题居中,数据刷新)                                      |
  551. //+------------------------------------------------------------------+
  552. void UpdateUI()
  553. {
  554.    // --- 1. 标题位置(始终居中) ---
  555.    int chartWidth = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
  556.    int chartHeight = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
  557.    ObjectSetInteger(0, g_UIPrefix + "TITLE", OBJPROP_XDISTANCE, chartWidth / 2);
  558.    ObjectSetInteger(0, g_UIPrefix + "TITLE", OBJPROP_YDISTANCE, chartHeight / 2);
  559.    
  560.    // --- 2. 持仓 ---
  561.    int buyCount = 0, sellCount = 0;
  562.    for(int i = PositionsTotal()-1; i >= 0; i--)
  563.    {
  564.       ulong ticket = PositionGetTicket(i);
  565.       if(PositionSelectByTicket(ticket))
  566.          if(PositionGetString(POSITION_SYMBOL) == g_Symbol && PositionGetInteger(POSITION_MAGIC) == g_MagicNumber)
  567.          {
  568.             if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) buyCount++;
  569.             else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) sellCount++;
  570.          }
  571.    }
  572.    ObjectSetString(0, g_UIPrefix + "POS", OBJPROP_TEXT, "持仓: 多" + IntegerToString(buyCount) + " 空" + IntegerToString(sellCount));
  573.    
  574.    // --- 3. 当前盈利 ---
  575.    double profit = GetTotalProfit();
  576.    string profitText = "当前盈利: [        DISCUZ_CODE_0        ]quot; + DoubleToString(profit, 2);
  577.    ObjectSetString(0, g_UIPrefix + "PROFIT", OBJPROP_TEXT, profitText);
  578.    ObjectSetInteger(0, g_UIPrefix + "PROFIT", OBJPROP_COLOR, (profit >= 0) ? clrLime : clrRed);
  579.    
  580.    // --- 4. 总计盈利 ---
  581.    double totalProfit = GetHistoryProfit();
  582.    string totalText = "总计盈利: [        DISCUZ_CODE_0        ]quot; + DoubleToString(totalProfit, 2);
  583.    ObjectSetString(0, g_UIPrefix + "TOTAL", OBJPROP_TEXT, totalText);
  584.    ObjectSetInteger(0, g_UIPrefix + "TOTAL", OBJPROP_COLOR, (totalProfit >= 0) ? clrLime : clrRed);
  585. }
  586. //+------------------------------------------------------------------+
  587. //| 获取历史总计盈利                                                  |
  588. //+------------------------------------------------------------------+
  589. double GetHistoryProfit()
  590. {
  591.    double total = 0;
  592.    HistorySelect(0, TimeCurrent());
  593.    for(int i = HistoryDealsTotal()-1; i >= 0; i--)
  594.    {
  595.       ulong ticket = HistoryDealGetTicket(i);
  596.       if(HistoryDealGetString(ticket, DEAL_SYMBOL) == g_Symbol &&
  597.          HistoryDealGetInteger(ticket, DEAL_MAGIC) == g_MagicNumber)
  598.          total += HistoryDealGetDouble(ticket, DEAL_PROFIT);
  599.    }
  600.    return total;
  601. }
  602. //+------------------------------------------------------------------+
  603. //| 实时状态输出(专家栏)                                           |
  604. //+------------------------------------------------------------------+
  605. void LogStatus()
  606. {
  607.    string status = "========== 蓝小凡精准狙击 状态 ==========";
  608.    status += "\n品种: " + g_Symbol;
  609.    status += "\n箱体有效: " + (g_BoxValid ? "是" : "否") +
  610.              (g_BoxValid ? (" 高点=" + DoubleToString(g_BoxHigh, _Digits) +
  611.                             " 低点=" + DoubleToString(g_BoxLow, _Digits)) : "");
  612.    status += "\n当前浮动盈利: [        DISCUZ_CODE_0        ]quot; + DoubleToString(GetTotalProfit(), 2);
  613.    status += "\n硬止损: [        DISCUZ_CODE_0        ]quot; + DoubleToString(InpHardStopLoss, 2) +
  614.              "  硬止盈: [        DISCUZ_CODE_0        ]quot; + DoubleToString(InpHardTakeProfit, 2);
  615.    status += "\n动态止盈监控: " + (g_IsMonitoring ? "开启" : "关闭") +
  616.              (g_IsMonitoring ? (" 监控阈值=" + DoubleToString(g_MonitorLevel, 2) +
  617.                                 " 峰值=" + DoubleToString(g_PeakProfit, 2)) : "");
  618.    
  619.    int buyCount = 0, sellCount = 0;
  620.    double buyVol = 0, sellVol = 0;
  621.    for(int i = PositionsTotal()-1; i >= 0; i--)
  622.    {
  623.       ulong ticket = PositionGetTicket(i);
  624.       if(PositionSelectByTicket(ticket))
  625.          if(PositionGetString(POSITION_SYMBOL) == g_Symbol && PositionGetInteger(POSITION_MAGIC) == g_MagicNumber)
  626.          {
  627.             if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
  628.             { buyCount++; buyVol += PositionGetDouble(POSITION_VOLUME); }
  629.             else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
  630.             { sellCount++; sellVol += PositionGetDouble(POSITION_VOLUME); }
  631.          }
  632.    }
  633.    status += "\n持仓: 多" + IntegerToString(buyCount) + "手(" + DoubleToString(buyVol, 2) +
  634.              ") 空" + IntegerToString(sellCount) + "手(" + DoubleToString(sellVol, 2) + ")";
  635.    status += "\n加仓次数: " + IntegerToString(g_AddCount) +
  636.              "  上次加仓间隔: " + IntegerToString((int)((TimeCurrent()-g_LastAddTime)/60)) + " 分钟";
  637.    status += "\n==========================================";
  638.    Print(status);
  639. }
  640. //+------------------------------------------------------------------+
复制代码
如果有帮助,就支持一下我呗
最近访问 头像模式
举报

评论 使用道具

热门主题

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册