//+------------------------------------------------------------------+ //| Swing.mqh | //+------------------------------------------------------------------+ #property strict #ifndef __SWING_MQH__ #define __SWING_MQH__ #include #include #include #include "TimePrice.mqh" string location = "DexterrFX - SMC Bible/"; //+------------------------------------------------------------------+ //| Swing | //+------------------------------------------------------------------+ class Swing : public CIndicator { private: string symbol; ENUM_TIMEFRAMES timeframe; double highBuffer[]; double lowBuffer[]; datetime highTimes[]; datetime lowTimes[]; CArrayList highs; CArrayList lows; CArrayList zigzag; bool asSeries; private: datetime FirstOfPreviousMonths(datetime currentTime, int monthsBack) { MqlDateTime dt; TimeToStruct(currentTime, dt); dt.mon -= monthsBack; while(dt.mon <= 0) { dt.mon += 12; dt.year--; } dt.day = 1; dt.hour = 0; dt.min = 0; dt.sec = 0; return StructToTime(dt); } int MapIndex(int i, int total) { if(i < 0 || i >= total) return -1; if(!asSeries) return i; return total - 1 - i; } bool ComesBefore(TimePrice &a, TimePrice &b) { if(!asSeries) return a.x < b.x; return a.x > b.x; } bool SamePoint(TimePrice &a, TimePrice &b) { return a.x == b.x && a.y == b.y; } bool IsBetterSameTypePoint(TimePrice ¤tPoint, TimePrice &newPoint, int type) { if(type > 0) return newPoint.y > currentPoint.y; return newPoint.y < currentPoint.y; } void AddZigZagPoint(TimePrice &point, int type, TimePrice &workPoints[], int &workTypes[], int &count) { if(!point.Ok()) return; if(count <= 0) { ArrayResize(workPoints, 1); ArrayResize(workTypes, 1); workPoints[0] = point; workTypes[0] = type; count = 1; return; } int last = count - 1; if(SamePoint(workPoints[last], point)) return; if(workTypes[last] == type) { if(IsBetterSameTypePoint(workPoints[last], point, type)) workPoints[last] = point; return; } ArrayResize(workPoints, count + 1); ArrayResize(workTypes, count + 1); workPoints[count] = point; workTypes[count] = type; count++; } void RebuildSwingList(double &source[], datetime ×[], CArrayList &target) { target.Clear(); int totalValues = ArraySize(source); int totalTimes = ArraySize(times); int total = MathMin(totalValues, totalTimes); for(int logical = 0; logical < total; logical++) { int physical = MapIndex(logical, total); if(physical < 0) continue; if(!IsValidValue(source[physical])) continue; if(times[physical] <= 0) continue; TimePrice point; point.Set(logical, times[physical], source[physical]); if(point.Ok()) target.Add(point); } } void BuildFullList(double &source[], datetime ×[], TimePrice &result[]) { int totalValues = ArraySize(source); int totalTimes = ArraySize(times); int total = MathMin(totalValues, totalTimes); ArrayResize(result, total); for(int logical = 0; logical < total; logical++) { int physical = MapIndex(logical, total); result[logical].Reset(); result[logical].i = logical; if(physical < 0) continue; result[logical].Set(logical, times[physical], source[physical]); } } void BuildValueOnlyList(double &source[], datetime ×[], TimePrice &result[]) { ArrayResize(result, 0); int totalValues = ArraySize(source); int totalTimes = ArraySize(times); int total = MathMin(totalValues, totalTimes); int count = 0; for(int logical = 0; logical < total; logical++) { int physical = MapIndex(logical, total); if(physical < 0) continue; if(!IsValidValue(source[physical])) continue; if(times[physical] <= 0) continue; ArrayResize(result, count + 1); result[count].Reset(); result[count].Set(logical, times[physical], source[physical]); count++; } } void BuildZigZagFromSwings() { zigzag.Clear(); TimePrice highArray[]; TimePrice lowArray[]; highs.GetAll(highArray); lows.GetAll(lowArray); int highCount = ArraySize(highArray); int lowCount = ArraySize(lowArray); int highIndex = 0; int lowIndex = 0; TimePrice workPoints[]; int workTypes[]; int workCount = 0; while(highIndex < highCount || lowIndex < lowCount) { bool useHigh = false; bool useLow = false; if(highIndex < highCount && lowIndex < lowCount) { TimePrice h = highArray[highIndex]; TimePrice l = lowArray[lowIndex]; if(h.x == l.x) { AddZigZagPoint(h, 1, workPoints, workTypes, workCount); AddZigZagPoint(l, -1, workPoints, workTypes, workCount); highIndex++; lowIndex++; continue; } if(ComesBefore(h, l)) useHigh = true; else useLow = true; } else if(highIndex < highCount) { useHigh = true; } else if(lowIndex < lowCount) { useLow = true; } if(useHigh) { TimePrice point = highArray[highIndex]; AddZigZagPoint(point, 1, workPoints, workTypes, workCount); highIndex++; } if(useLow) { TimePrice point = lowArray[lowIndex]; AddZigZagPoint(point, -1, workPoints, workTypes, workCount); lowIndex++; } } for(int i = 0; i < workCount; i++) zigzag.Add(workPoints[i]); } public: Swing() { symbol = _Symbol; timeframe = _Period; asSeries = false; ArrayResize(highBuffer, 0); ArrayResize(lowBuffer, 0); ArrayResize(highTimes, 0); ArrayResize(lowTimes, 0); } ~Swing() { ArrayFree(highBuffer); ArrayFree(lowBuffer); ArrayFree(highTimes); ArrayFree(lowTimes); highs.Reset(); lows.Reset(); zigzag.Reset(); } void SetAsSeries(bool value) { asSeries = value; RebuildSwingList(highBuffer, highTimes, highs); RebuildSwingList(lowBuffer, lowTimes, lows); BuildZigZagFromSwings(); } bool GetAsSeries() { return asSeries; } bool IsValidValue(double value) { return value != 0.0 && value != EMPTY_VALUE; } int Init(string indName, string pSymbol, ENUM_TIMEFRAMES pTimeframe, int InpLeftRightBars = 3, int InpSwingWindow = 1) { symbol = pSymbol; timeframe = pTimeframe; handle = iCustom(symbol, timeframe, location + indName, InpLeftRightBars, InpSwingWindow); if(handle == INVALID_HANDLE) { Print("Error creating Swing Indicator: ", GetLastError()); return -1; } return handle; } int UpdateRange(int bufferIndex, datetime startTime, datetime stopTime, double &dst[], datetime ×[]) { ArrayResize(dst, 0); ArrayResize(times, 0); if(handle == INVALID_HANDLE) return 0; if(startTime <= 0 || stopTime <= 0 || stopTime <= startTime) return 0; double tempValues[]; datetime tempTimes[]; ArraySetAsSeries(tempValues, false); ArraySetAsSeries(tempTimes, false); int copiedValues = CopyBuffer(handle, bufferIndex, startTime, stopTime, tempValues); int copiedTimes = CopyTime(symbol, timeframe, startTime, stopTime, tempTimes); if(copiedValues <= 0 || copiedTimes <= 0) { Print("Swing::UpdateRange failed. buffer=", bufferIndex, " values=", copiedValues, " times=", copiedTimes, " error=", GetLastError(), " from=", TimeToString(startTime), " to=", TimeToString(stopTime)); return 0; } int total = MathMin(copiedValues, copiedTimes); ArrayResize(dst, total); ArrayResize(times, total); for(int i = 0; i < total; i++) { dst[i] = tempValues[i]; times[i] = tempTimes[i]; } return total; } int UpdateRange(int bufferIndex, int count, double &dst[], datetime ×[]) { ArrayResize(dst, 0); ArrayResize(times, 0); if(handle == INVALID_HANDLE) return 0; if(count <= 0) return 0; double tempValues[]; datetime tempTimes[]; ArraySetAsSeries(tempValues, false); ArraySetAsSeries(tempTimes, false); int copiedValues = CopyBuffer(handle, bufferIndex, 0, count, tempValues); int copiedTimes = CopyTime(symbol, timeframe, 0, count, tempTimes); if(copiedValues <= 0 || copiedTimes <= 0) { Print("Swing::UpdateRange failed. buffer=", bufferIndex, " values=", copiedValues, " times=", copiedTimes, " error=", GetLastError(), " count=", count); return 0; } int total = MathMin(copiedValues, copiedTimes); ArrayResize(dst, total); ArrayResize(times, total); for(int i = 0; i < total; i++) { dst[i] = tempValues[i]; times[i] = tempTimes[i]; } return total; } int UpdateFromMonthsBack(int monthsCount, bool useMonths = true) { if(monthsCount <= 0) { Print("Swing::UpdateFromMonthsBack invalid monthsCount=", monthsCount); Clear(); return 0; } int copiedHigh = 0; int copiedLow = 0; if(useMonths) { datetime toTime = TimeCurrent(); datetime fromTime = FirstOfPreviousMonths(toTime, monthsCount); copiedHigh = UpdateRange(0, fromTime, toTime, highBuffer, highTimes); copiedLow = UpdateRange(1, fromTime, toTime, lowBuffer, lowTimes); } else { copiedHigh = UpdateRange(0, monthsCount, highBuffer, highTimes); copiedLow = UpdateRange(1, monthsCount, lowBuffer, lowTimes); } RebuildSwingList(highBuffer, highTimes, highs); RebuildSwingList(lowBuffer, lowTimes, lows); BuildZigZagFromSwings(); return copiedHigh + copiedLow; } int GetHighSize() { return highs.Size(); } int GetLowSize() { return lows.Size(); } int GetZigZagSize() { return zigzag.Size(); } int GetHighBufferSize() { return ArraySize(highBuffer); } int GetLowBufferSize() { return ArraySize(lowBuffer); } int GetFullHighSize() { return MathMin(ArraySize(highBuffer), ArraySize(highTimes)); } int GetFullLowSize() { return MathMin(ArraySize(lowBuffer), ArraySize(lowTimes)); } double High(int index = 0) { TimePrice point; if(!highs.Get(index, point)) return 0.0; return point.y; } double Low(int index = 0) { TimePrice point; if(!lows.Get(index, point)) return 0.0; return point.y; } double ZigZag(int index = 0) { TimePrice point; if(!zigzag.Get(index, point)) return 0.0; return point.y; } double FullHigh(int index = 0) { int total = GetFullHighSize(); int i = MapIndex(index, total); if(i < 0) return 0.0; return highBuffer[i]; } double FullLow(int index = 0) { int total = GetFullLowSize(); int i = MapIndex(index, total); if(i < 0) return 0.0; return lowBuffer[i]; } datetime FullHighTime(int index = 0) { int total = GetFullHighSize(); int i = MapIndex(index, total); if(i < 0) return 0; return highTimes[i]; } datetime FullLowTime(int index = 0) { int total = GetFullLowSize(); int i = MapIndex(index, total); if(i < 0) return 0; return lowTimes[i]; } bool High(int index, TimePrice &point) { point.Reset(); return highs.Get(index, point); } bool Low(int index, TimePrice &point) { point.Reset(); return lows.Get(index, point); } bool ZigZag(int index, TimePrice &point) { point.Reset(); return zigzag.Get(index, point); } bool FullHigh(int index, TimePrice &point) { point.Reset(); int total = GetFullHighSize(); int i = MapIndex(index, total); if(i < 0) return false; point.Set(index, highTimes[i], highBuffer[i]); return true; } bool FullLow(int index, TimePrice &point) { point.Reset(); int total = GetFullLowSize(); int i = MapIndex(index, total); if(i < 0) return false; point.Set(index, lowTimes[i], lowBuffer[i]); return true; } bool HighAtBar(int barIndex, TimePrice &point) { return FullHigh(barIndex, point); } bool LowAtBar(int barIndex, TimePrice &point) { return FullLow(barIndex, point); } bool IsHighAtBar(int barIndex) { double value = FullHigh(barIndex); return IsValidValue(value); } bool IsLowAtBar(int barIndex) { double value = FullLow(barIndex); return IsValidValue(value); } void GetHighs(TimePrice &result[]) { highs.GetAll(result); } void GetLows(TimePrice &result[]) { lows.GetAll(result); } void GetZigZag(TimePrice &result[]) { zigzag.GetAll(result); } void GetFullHighs(TimePrice &result[]) { BuildFullList(highBuffer, highTimes, result); } void GetFullLows(TimePrice &result[]) { BuildFullList(lowBuffer, lowTimes, result); } void GetHighs(TimePrice &result[], bool includeNonValues) { if(includeNonValues) { GetFullHighs(result); return; } GetHighs(result); } void GetLows(TimePrice &result[], bool includeNonValues) { if(includeNonValues) { GetFullLows(result); return; } GetLows(result); } void GetHighValuesOnly(TimePrice &result[]) { BuildValueOnlyList(highBuffer, highTimes, result); } void GetLowValuesOnly(TimePrice &result[]) { BuildValueOnlyList(lowBuffer, lowTimes, result); } void GetHighBuffer(double &result[]) { int total = ArraySize(highBuffer); ArrayResize(result, total); for(int logical = 0; logical < total; logical++) { int physical = MapIndex(logical, total); if(physical < 0) result[logical] = 0.0; else result[logical] = highBuffer[physical]; } } void GetLowBuffer(double &result[]) { int total = ArraySize(lowBuffer); ArrayResize(result, total); for(int logical = 0; logical < total; logical++) { int physical = MapIndex(logical, total); if(physical < 0) result[logical] = 0.0; else result[logical] = lowBuffer[physical]; } } void GetHighTimes(datetime &result[]) { int total = ArraySize(highTimes); ArrayResize(result, total); for(int logical = 0; logical < total; logical++) { int physical = MapIndex(logical, total); if(physical < 0) result[logical] = 0; else result[logical] = highTimes[physical]; } } void GetLowTimes(datetime &result[]) { int total = ArraySize(lowTimes); ArrayResize(result, total); for(int logical = 0; logical < total; logical++) { int physical = MapIndex(logical, total); if(physical < 0) result[logical] = 0; else result[logical] = lowTimes[physical]; } } void RebuildZigZag() { BuildZigZagFromSwings(); } void Clear() { ArrayResize(highBuffer, 0); ArrayResize(lowBuffer, 0); ArrayResize(highTimes, 0); ArrayResize(lowTimes, 0); highs.Clear(); lows.Clear(); zigzag.Clear(); } }; #endif //+------------------------------------------------------------------+