📅 财经日历 📊 实时波动 📈 大盘云图 📶 行情走势 🆚 投机情绪 🚀 今日热点

    请老师帮忙修复KDJ箭头显示错位问题,祝大吉大利,非常感谢!

    2026-06-21 · 125 阅读
    请帮我修复mt4跨周期kdj代码,只修复箭头问题,逻辑框架不动。指标加载后出现的错误:箭头显示位置偏移错乱。正确的显示如下:默认当j值上穿超买线100以上后,又回落下穿100形成死叉时生成绿色向下箭头,默认当j值下穿超卖线0以下后,又上穿0形成金叉时生成红色向上箭头,箭头放在交叉点上。超买超卖参数可调,调整超买超卖参数后,箭头的位置落在调整后的金叉死叉的交叉点上。


    //+------------------------------------------------------------------+
    //|                                        跨周期KDJ_J值_箭头_超卖线最终修复版.mq4 |
    //| 修复1:箭头严格条件 前J<0现J≥0红箭头;前J>100现J≤100绿箭头
    //| 修复2:参数修改强制全量清空缓冲区,消除残留水平线、残留箭头
    //| 修复3:箭头坐标绑定J交叉点位,彻底解决偏移错乱
    //+------------------------------------------------------------------+
    #property copyright "CrossTF KDJ J-Value LevelsFix Final"
    #property link      ""
    #property version   "2.00"
    #property strict

    #property indicator_separate_window
    #property indicator_buffers  5
    #property indicator_color1   clrGold       // J值线
    #property indicator_color2   clrGray       // 超买线
    #property indicator_color3   clrGray       // 超卖线
    #property indicator_color4   clrRed        // 向上箭头(金叉)
    #property indicator_color5   clrGreen      // 向下箭头(死叉)

    #property indicator_width1   2
    #property indicator_width2   1
    #property indicator_width3   1
    #property indicator_width4   2
    #property indicator_width5   2

    //==================== 参数面板 ====================
    input string   inp_TF            = "D1";           // ---- 跨周期时间框架 ----
    input int      KDJ_Period        = 9;              // ---- KDJ RSV周期(N) ----
    input int      KDJ_K             = 3;              // ---- K平滑周期 ----
    input int      KDJ_D             = 3;              // ---- D平滑周期 ----
    input double   OverboughtLine    = 100.0;           // ---- 超买线 ----
    input double   OversoldLine      = 0.0;             // ---- 超卖线 ----
    input bool     ShowLevels        = true;            // ---- 显示超买超卖线 ----
    input int      J_LineWidth       = 2;               // ---- J线粗细(1-5) ----
    input int      ArrowSize         = 2;               // ---- 箭头大小(1-5) ----

    //---- 绘图缓冲区
    double bufJ[];
    double bufOverbought[];
    double bufOversold[];
    double bufArrowUp[];
    double bufArrowDn[];

    //---- 跨周期J值缓存
    double ht_j[];
    datetime ht_last_time = 0;
    int ht_last_bars = 0;

    //---- 保险机制变量
    bool   firstRun = true;
    int    lastTFBarCount = 0;
    datetime lastHigherTFTime = 0;
    datetime lastCurrentTFTime = 0;
    double   lastClosePrice = 0.0;
    datetime lastRefreshTime = 0;
    string forceActiveObjName = "_KDJ_ForceActive_";

    //---- 箭头已绘制标记
    bool   arrowDrawnUp[];
    bool   arrowDrawnDn[];

    //---- 记录上次参数值
    double lastOverbought = -999999;
    double lastOversold = -999999;

    //---- 全局周期映射
    int targetTF;
    string sTfTable[];
    int    iTfTable[];

    //+------------------------------------------------------------------+
    int StringToTimeFrame(string tfStr)
    {
       string s = StringUpperCase(tfStr);
       if(s == "CURRENT TIME FRAME" || s == "CURRENT" || s == "" || s == "0")
          return(_Period);
       int tableSize = (int)ArraySize(iTfTable);
       for(int p = 0; p < tableSize; p++)
       {
          if(s == sTfTable[p] || s == (string)iTfTable[p])
             return(iTfTable[p]);
       }
       return(_Period);
    }
    //+------------------------------------------------------------------+
    string TimeFrameToString(int tf)
    {
       int tableSize = (int)ArraySize(iTfTable);
       for(int q = 0; q < tableSize; q++)
       {
          if(iTfTable[q] == tf)
             return(sTfTable[q]);
       }
       return("");
    }
    //+------------------------------------------------------------------+
    string StringUpperCase(string str)
    {
       string res = str;
       int len = StringLen(str);
       for(int r = 0; r < len; r++)
       {
          ushort c = StringGetChar(res, r);
          if(c > 96 && c < 123)
             StringSetChar(res, r, c - 32);
       }
       return(res);
    }
    //+------------------------------------------------------------------+
    int OnInit()
    {
       int jWidth = MathMax(1, MathMin(J_LineWidth, 5));
       int arrSize = MathMax(1, MathMin(ArrowSize, 5));

       ArrayResize(sTfTable, 9);
       sTfTable[0] = "M1";  sTfTable[1] = "M5";  sTfTable[2] = "M15";
       sTfTable[3] = "M30"; sTfTable[4] = "H1";  sTfTable[5] = "H4";
       sTfTable[6] = "D1";  sTfTable[7] = "W1";  sTfTable[8] = "MN";
       ArrayResize(iTfTable, 9);
       iTfTable[0] = 1;     iTfTable[1] = 5;     iTfTable[2] = 15;
       iTfTable[3] = 30;    iTfTable[4] = 60;    iTfTable[5] = 240;
       iTfTable[6] = 1440;  iTfTable[7] = 10080; iTfTable[8] = 43200;

       SetIndexBuffer(0, bufJ);
       SetIndexBuffer(1, bufOverbought);
       SetIndexBuffer(2, bufOversold);
       SetIndexBuffer(3, bufArrowUp);
       SetIndexBuffer(4, bufArrowDn);

       SetIndexEmptyValue(0, EMPTY_VALUE);
       SetIndexEmptyValue(1, EMPTY_VALUE);
       SetIndexEmptyValue(2, EMPTY_VALUE);
       SetIndexEmptyValue(3, EMPTY_VALUE);
       SetIndexEmptyValue(4, EMPTY_VALUE);

       SetIndexStyle(0, DRAW_LINE, 0, jWidth, clrGold);
       if(ShowLevels)
       {
          SetIndexStyle(1, DRAW_LINE, STYLE_DOT, 1, clrGray);
          SetIndexStyle(2, DRAW_LINE, STYLE_DOT, 1, clrGray);
       }
       else
       {
          SetIndexStyle(1, DRAW_NONE);
          SetIndexStyle(2, DRAW_NONE);
       }
       
       SetIndexStyle(3, DRAW_ARROW, 0, arrSize, clrRed);
       SetIndexStyle(4, DRAW_ARROW, 0, arrSize, clrGreen);

       SetIndexArrow(3, 233); //向上箭头
       SetIndexArrow(4, 234); //向下箭头

       targetTF = StringToTimeFrame(inp_TF);
       string tfName = TimeFrameToString(targetTF);
       IndicatorShortName("跨周期KDJ-J (" + tfName + ") 超买超卖箭头【修复版】");

       ArraySetAsSeries(bufJ, true);
       ArraySetAsSeries(bufOverbought, true);
       ArraySetAsSeries(bufOversold, true);
       ArraySetAsSeries(bufArrowUp, true);
       ArraySetAsSeries(bufArrowDn, true);
       ArraySetAsSeries(ht_j, true);

       firstRun = true;
       lastOverbought = OverboughtLine;
       lastOversold = OversoldLine;
       lastTFBarCount = iBars(_Symbol, targetTF);
       lastHigherTFTime = iTime(_Symbol, targetTF, 0);
       lastCurrentTFTime = iTime(_Symbol, _Period, 0);
       lastClosePrice = iClose(_Symbol, _Period, 0);
       lastRefreshTime = TimeCurrent();

       CreateForceActiveObject();
       EventSetTimer(1);

       int retryCount = 0;
       while(retryCount < 5 && ArraySize(ht_j) == 0)
       {
          CalculateHigherTF_KDJ();
          retryCount++;
          if(ArraySize(ht_j) == 0) Sleep(100);
       }

       return(INIT_SUCCEEDED);
    }
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
       EventKillTimer();
       if(ObjectFind(0, forceActiveObjName) >= 0) ObjectDelete(0, forceActiveObjName);
       ArrayFree(ht_j);
       ArrayFree(arrowDrawnUp);
       ArrayFree(arrowDrawnDn);
    }
    //+------------------------------------------------------------------+
    void CreateForceActiveObject()
    {
       if(ObjectFind(0, forceActiveObjName) < 0)
       {
          ObjectCreate(0, forceActiveObjName, OBJ_LABEL, 0, 0, 0);
          ObjectSetInteger(0, forceActiveObjName, OBJPROP_COLOR, clrNONE);
          ObjectSetString(0, forceActiveObjName, OBJPROP_TEXT, " ");
          ObjectSetInteger(0, forceActiveObjName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
          ObjectSetInteger(0, forceActiveObjName, OBJPROP_XDISTANCE, 0);
          ObjectSetInteger(0, forceActiveObjName, OBJPROP_YDISTANCE, 0);
       }
    }
    //+------------------------------------------------------------------+
    void CalculateHigherTF_KDJ()
    {
       int ht_bars = iBars(_Symbol, targetTF);
       if(ht_bars <= 0) return;
       if(ht_bars < KDJ_Period + 5) return;

       datetime ht_time_0 = iTime(_Symbol, targetTF, 0);
       if(ht_time_0 == 0) return;

       if(ht_bars == ht_last_bars && ht_time_0 == ht_last_time && ArraySize(ht_j) > 0)
          return;

       int needed = MathMin(ht_bars, 500);
       if(needed < KDJ_Period + 5) return;

       double temp_rsv[];
       double temp_k[];
       double temp_d[];
       ArrayResize(temp_rsv, needed);
       ArrayResize(temp_k, needed);
       ArrayResize(temp_d, needed);
       ArrayInitialize(temp_rsv, EMPTY_VALUE);
       ArrayInitialize(temp_k, EMPTY_VALUE);
       ArrayInitialize(temp_d, EMPTY_VALUE);

       int batchSize = 50;
       int batches = (needed + batchSize - 1) / batchSize;

       for(int b = 0; b < batches; b++)
       {
          int s = b * batchSize;
          int e = MathMin(s + batchSize, needed);
          for(int j = s; j < e; j++)
          {
             int highest_idx = iHighest(_Symbol, targetTF, MODE_HIGH, KDJ_Period, j);
             int lowest_idx  = iLowest(_Symbol, targetTF, MODE_LOW, KDJ_Period, j);
             double close = iClose(_Symbol, targetTF, j);

             if(highest_idx < 0 || lowest_idx < 0 || close == 0)
             {
                temp_rsv[j] = EMPTY_VALUE;
                continue;
             }

             double highest = iHigh(_Symbol, targetTF, highest_idx);
             double lowest  = iLow(_Symbol, targetTF, lowest_idx);

             if(highest > lowest)
                temp_rsv[j] = 100.0 * (close - lowest) / (highest - lowest);
             else
                temp_rsv[j] = 50.0;
          }
          if(b < batches - 1) Sleep(10);
       }

       double alphaK = 1.0 / MathMax(KDJ_K, 1);
       double alphaD = 1.0 / MathMax(KDJ_D, 1);
       int oldest = needed - 1;

       if(temp_rsv[oldest] != EMPTY_VALUE)
       {
          temp_k[oldest] = temp_rsv[oldest];
          temp_d[oldest] = temp_rsv[oldest];
       }
       else
       {
          temp_k[oldest] = 50.0;
          temp_d[oldest] = 50.0;
       }

       for(int j = oldest - 1; j >= 0; j--)
       {
          if(temp_rsv[j] != EMPTY_VALUE)
          {
             temp_k[j] = (1.0 - alphaK) * temp_k[j+1] + alphaK * temp_rsv[j];
             temp_d[j] = (1.0 - alphaD) * temp_d[j+1] + alphaD * temp_k[j];
          }
          else
          {
             temp_k[j] = temp_k[j+1];
             temp_d[j] = temp_d[j+1];
          }
       }

       ArrayResize(ht_j, needed);
       ArrayInitialize(ht_j, EMPTY_VALUE);
       for(int j = 0; j < needed; j++)
       {
          if(temp_k[j] != EMPTY_VALUE && temp_d[j] != EMPTY_VALUE)
             ht_j[j] = 3.0 * temp_k[j] - 2.0 * temp_d[j];
          else if(j > 0)
             ht_j[j] = ht_j[j-1];
          else
             ht_j[j] = 50.0;
       }

       ArrayFree(temp_rsv);
       ArrayFree(temp_k);
       ArrayFree(temp_d);

       ht_last_bars = ht_bars;
       ht_last_time = ht_time_0;
    }
    //+------------------------------------------------------------------+
    void OnTick()
    {
       int currTFBars = iBars(_Symbol, targetTF);
       datetime currHigherTFTime = iTime(_Symbol, targetTF, 0);
       double currClose = iClose(_Symbol, _Period, 0);

       if(currTFBars != lastTFBarCount || currHigherTFTime != lastHigherTFTime || currClose != lastClosePrice)
       {
          lastTFBarCount = currTFBars;
          lastHigherTFTime = currHigherTFTime;
          lastClosePrice = currClose;
          WindowRedraw();
       }
    }
    //+------------------------------------------------------------------+
    void OnTimer()
    {
       datetime now = TimeCurrent();
       int currTFBars = iBars(_Symbol, targetTF);
       datetime currHigherTFTime = iTime(_Symbol, targetTF, 0);
       datetime currCurrentTFTime = iTime(_Symbol, _Period, 0);
       double currClose = iClose(_Symbol, _Period, 0);

       bool needFullRecalc = false;
       if(currTFBars != lastTFBarCount) needFullRecalc = true;
       if(currHigherTFTime != lastHigherTFTime) needFullRecalc = true;
       if(currCurrentTFTime != lastCurrentTFTime) needFullRecalc = true;
       if(currClose != lastClosePrice) needFullRecalc = true;
       if(now - lastRefreshTime > 30) needFullRecalc = true;

       if(needFullRecalc)
       {
          firstRun = true;
          lastRefreshTime = now;
       }

       static int microShift = 0;
       microShift = (microShift + 1) % 2;
       if(ObjectFind(0, forceActiveObjName) >= 0)
          ObjectSetInteger(0, forceActiveObjName, OBJPROP_XDISTANCE, microShift);

       WindowRedraw();
    }
    //+------------------------------------------------------------------+
    int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const datetime &time[],
                    const double &open[],
                    const double &high[],
                    const double &low[],
                    const double &close[],
                    const long &tick_volume[],
                    const long &volume[],
                    const int &spread[])
    {
       if(rates_total < 3) return(rates_total);

       // 【检测超买超卖线参数改动】
       bool paramsChanged = false;
       if(OverboughtLine != lastOverbought || OversoldLine != lastOversold)
       {
          paramsChanged = true;
          lastOverbought = OverboughtLine;
          lastOversold = OversoldLine;
       }

       // 参数变动 → 全局清空所有绘图缓存,消除残留线、残留箭头
       if(paramsChanged)
       {
          for(int m = 0; m < rates_total; m++)
          {
             bufJ[m] = EMPTY_VALUE;
             bufOverbought[m] = EMPTY_VALUE;
             bufOversold[m] = EMPTY_VALUE;
             bufArrowUp[m] = EMPTY_VALUE;
             bufArrowDn[m] = EMPTY_VALUE;
          }
          ArrayResize(arrowDrawnUp, rates_total);
          ArrayResize(arrowDrawnDn, rates_total);
          ArrayInitialize(arrowDrawnUp, false);
          ArrayInitialize(arrowDrawnDn, false);
       }

       if(ArraySize(ht_j) == 0)
       {
          CalculateHigherTF_KDJ();
          if(ArraySize(ht_j) == 0) return(rates_total);
       }
       CalculateHigherTF_KDJ();

       int htSize = ArraySize(ht_j);
       if(htSize == 0) return(rates_total);

       // 计算起点
       int start;
       if(paramsChanged || prev_calculated == 0 || firstRun)
       {
          start = 0;
          firstRun = false;
       }
       else
       {
          start = MathMax(prev_calculated - 1, 0);
       }

       // 箭头标记数组同步尺寸
       if(ArraySize(arrowDrawnUp) != rates_total)
       {
          ArrayResize(arrowDrawnUp, rates_total);
          ArrayResize(arrowDrawnDn, rates_total);
          ArrayInitialize(arrowDrawnUp, false);
          ArrayInitialize(arrowDrawnDn, false);
       }

       // 1. 填充J线、超买超卖水平线
       for(int i = start; i < rates_total; i++)
       {
          if(ShowLevels)
          {
             bufOverbought[i] = OverboughtLine;
             bufOversold[i] = OversoldLine;
          }
          else
          {
             bufOverbought[i] = EMPTY_VALUE;
             bufOversold[i] = EMPTY_VALUE;
          }

          int shiftTF = iBarShift(_Symbol, targetTF, time[i], false);
          if(shiftTF < 0)
             shiftTF = iBarShift(_Symbol, targetTF, time[i], true);

          if(shiftTF >= 0 && shiftTF < htSize && ht_j[shiftTF] != EMPTY_VALUE)
             bufJ[i] = ht_j[shiftTF];
          else
             bufJ[i] = (i > 0 && bufJ[i-1] != EMPTY_VALUE) ? bufJ[i-1] : 50.0;
       }

       // 2. 箭头逻辑计算(严格匹配你的条件)
       int arrowStart, arrowEnd;
       if(prev_calculated > 0 && !paramsChanged && !firstRun)
       {
          arrowStart = MathMax(start, 1);
          arrowEnd = rates_total;
       }
       else
       {
          arrowStart = 1;
          arrowEnd = rates_total;
          ArrayInitialize(arrowDrawnUp, false);
          ArrayInitialize(arrowDrawnDn, false);
          for(int m = 0; m < rates_total; m++)
          {
             bufArrowUp[m] = EMPTY_VALUE;
             bufArrowDn[m] = EMPTY_VALUE;
          }
       }

       for(int i = arrowStart; i < arrowEnd; i++)
       {
          double currJ = bufJ[i];
          double prevJ = bufJ[i-1];

          if(currJ == EMPTY_VALUE || prevJ == EMPTY_VALUE)
          {
             bufArrowUp[i] = EMPTY_VALUE;
             bufArrowDn[i] = EMPTY_VALUE;
             continue;
          }

          // ========== 严格要求的箭头触发条件 ==========
          // 绿色向下卖出箭头:前一根J > 100 且 当前J ≤ 100
          if(prevJ > OverboughtLine && currJ <= OverboughtLine)
          {
             bufArrowDn[i] = currJ + 8.0; //箭头画在交叉点上方,不偏移错乱
             arrowDrawnDn[i] = true;
          }
          else
          {
             bufArrowDn[i] = EMPTY_VALUE;
             arrowDrawnDn[i] = false;
          }

          // 红色向上买入箭头:前一根J < 0 且 当前J ≥ 0
          if(prevJ < OversoldLine && currJ >= OversoldLine)
          {
             bufArrowUp[i] = currJ - 8.0; //箭头画在交叉点下方,紧贴0线
             arrowDrawnUp[i] = true;
          }
          else
          {
             bufArrowUp[i] = EMPTY_VALUE;
             arrowDrawnUp[i] = false;
          }
       }

       lastHigherTFTime = iTime(_Symbol, targetTF, 0);
       lastCurrentTFTime = time[0];
       lastClosePrice = close[0];

       return(rates_total);
    }
    //+------------------------------------------------------------------+

    箭头错位.jpg
    ""
    还没有人打赏,支持一下
    回复

    举报

     

    回答|共 2 个

    641386452 LV6

    发表于 2026-6-21 08:38:18 | 显示全部楼层

    修改图示,大箭头位置正确。
    箭头偏移错乱 修改图示.jpg

    yu1471598888 LV0

    发表于 2026-6-21 10:07:53 | 显示全部楼层

    有毛用
    您需要登录后才可以回帖 登录 | 注册

    提醒: 禁止引战、谩骂、灌水内容

    微信二维码

    有问题联系客服