|
请帮我修复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); } //+------------------------------------------------------------------+ |
指标发布