697 lines
16 KiB
MQL5
697 lines
16 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| Swing.mqh |
| |||
//+------------------------------------------------------------------+
| |||
#property strict
| |||
| |||
#ifndef __SWING_MQH__
| |||
#define __SWING_MQH__
| |||
| |||
#include <Mql5Book\Timer.mqh>
| |||
#include <Mql5Book\Indicators.mqh>
| |||
#include <Collection\DynamicArrayList.mqh>
| |||
#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<TimePrice> highs;
| |||
CArrayList<TimePrice> lows;
| |||
CArrayList<TimePrice> 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<TimePrice> &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
| |||
//+------------------------------------------------------------------+
|