697 lines
No EOL
16 KiB
MQL5
697 lines
No EOL
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
| |
//+------------------------------------------------------------------+ |