缠论源码-笔 一、 分型以上因人而异,这次分享一个新笔(MT4版)源码,以此支持更多缠迷朋友创作:#property strict #property indicator_chart_window #property indicator_buffers 2 #property indicator_color1 clrRed #property indicator_color2 clrLime #property indicator_width1 2 #property indicator_width2 2 #property indicator_style1 STYLE_SOLID #property indicator_style2 STYLE_SOLID enum MERGE_DIRECTION { MERGE_NONE, MERGE_HIGH_HIGH, MERGE_LOW_LOW }; enum STROKE_MODE { MODE_X }; double upStrokeBuffer[]; double downStrokeBuffer[]; struct FractalPoint { datetime time; double price; bool isTop; int barIndex; int mergedIndex; double fractalHigh; double fractalLow; }; struct MergedCandle { double high; double low; datetime time; bool isMerged; datetime highTime; datetime lowTime; int highBarIndex; int lowBarIndex; }; MergedCandle mergedCandles[]; FractalPoint fractalPoints[]; FractalPoint validFractals[]; FractalPoint finalFractals[]; int strokeStartIndex = -1; int OnInit() { IndicatorShortName("MT4缠论新笔"); SetIndexBuffer(0, upStrokeBuffer); SetIndexBuffer(1, downStrokeBuffer); SetIndexStyle(0, DRAW_LINE); SetIndexStyle(1, DRAW_LINE); SetIndexLabel(0, "上升笔"); SetIndexLabel(1, "下降笔"); return(INIT_SUCCEEDED); } 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[]) { ArrayInitialize(upStrokeBuffer, EMPTY_VALUE); ArrayInitialize(downStrokeBuffer, EMPTY_VALUE); int mergedCount = ProcessMergedCandles(rates_total, high, low, time); IdentifyFractals(mergedCount); FilterFractals(); ProcessStrokesFractals(high, low); IdentifyAndDrawStrokes(rates_total, time); return(rates_total); } int ProcessMergedCandles(int rates_total, const double& high[], const double& low[], const datetime& time[]) { int allocSize = MathMin(rates_total, 256); ArrayResize(mergedCandles, allocSize); int mergedIndex = 0; for(int i = rates_total - 1; i >= 0; i--) { if(mergedIndex >= allocSize) { allocSize *= 2; ArrayResize(mergedCandles, allocSize); } if(mergedIndex == 0) { mergedCandles[mergedIndex].high = high[i]; mergedCandles[mergedIndex].low = low[i]; mergedCandles[mergedIndex].time = time[i]; mergedCandles[mergedIndex].highTime = time[i]; mergedCandles[mergedIndex].lowTime = time[i]; mergedCandles[mergedIndex].highBarIndex = i; mergedCandles[mergedIndex].lowBarIndex = i; mergedCandles[mergedIndex].isMerged = false; mergedIndex++; continue; } int lastIndex = mergedIndex - 1; double lastHigh = mergedCandles[lastIndex].high; double lastLow = mergedCandles[lastIndex].low; if((high[i] <= lastHigh && low[i] >= lastLow) || (high[i] >= lastHigh && low[i] <= lastLow)) { MERGE_DIRECTION direction = MERGE_NONE; if(mergedIndex >= 2) { double prevHigh = mergedCandles[lastIndex-1].high; double prevLow = mergedCandles[lastIndex-1].low; if(lastHigh >= prevHigh && lastLow >= prevLow) { direction = MERGE_HIGH_HIGH; } else if(lastHigh <= prevHigh && lastLow <= prevLow) { direction = MERGE_LOW_LOW; } } if(direction == MERGE_NONE) { direction = (high[i] > lastHigh) ? MERGE_HIGH_HIGH : MERGE_LOW_LOW; } if(direction == MERGE_HIGH_HIGH) { if(high[i] > lastHigh) { mergedCandles[lastIndex].highTime = time[i]; mergedCandles[lastIndex].highBarIndex = i; } if(low[i] > lastLow) { mergedCandles[lastIndex].lowTime = time[i]; mergedCandles[lastIndex].lowBarIndex = i; } mergedCandles[lastIndex].high = MathMax(lastHigh, high[i]); mergedCandles[lastIndex].low = MathMax(lastLow, low[i]); mergedCandles[lastIndex].isMerged = true; } else { if(high[i] < lastHigh) { mergedCandles[lastIndex].highTime = time[i]; mergedCandles[lastIndex].highBarIndex = i; } if(low[i] < lastLow) { mergedCandles[lastIndex].lowTime = time[i]; mergedCandles[lastIndex].lowBarIndex = i; } mergedCandles[lastIndex].high = MathMin(lastHigh, high[i]); mergedCandles[lastIndex].low = MathMin(lastLow, low[i]); mergedCandles[lastIndex].isMerged = true; } } else { mergedCandles[mergedIndex].high = high[i]; mergedCandles[mergedIndex].low = low[i]; mergedCandles[mergedIndex].time = time[i]; mergedCandles[mergedIndex].highTime = time[i]; mergedCandles[mergedIndex].lowTime = time[i]; mergedCandles[mergedIndex].highBarIndex = i; mergedCandles[mergedIndex].lowBarIndex = i; mergedCandles[mergedIndex].isMerged = false; mergedIndex++; } } return mergedIndex; } void IdentifyFractals(int mergedCount) { if(mergedCount < 3) { ArrayResize(fractalPoints, 0); return; } int maxFractals = mergedCount - 2; ArrayResize(fractalPoints, maxFractals); int fIdx = 0; for(int i = 0; i < mergedCount - 2; i++) { int left = i+2; int middle = i+1; int right = i; double leftHigh = mergedCandles[left].high; double leftLow = mergedCandles[left].low; double midHigh = mergedCandles[middle].high; double midLow = mergedCandles[middle].low; double rightHigh = mergedCandles[right].high; double rightLow = mergedCandles[right].low; if(midHigh > leftHigh && midHigh > rightHigh && midLow > leftLow && midLow > rightLow) { fractalPoints[fIdx].time = mergedCandles[middle].highTime; fractalPoints[fIdx].price = midHigh; fractalPoints[fIdx].isTop = true; fractalPoints[fIdx].barIndex = mergedCandles[middle].highBarIndex; fractalPoints[fIdx].mergedIndex = middle; fractalPoints[fIdx].fractalHigh = MathMax(MathMax(leftHigh, midHigh), rightHigh); fractalPoints[fIdx].fractalLow = MathMin(MathMin(leftLow, midLow), rightLow); fIdx++; } else if(midLow < leftLow && midLow < rightLow && midHigh < leftHigh && midHigh < rightHigh) { fractalPoints[fIdx].time = mergedCandles[middle].lowTime; fractalPoints[fIdx].price = midLow; fractalPoints[fIdx].isTop = false; fractalPoints[fIdx].barIndex = mergedCandles[middle].lowBarIndex; fractalPoints[fIdx].mergedIndex = middle; fractalPoints[fIdx].fractalHigh = MathMax(MathMax(leftHigh, midHigh), rightHigh); fractalPoints[fIdx].fractalLow = MathMin(MathMin(leftLow, midLow), rightLow); fIdx++; } } ArrayResize(fractalPoints, fIdx); } void FilterFractals() { ArrayResize(validFractals, 0); int total = ArraySize(fractalPoints); if(total < 1) return; int sorted[]; ArrayResize(sorted, total); for(int i = 0; i < total; i++) sorted[i] = i; for(int i = 1; i < total; i++) { int key = sorted[i]; datetime keyTime = fractalPoints[key].time; int j = i - 1; while(j >= 0 && fractalPoints[sorted[j]].time > keyTime) { sorted[j + 1] = sorted[j]; j--; } sorted[j + 1] = key; } ArrayResize(validFractals, total); int vIdx = 0; for(int si = 0; si < total; si++) { int fi = sorted[si]; if(vIdx == 0) { validFractals[vIdx++] = fractalPoints[fi]; continue; } FractalPoint last = validFractals[vIdx - 1]; if(last.isTop == fractalPoints[fi].isTop) { if(last.isTop) { if(fractalPoints[fi].price > last.price) validFractals[vIdx - 1] = fractalPoints[fi]; } else { if(fractalPoints[fi].price < last.price) validFractals[vIdx - 1] = fractalPoints[fi]; } } else { validFractals[vIdx++] = fractalPoints[fi]; } } ArrayResize(validFractals, vIdx); } void ProcessStrokesFractals(const double& high[], const double& low[]) { ArrayResize(finalFractals, 0); int total = ArraySize(validFractals); if(total < 1) return; ArrayResize(finalFractals, 1); finalFractals[0] = validFractals[0]; for(int i = 1; i < total; i++) { int lastIdx = ArraySize(finalFractals) - 1; FractalPoint last = finalFractals[lastIdx]; FractalPoint current = validFractals[i]; if(last.isTop == current.isTop) { if(last.isTop) { if(current.price >= last.price) { finalFractals[lastIdx] = current; } } else { if(current.price <= last.price) { finalFractals[lastIdx] = current; } } } else { int barDiff = MathAbs(last.barIndex - current.barIndex); bool validStroke = false; bool case2Stroke = false; if(barDiff >= 4) { int mergedIndexDiff = MathAbs(last.mergedIndex - current.mergedIndex); if(mergedIndexDiff > 1) { bool originalValid = true; for(int j = i-1; j > 0; j--) { if(validFractals[j].time < last.time) break; if(last.isTop && !current.isTop) { if(!validFractals[j].isTop && validFractals[j].price < current.price && validFractals[j].price != current.price) { originalValid = false; break; } if(validFractals[j].isTop && validFractals[j].price > last.price && validFractals[j].price != last.price) { originalValid = false; break; } } else if(!last.isTop && current.isTop) { if(validFractals[j].isTop && validFractals[j].price > current.price && validFractals[j].price != current.price) { originalValid = false; break; } if(!validFractals[j].isTop && validFractals[j].price < last.price && validFractals[j].price != last.price) { originalValid = false; break; } } } if(originalValid) validStroke = true; } if(!validStroke) { int case2End; if(!last.isTop && current.isTop) { case2Stroke = CheckUpStrokeCase2(validFractals, total, i, last.price, current.price, case2End); if(case2Stroke) { current = validFractals[case2End]; i = case2End; } } else if(last.isTop && !current.isTop) { case2Stroke = CheckDownStrokeCase2(validFractals, total, i, last.price, current.price, case2End); if(case2Stroke) { current = validFractals[case2End]; i = case2End; } } if(case2Stroke) validStroke = true; } } if(validStroke) { ArrayResize(finalFractals, lastIdx + 2); finalFractals[lastIdx + 1] = current; } } } } bool CheckUpStrokeCase2(FractalPoint &fractalArr[], int count, int startIdx, double firstLow, double prevHigh, int &endIdx) { endIdx = startIdx; if(startIdx + 1 >= count) return false; for(int i = startIdx + 1; i < count; i++) { if(!fractalArr[i-1].isTop && fractalArr[i].isTop) { double currentLow = fractalArr[i-1].price; double currentHigh = fractalArr[i].price; if(currentLow <= firstLow) return false; if(currentHigh > prevHigh) { endIdx = i; return true; } prevHigh = currentHigh; } } return false; } bool CheckDownStrokeCase2(FractalPoint &fractalArr[], int count, int startIdx, double firstHigh, double prevLow, int &endIdx) { endIdx = startIdx; if(startIdx + 1 >= count) return false; for(int i = startIdx + 1; i < count; i++) { if(fractalArr[i-1].isTop && !fractalArr[i].isTop) { double currentHigh = fractalArr[i-1].price; double currentLow = fractalArr[i].price; if(currentHigh >= firstHigh) return false; if(currentLow < prevLow) { endIdx = i; return true; } prevLow = currentLow; } } return false; } void IdentifyAndDrawStrokes(int rates_total, const datetime &time[]) { int total = ArraySize(finalFractals); if(total < 2) return; for(int i = 1; i < total; i++) { FractalPoint prev = finalFractals[i-1]; FractalPoint curr = finalFractals[i]; int startBar = prev.barIndex; int endBar = curr.barIndex; double startPrice = prev.price; double endPrice = curr.price; if(startBar > endBar) { int tempBar = startBar; startBar = endBar; endBar = tempBar; double tempPrice = startPrice; startPrice = endPrice; endPrice = tempPrice; } if(!prev.isTop && curr.isTop) { for(int j = startBar; j <= endBar; j++) { if(j >= 0 && j < rates_total) { double ratio = (double)(j - startBar) / (endBar - startBar); upStrokeBuffer[j] = startPrice + ratio * (endPrice - startPrice); } } } else if(prev.isTop && !curr.isTop) { for(int j = startBar; j <= endBar; j++) { if(j >= 0 && j < rates_total) { double ratio = (double)(j - startBar) / (endBar - startBar); downStrokeBuffer[j] = startPrice - ratio * (startPrice - endPrice); } } } } for(int j = 0; j < rates_total; j++) { if(upStrokeBuffer[j] == 0) upStrokeBuffer[j] = EMPTY_VALUE; if(downStrokeBuffer[j] == 0) downStrokeBuffer[j] = EMPTY_VALUE; } }
二、实时监控三级共振策略,能及时弹窗和预警,还能把共振信号推送到微信。