diff --git a/.github/workflows/test-indicators.yml b/.github/workflows/test-indicators.yml
new file mode 100644
index 000000000..d49351f5e
--- /dev/null
+++ b/.github/workflows/test-indicators.yml
@@ -0,0 +1,128 @@
+---
+name: Test Indicators
+
+# yamllint disable-line rule:truthy
+on:
+ pull_request:
+ paths:
+ - 'Indicator**'
+ - 'Indicators/**'
+ - '.github/workflows/test-indicators.yml'
+ push:
+ paths:
+ - 'Indicator**'
+ - 'Indicators/**'
+ - '.github/workflows/test-indicators.yml'
+
+jobs:
+
+ Compile:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Compile
+ uses: fx31337/mql-compile-action@master
+ with:
+ init-platform: true
+ path: 'Indicators/tests'
+ verbose: true
+ - name: Print compiled files
+ run: '(Get-ChildItem -Recurse -Path . -Include *.ex[45]).fullname'
+ shell: powershell
+ - name: Upload artifacts (MQL4)
+ uses: actions/upload-artifact@v2
+ with:
+ name: files-ex4
+ path: '**/*.ex4'
+ - name: Upload artifacts (MQL5)
+ uses: actions/upload-artifact@v2
+ with:
+ name: files-ex5
+ path: '**/*.ex5'
+
+ Indicators-Tests-MQL4:
+ defaults:
+ run:
+ shell: bash
+ working-directory: Indicators/tests
+ needs: Compile
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ test:
+ - Indi_AC.test
+ - Indi_AD.test
+ - Indi_ADX.test
+ - Indi_ADXW.test
+ - Indi_AMA.test
+ - Indi_AO.test
+ - Indi_ASI.test
+ - Indi_ATR.test
+ - Indi_Alligator.test
+ - Indi_AppliedPrice.test
+ - Indi_BWMFI.test
+ - Indi_BWZT.test
+ - Indi_Bands.test
+ - Indi_BearsPower.test
+ - Indi_BullsPower.test
+ - Indi_CCI.test
+ - Indi_CHO.test
+ - Indi_CHV.test
+ - Indi_ColorBars.test
+ - Indi_ColorCandlesDaily.test
+ - Indi_ColorLine.test
+ - Indi_CustomMovingAverage.test
+ - Indi_DEMA.test
+ - Indi_DeMarker.test
+ - Indi_Demo.test
+ - Indi_DetrendedPrice.test
+ - Indi_Drawer.test
+ - Indi_Envelopes.test
+ - Indi_Force.test
+ - Indi_FractalAdaptiveMA.test
+ - Indi_Fractals.test
+ - Indi_Gator.test
+ - Indi_HeikenAshi.test
+ - Indi_Ichimoku.test
+ - Indi_Killzones.test
+ - Indi_MA.test
+ - Indi_MACD.test
+ - Indi_MFI.test
+ - Indi_MassIndex.test
+ - Indi_Momentum.test
+ - Indi_OBV.test
+ - Indi_OsMA.test
+ - Indi_Pattern.test
+ - Indi_Pivot.test
+ - Indi_Price.test
+ # - Indi_PriceChannel.test
+ - Indi_PriceFeeder.test
+ - Indi_PriceVolumeTrend.test
+ - Indi_RS.test
+ - Indi_RSI.test
+ - Indi_RVI.test
+ - Indi_RateOfChange.test
+ - Indi_SAR.test
+ - Indi_StdDev.test
+ - Indi_Stochastic.test
+ - Indi_TEMA.test
+ - Indi_TRIX.test
+ - Indi_UltimateOscillator.test
+ - Indi_VIDYA.test
+ - Indi_VROC.test
+ - Indi_Volumes.test
+ - Indi_WPR.test
+ - Indi_WilliamsAD.test
+ # - Indi_ZigZag.test
+ # - Indi_ZigZagColor.test
+ steps:
+ - uses: actions/download-artifact@v2
+ with:
+ name: files-ex4
+ - name: Run ${{ matrix.test }}
+ uses: fx31337/mql-tester-action@master
+ with:
+ BtDays: 4-8
+ BtMonths: 1
+ BtYears: 2020
+ TestExpert: ${{ matrix.test }}
diff --git a/.github/workflows/test-trade.yml b/.github/workflows/test-trade.yml
new file mode 100644
index 000000000..744585359
--- /dev/null
+++ b/.github/workflows/test-trade.yml
@@ -0,0 +1,60 @@
+---
+name: Test Trade
+
+# yamllint disable-line rule:truthy
+on:
+ pull_request:
+ paths:
+ - 'Trade/**.h'
+ - '.github/workflows/test-trade.yml'
+ push:
+ paths:
+ - 'Trade/**.h'
+ - '.github/workflows/test-trade.yml'
+
+jobs:
+
+ Compile:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Compile
+ uses: fx31337/mql-compile-action@master
+ with:
+ init-platform: true
+ path: 'Trade/tests'
+ verbose: true
+ - name: Print compiled files
+ run: '(Get-ChildItem -Recurse -Path . -Include *.ex[45]).fullname'
+ shell: powershell
+ - name: Upload artifacts (MQL4)
+ uses: actions/upload-artifact@v2
+ with:
+ name: files-ex4
+ path: '**/*.ex4'
+ - name: Upload artifacts (MQL5)
+ uses: actions/upload-artifact@v2
+ with:
+ name: files-ex5
+ path: '**/*.ex5'
+
+ Trade-Tests-MQL4:
+ defaults:
+ run:
+ shell: bash
+ working-directory: Trade/tests
+ needs: Compile
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ test:
+ - TradeSignalTest
+ - TradeSignalManagerTest
+ steps:
+ - uses: actions/download-artifact@v2
+ with:
+ name: files-ex4
+ - name: Run ${{ matrix.test }}
+ uses: fx31337/mql-tester-action@master
+ with:
+ Script: ${{ matrix.test }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2bc531166..81d269619 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -58,6 +58,7 @@ jobs:
- BufferStructTest
- BufferTest
- ChartTest
+ - CompileIndicatorsTest
- ConditionTest
- DatabaseTest
- DrawIndicatorTest
@@ -90,93 +91,6 @@ jobs:
BtYears: 2020
TestExpert: ${{ matrix.test }}
- Indicators-MQL4:
- defaults:
- run:
- shell: bash
- working-directory: Indicators/tests
- needs: Compile
- runs-on: ubuntu-latest
- strategy:
- matrix:
- test:
- - Indi_AC.test
- - Indi_AD.test
- # - Indi_ADX.test
- - Indi_ADXW.test
- - Indi_AMA.test
- - Indi_AO.test
- - Indi_ASI.test
- - Indi_ATR.test
- - Indi_Alligator.test
- # - Indi_AppliedPrice.test
- # - Indi_BWMFI.test
- # - Indi_BWZT.test
- # - Indi_Bands.test
- # - Indi_BearsPower.test
- # - Indi_BullsPower.test
- # - Indi_CCI.test
- # - Indi_CHO.test
- # - Indi_CHV.test
- # - Indi_ColorBars.test
- # - Indi_ColorCandlesDaily.test
- # - Indi_ColorLine.test
- # - Indi_CustomMovingAverage.test
- # - Indi_DEMA.test
- # - Indi_DeMarker.test
- # - Indi_Demo.test
- # - Indi_DetrendedPrice.test
- # - Indi_Drawer.test
- # - Indi_Envelopes.test
- # - Indi_Force.test
- # - Indi_FractalAdaptiveMA.test
- # - Indi_Fractals.test
- # - Indi_Gator.test
- # - Indi_HeikenAshi.test
- # - Indi_Ichimoku.test
- # - Indi_MA.test
- # - Indi_MACD.test
- # - Indi_MFI.test
- # - Indi_MarketFacilitationIndex.test
- # - Indi_MassIndex.test
- # - Indi_Momentum.test
- # - Indi_OBV.test
- # - Indi_OsMA.test
- # - Indi_Pattern.test
- # - Indi_Pivot.test
- # - Indi_Price.test
- # - Indi_PriceChannel.test
- # - Indi_PriceFeeder.test
- # - Indi_PriceVolumeTrend.test
- # - Indi_RS.test
- # - Indi_RSI.test
- # - Indi_RVI.test
- # - Indi_RateOfChange.test
- # - Indi_SAR.test
- # - Indi_StdDev.test
- # - Indi_Stochastic.test
- # - Indi_TEMA.test
- # - Indi_TRIX.test
- # - Indi_UltimateOscillator.test
- # - Indi_VIDYA.test
- # - Indi_VROC.test
- # - Indi_Volumes.test
- # - Indi_WPR.test
- # - Indi_WilliamsAD.test
- # - Indi_ZigZag.test
- # - Indi_ZigZagColor.test
- steps:
- - uses: actions/download-artifact@v2
- with:
- name: files-ex4
- - name: Run ${{ matrix.test }}
- uses: fx31337/mql-tester-action@master
- with:
- BtDays: 4-8
- BtMonths: 1
- BtYears: 2020
- TestExpert: ${{ matrix.test }}
-
Scripts-MQL4:
defaults:
run:
@@ -189,7 +103,6 @@ jobs:
test:
# - 3DTest
- CollectionTest
- - CompileIndicatorsTest
- ConfigTest
- ConvertTest
- DateTimeTest
@@ -236,3 +149,23 @@ jobs:
with:
Script: ${{ matrix.test }}
RunOnFail: "exit 0"
+
+ Trade-Tests-MQL4:
+ defaults:
+ run:
+ shell: bash
+ working-directory: Trade/tests
+ needs: Compile
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ test:
+ - TradeSignalTest
+ steps:
+ - uses: actions/download-artifact@v2
+ with:
+ name: files-ex4
+ - name: Run ${{ matrix.test }}
+ uses: fx31337/mql-tester-action@master
+ with:
+ Script: ${{ matrix.test }}
diff --git a/Action.mqh b/Action.mqh
index 85f9a3a9b..5ed379560 100644
--- a/Action.mqh
+++ b/Action.mqh
@@ -141,7 +141,7 @@ class Action {
#ifdef INDICATOR_MQH
case ACTION_TYPE_INDICATOR:
if (Object::IsValid(_entry.obj)) {
- _result = ((Indicator *)_entry.obj).ExecuteAction((ENUM_INDICATOR_ACTION)_entry.action_id);
+ _result = ((IndicatorBase *)_entry.obj).ExecuteAction((ENUM_INDICATOR_ACTION)_entry.action_id);
} else {
_result = false;
_entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID);
diff --git a/Bar.enum.h b/Bar.enum.h
index 11c5c7911..e4c834f7e 100644
--- a/Bar.enum.h
+++ b/Bar.enum.h
@@ -38,5 +38,4 @@ enum ENUM_PP_TYPE {
PP_FLOOR = 4, // Most basic and popular type of pivots used in Forex trading technical analysis
PP_TOM_DEMARK = 5, // Tom DeMark's pivot point (predicted lows and highs of the period)
PP_WOODIE = 6, // Woodie's pivot point are giving more weight to the Close price of the previous period
- FINAL_ENUM_PP_TYPE_ENTRY
};
diff --git a/Chart.define.h b/Chart.define.h
index e0903d891..7f06e374a 100644
--- a/Chart.define.h
+++ b/Chart.define.h
@@ -27,6 +27,29 @@
/* Defines */
+// Define type of timeframe periods using bitwise values.
+#define M1B (1 << M1) // 1 minute
+#define M2B (1 << M2) // 2 minutes (non-standard)
+#define M3B (1 << M3) // 3 minutes (non-standard)
+#define M4B (1 << M4) // 4 minutes (non-standard)
+#define M5B (1 << M5) // 5 minutes
+#define M6B (1 << M6) // 6 minutes (non-standard)
+#define M10B (1 << M10) // 10 minutes (non-standard)
+#define M12B (1 << M12) // 12 minutes (non-standard)
+#define M15B (1 << M15) // 15 minutes
+#define M20B (1 << M20) // 20 minutes (non-standard)
+#define M30B (1 << M30) // 30 minutes
+#define H1B (1 << H1) // 1 hour
+#define H2B (1 << H2) // 2 hours (non-standard)
+#define H3B (1 << H3) // 3 hours (non-standard)
+#define H4B (1 << H4) // 4 hours
+#define H6B (1 << H6) // 6 hours (non-standard)
+#define H8B (1 << H8) // 8 hours (non-standard)
+#define H12B (1 << H12) // 12 hours (non-standard)
+#define D1B (1 << D1) // Daily
+#define W1B (1 << W1) // Weekly
+#define MN1B (1 << MN1) // Monthly
+
#ifndef __MQL4__
// Chart.
#define CHART_BAR 0
diff --git a/Chart.enum.h b/Chart.enum.h
index f8ad5dc22..1bafacd19 100644
--- a/Chart.enum.h
+++ b/Chart.enum.h
@@ -88,20 +88,6 @@ enum ENUM_TIMEFRAMES_INDEX {
FINAL_ENUM_TIMEFRAMES_INDEX = 21
};
-/* Define type of periods using bitwise operators. */
-enum ENUM_TIMEFRAMES_BITS {
- M1B = 1 << 0, // =1: 1 minute
- M5B = 1 << 1, // =2: 5 minutes
- M15B = 1 << 2, // =4: 15 minutes
- M30B = 1 << 3, // =8: 30 minutes
- H1B = 1 << 4, // =16: 1 hour
- H4B = 1 << 5, // =32: 4 hours
- H8B = 1 << 6, // =64: 8 hours
- D1B = 1 << 7, // =128: Daily
- W1B = 1 << 8, // =256: Weekly
- MN1B = 1 << 9, // =512: Monthly
-};
-
#ifndef __MQLBUILD__
/**
* Defines chart timeframes.
diff --git a/Chart.struct.static.h b/Chart.struct.static.h
index b34665fa9..7034f7bef 100644
--- a/Chart.struct.static.h
+++ b/Chart.struct.static.h
@@ -277,10 +277,16 @@ struct ChartStatic {
*
* If local history is empty (not loaded), function returns 0.
*/
- static long iVolume(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, uint _shift = 0) {
+ static long iVolume(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0) {
#ifdef __MQL4__
- return ::iVolume(_symbol, _tf, _shift); // Same as: Volume[_shift]
-#else // __MQL5__
+ ResetLastError();
+ long _volume = ::iVolume(_symbol, _tf, _shift); // Same as: Volume[_shift]
+ if (_LastError != ERR_NO_ERROR) {
+ _volume = EMPTY_VALUE;
+ ResetLastError();
+ }
+ return _volume;
+#else // __MQL5__
ARRAY(long, _arr);
ArraySetAsSeries(_arr, true);
return (_shift >= 0 && CopyTickVolume(_symbol, _tf, _shift, 1, _arr) > 0) ? _arr[0] : -1;
diff --git a/Chart.struct.tf.h b/Chart.struct.tf.h
index 46c80f026..cc9e1d3e9 100644
--- a/Chart.struct.tf.h
+++ b/Chart.struct.tf.h
@@ -122,7 +122,7 @@ struct ChartTf {
* @param
* _tf ENUM_TIMEFRAMES_INDEX Specify timeframe index enum.
*/
- static ENUM_TIMEFRAMES IndexToTf(ENUM_TIMEFRAMES_INDEX index) {
+ static ENUM_TIMEFRAMES const IndexToTf(ENUM_TIMEFRAMES_INDEX index) {
// @todo: Convert it into a loop and using tf constant, see: TfToIndex().
switch (index) {
case M1:
diff --git a/Condition.mqh b/Condition.mqh
index 27183866b..915cbcd4e 100644
--- a/Condition.mqh
+++ b/Condition.mqh
@@ -167,7 +167,7 @@ class Condition {
#ifdef INDICATOR_MQH
case COND_TYPE_INDICATOR:
if (Object::IsValid(_entry.obj)) {
- _result = ((Indicator *)_entry.obj).CheckCondition((ENUM_INDICATOR_CONDITION)_entry.cond_id, _entry.args);
+ _result = ((IndicatorBase *)_entry.obj).CheckCondition((ENUM_INDICATOR_CONDITION)_entry.cond_id, _entry.args);
} else {
// Static method not supported.
_result = false;
diff --git a/Dict.enum.h b/Dict.enum.h
new file mode 100644
index 000000000..33887918c
--- /dev/null
+++ b/Dict.enum.h
@@ -0,0 +1,34 @@
+//+------------------------------------------------------------------+
+//| EA31337 framework |
+//| Copyright 2016-2021, EA31337 Ltd |
+//| https://github.com/EA31337 |
+//+------------------------------------------------------------------+
+
+/*
+ * This file is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+/**
+ * @file
+ * Includes Dicts's enums and defines.
+ */
+
+#ifndef __MQL__
+// Allows the preprocessor to include a header file when it is needed.
+#pragma once
+#endif
+
+#define DICT_GROW_UP_PERCENT_DEFAULT 25
+#define DICT_PERFORMANCE_PROBLEM_AVG_CONFLICTS 10
diff --git a/Dict.mqh b/Dict.mqh
index 2ca902d28..3fe692a31 100644
--- a/Dict.mqh
+++ b/Dict.mqh
@@ -112,7 +112,7 @@ class Dict : public DictBase {
* Inserts or replaces value for a given key.
*/
bool Set(K key, V value) {
- if (!InsertInto(_DictSlots_ref, key, value)) return false;
+ if (!InsertInto(_DictSlots_ref, key, value, true)) return false;
return true;
}
@@ -204,7 +204,14 @@ class Dict : public DictBase {
/**
* Inserts value into given array of DictSlots.
*/
- bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V value) {
+ bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V value, bool allow_resize) {
+ // Will resize dict if there were performance problems before.
+ if (allow_resize && !dictSlotsRef.IsPerformant()) {
+ if (!GrowUp()) {
+ return false;
+ }
+ }
+
if (_mode == DictModeUnknown)
_mode = DictModeDict;
else if (_mode != DictModeDict) {
@@ -227,8 +234,8 @@ class Dict : public DictBase {
}
if (keySlot == NULL) {
- // We need to expand array of DictSlotsRef.DictSlots (by 25%).
- if (!Resize(MathMax(10, (int)((float)ArraySize(dictSlotsRef.DictSlots) * 1.25)))) return false;
+ // We need to expand array of DictSlotsRef.DictSlots (by 25% by default).
+ if (!GrowUp()) return false;
}
}
@@ -268,6 +275,8 @@ class Dict : public DictBase {
// Slot overwrite is not needed. Using empty slot.
++dictSlotsRef._num_used;
}
+
+ dictSlotsRef.AddConflicts(_num_conflicts);
}
dictSlotsRef.DictSlots[position].key = key;
@@ -289,8 +298,8 @@ class Dict : public DictBase {
}
if (dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots)) {
- // No DictSlotsRef.DictSlots available, we need to expand array of DictSlotsRef.DictSlots (by 25%).
- if (!Resize(MathMax(10, (int)((float)ArraySize(dictSlotsRef.DictSlots) * 1.25)))) return false;
+ // No DictSlotsRef.DictSlots available, we need to expand array of DictSlotsRef.DictSlots.
+ if (!GrowUp()) return false;
}
unsigned int position = Hash((unsigned int)dictSlotsRef._list_index) % ArraySize(dictSlotsRef.DictSlots);
@@ -309,10 +318,17 @@ class Dict : public DictBase {
return true;
}
+ /**
+ * Expands array of DictSlots by given percentage value.
+ */
+ bool GrowUp(int percent = DICT_GROW_UP_PERCENT_DEFAULT) {
+ return Resize(MathMax(10, (int)((float)ArraySize(_DictSlots_ref.DictSlots) * ((float)(percent + 100) / 100.0f))));
+ }
+
/**
* Shrinks or expands array of DictSlots.
*/
- bool Resize(unsigned int new_size) {
+ bool Resize(int new_size) {
if (new_size <= MathMin(_DictSlots_ref._num_used, ArraySize(_DictSlots_ref.DictSlots))) {
// We already use minimum number of slots possible.
return true;
@@ -322,7 +338,7 @@ class Dict : public DictBase {
if (ArrayResize(new_DictSlots.DictSlots, new_size) == -1) return false;
- unsigned int i;
+ int i;
for (i = 0; i < new_size; ++i) {
new_DictSlots.DictSlots[i].SetFlags(0);
@@ -331,11 +347,11 @@ class Dict : public DictBase {
new_DictSlots._num_used = 0;
// Copies entire array of DictSlots into new array of DictSlots. Hashes will be rehashed.
- for (i = 0; i < (unsigned int)ArraySize(_DictSlots_ref.DictSlots); ++i) {
+ for (i = 0; i < ArraySize(_DictSlots_ref.DictSlots); ++i) {
if (!_DictSlots_ref.DictSlots[i].IsUsed()) continue;
if (_DictSlots_ref.DictSlots[i].HasKey()) {
- if (!InsertInto(new_DictSlots, _DictSlots_ref.DictSlots[i].key, _DictSlots_ref.DictSlots[i].value))
+ if (!InsertInto(new_DictSlots, _DictSlots_ref.DictSlots[i].key, _DictSlots_ref.DictSlots[i].value, false))
return false;
} else {
if (!InsertInto(new_DictSlots, _DictSlots_ref.DictSlots[i].value)) return false;
diff --git a/DictBase.mqh b/DictBase.mqh
index 8976c3514..534f91e8a 100644
--- a/DictBase.mqh
+++ b/DictBase.mqh
@@ -26,6 +26,7 @@
// Includes.
#include "Convert.mqh"
+#include "Dict.enum.h"
#include "DictIteratorBase.mqh"
#include "DictSlot.mqh"
#include "Serializer.mqh"
@@ -43,6 +44,14 @@ enum ENUM_DICT_OVERFLOW_REASON {
DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS,
};
+/**
+ * Dictionary flags.
+ */
+enum ENUM_DICT_FLAG {
+ DICT_FLAG_NONE = 0,
+ DICT_FLAG_FILL_HOLES_UNSORTED = 1,
+};
+
/**
* Dictionary overflow listener. arguments are:
* - ENUM_DICT_OVERFLOW_REASON overflow_reason
@@ -65,11 +74,15 @@ class DictBase {
// Whether Dict operates in yet uknown mode, as dict or as list.
DictMode _mode;
+ // Dictionary flags.
+ int _flags;
+
public:
DictBase() {
_hash = rand();
_current_id = 0;
_mode = DictModeUnknown;
+ _flags = 0;
}
/**
@@ -92,6 +105,16 @@ class DictBase {
const unsigned int GetSlotCount() const { return ArraySize(_DictSlots_ref.DictSlots); }
+ /**
+ * Adds flags to dict.
+ */
+ void AddFlags(int flags) { _flags |= flags; }
+
+ /**
+ * Checks whether dict have all given flags.
+ */
+ bool HasFlags(int flags) { return (_flags & flags) == flags; }
+
DictSlot* GetSlot(const unsigned int index) {
if (index >= GetSlotCount()) {
// Index of out bounds.
@@ -146,16 +169,50 @@ class DictBase {
int GetMode() { return _mode; }
+ /**
+ * Removes value from the dictionary by the given iterator. Could be used to remove value on Dict with
+ * DICT_FLAG_FILL_HOLES_UNSORTED flag.
+ */
+ void Unset(DictIteratorBase& iter) {
+ InternalUnset(iter.Key());
+ if (HasFlags(DICT_FLAG_FILL_HOLES_UNSORTED)) {
+ // After incrementing, iterator will use moved slot.
+ iter.ShiftPosition(-1, true);
+ }
+ }
+
/**
* Removes value from the dictionary by the given key (if exists).
*/
void Unset(const K key) {
+ if (HasFlags(DICT_FLAG_FILL_HOLES_UNSORTED)) {
+ Print(
+ "Unset on Dict with DICT_FLAG_FILL_HOLES_UNSORTED flag must be called by passing the iterator, instead of "
+ "the key. Thus way iterator will continue with proper value after incrementation.");
+ DebugBreak();
+ return;
+ }
+ InternalUnset(key);
+ }
+
+ /**
+ * Removes value from the dictionary by the given key (if exists).
+ */
+ void InternalUnset(const K key) {
if (ArraySize(_DictSlots_ref.DictSlots) == 0) {
// Nothing to unset.
return;
}
- unsigned int position = Hash(key) % ArraySize(_DictSlots_ref.DictSlots);
+ unsigned int position;
+
+ if (GetMode() == DictModeList) {
+ // In list mode value index is the slot index.
+ position = (int)key;
+ } else {
+ position = Hash(key) % ArraySize(_DictSlots_ref.DictSlots);
+ }
+
unsigned int tries_left = ArraySize(_DictSlots_ref.DictSlots);
while (tries_left-- > 0) {
@@ -164,11 +221,32 @@ class DictBase {
return;
}
- if (_DictSlots_ref.DictSlots[position].IsUsed() && _DictSlots_ref.DictSlots[position].HasKey() &&
- _DictSlots_ref.DictSlots[position].key == key) {
- // Key perfectly matches, it indicates key exists in the dictionary.
+ bool _should_be_removed = false;
+
+ if (_DictSlots_ref.DictSlots[position].IsUsed()) {
+ if (GetMode() == DictModeList) {
+ _should_be_removed = position == (int)key;
+ } else {
+ _should_be_removed =
+ _DictSlots_ref.DictSlots[position].HasKey() && _DictSlots_ref.DictSlots[position].key == key;
+ }
+ }
+
+ if (_should_be_removed) {
+ // Key/index perfectly matches, it indicates key/index exists in the dictionary.
_DictSlots_ref.DictSlots[position].RemoveFlags(DICT_SLOT_IS_USED);
- --_DictSlots_ref._num_used;
+
+ if (GetMode() == DictModeDict) {
+ // In List mode we don't decrement number of used elements.
+ --_DictSlots_ref._num_used;
+ } else if (HasFlags(DICT_FLAG_FILL_HOLES_UNSORTED)) {
+ // This is List mode and we need to fill this hole.
+ FillHoleUnsorted(position);
+ }
+ return;
+ } else if (GetMode() == DictModeList) {
+ Print("Internal error. Slot should have been removed!");
+ DebugBreak();
return;
}
@@ -179,6 +257,24 @@ class DictBase {
// No key found.
}
+ /**
+ * Moves last slot to given one to fill the hole after removing the value.
+ */
+ void FillHoleUnsorted(int _hole_slot_idx) {
+ // After moving last element to fill the hole we
+ if (_hole_slot_idx == Size() - 1) {
+ // We've just removed last element, thus don't need to do anything.
+ } else {
+ // Moving last slot into given one.
+ _DictSlots_ref.DictSlots[_hole_slot_idx] = _DictSlots_ref.DictSlots[Size() - 1];
+
+ // Marking last slot as unused.
+ _DictSlots_ref.DictSlots[Size() - 1].RemoveFlags(DICT_SLOT_IS_USED);
+ }
+ // One element less in the List-based Dict.
+ --_DictSlots_ref._num_used;
+ }
+
/**
* Returns number of used DictSlots.
*/
diff --git a/DictIteratorBase.mqh b/DictIteratorBase.mqh
index 330a3c84d..16fafb8a8 100644
--- a/DictIteratorBase.mqh
+++ b/DictIteratorBase.mqh
@@ -26,6 +26,7 @@
#endif
#include "DictBase.mqh"
+#include "DictSlotsRef.h"
#include "SerializerConversions.h"
template
@@ -36,20 +37,23 @@ class DictIteratorBase {
protected:
DictBase* _dict;
int _hash;
- unsigned int _slotIdx;
- unsigned int _index;
+ int _slotIdx;
+ int _index;
+ bool _invalid_until_incremented;
public:
/**
* Constructor.
*/
- DictIteratorBase() : _dict(NULL) {}
+ DictIteratorBase() : _dict(NULL) { _invalid_until_incremented = false; }
/**
* Constructor.
*/
- DictIteratorBase(DictBase& dict, unsigned int slotIdx)
- : _dict(&dict), _hash(dict.GetHash()), _slotIdx(slotIdx), _index(0) {}
+ DictIteratorBase(DictBase& dict, int slotIdx)
+ : _dict(&dict), _hash(dict.GetHash()), _slotIdx(slotIdx), _index(0) {
+ _invalid_until_incremented = false;
+ }
/**
* Copy constructor.
@@ -58,7 +62,9 @@ class DictIteratorBase {
: _dict(right._dict),
_hash(right._dict ? right._dict.GetHash() : 0),
_slotIdx(right._slotIdx),
- _index(right._index) {}
+ _index(right._index) {
+ _invalid_until_incremented = false;
+ }
/**
* Iterator incrementation operator.
@@ -67,6 +73,7 @@ class DictIteratorBase {
// Going to the next slot.
++_slotIdx;
++_index;
+ _invalid_until_incremented = false;
DictSlot* slot = _dict.GetSlot(_slotIdx);
@@ -83,15 +90,31 @@ class DictIteratorBase {
bool HasKey() { return _dict.GetSlot(_slotIdx).HasKey(); }
- K Key() { return _dict.GetMode() == DictModeList ? (K)_slotIdx : _dict.GetSlot(_slotIdx).key; }
+ K Key() {
+ CheckValidity();
+ return _dict.GetMode() == DictModeList ? (K)_slotIdx : _dict.GetSlot(_slotIdx).key;
+ }
string KeyAsString(bool includeQuotes = false) {
return HasKey() ? SerializerConversions::ValueToString(Key(), includeQuotes) : "";
}
- unsigned int Index() { return _index; }
+ int Index() {
+ CheckValidity();
+ return _index;
+ }
+
+ V Value() {
+ CheckValidity();
+ return _dict.GetSlot(_slotIdx).value;
+ }
- V Value() { return _dict.GetSlot(_slotIdx).value; }
+ void CheckValidity() {
+ if (_invalid_until_incremented) {
+ Alert("Iterator must be incremented before using it again!");
+ DebugBreak();
+ }
+ }
bool IsValid() { return _dict != NULL; }
@@ -108,22 +131,10 @@ class DictIteratorBase {
return _index == _dict.Size() - 1;
}
-};
-
-template
-class DictSlot;
-
-template
-struct DictSlotsRef {
- DictSlot DictSlots[];
-
- // Incremental index for dict operating in list mode.
- unsigned int _list_index;
-
- unsigned int _num_used;
- DictSlotsRef() {
- _list_index = 0;
- _num_used = 0;
+ void ShiftPosition(int shift, bool invalid_until_incremented = false) {
+ _slotIdx += shift;
+ _index += shift;
+ _invalid_until_incremented |= invalid_until_incremented;
}
};
diff --git a/DictObject.mqh b/DictObject.mqh
index 25bcba852..4491707cb 100644
--- a/DictObject.mqh
+++ b/DictObject.mqh
@@ -114,7 +114,7 @@ class DictObject : public DictBase {
* Inserts or replaces value for a given key.
*/
bool Set(K key, V& value) {
- if (!InsertInto(_DictSlots_ref, key, value)) return false;
+ if (!InsertInto(_DictSlots_ref, key, value, true)) return false;
return true;
}
@@ -191,7 +191,14 @@ class DictObject : public DictBase {
/**
* Inserts value into given array of DictSlots.
*/
- bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value) {
+ bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value, bool allow_resize) {
+ // Will resize dict if there were performance problems before.
+ if (allow_resize && !dictSlotsRef.IsPerformant()) {
+ if (!GrowUp()) {
+ return false;
+ }
+ }
+
if (_mode == DictModeUnknown)
_mode = DictModeDict;
else if (_mode != DictModeDict) {
@@ -214,8 +221,8 @@ class DictObject : public DictBase {
}
if (keySlot == NULL) {
- // We need to expand array of DictSlotsRef.DictSlots (by 25%).
- if (!Resize(MathMax(10, (int)((float)ArraySize(dictSlotsRef.DictSlots) * 1.25)))) return false;
+ // We need to expand array of DictSlotsRef.DictSlots.
+ if (!GrowUp()) return false;
}
}
@@ -255,6 +262,8 @@ class DictObject : public DictBase {
// Slot overwrite is not needed. Using empty slot.
++dictSlotsRef._num_used;
}
+
+ dictSlotsRef.AddConflicts(_num_conflicts);
}
dictSlotsRef.DictSlots[position].key = key;
@@ -276,8 +285,8 @@ class DictObject : public DictBase {
}
if (dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots)) {
- // No DictSlotsRef.DictSlots available, we need to expand array of DictSlotsRef.DictSlots (by 25%).
- if (!Resize(MathMax(10, (int)((float)ArraySize(dictSlotsRef.DictSlots) * 1.25)))) return false;
+ // No DictSlotsRef.DictSlots available, we need to expand array of DictSlotsRef.DictSlots.
+ if (!GrowUp()) return false;
}
unsigned int position = Hash((unsigned int)dictSlotsRef._list_index) % ArraySize(dictSlotsRef.DictSlots);
@@ -296,10 +305,17 @@ class DictObject : public DictBase {
return true;
}
+ /**
+ * Expands array of DictSlots by given percentage value.
+ */
+ bool GrowUp(int percent = DICT_GROW_UP_PERCENT_DEFAULT) {
+ return Resize(MathMax(10, (int)((float)ArraySize(_DictSlots_ref.DictSlots) * ((float)(percent + 100) / 100.0f))));
+ }
+
/**
* Shrinks or expands array of DictSlots.
*/
- bool Resize(unsigned int new_size) {
+ bool Resize(int new_size) {
if (new_size <= MathMin(_DictSlots_ref._num_used, ArraySize(_DictSlots_ref.DictSlots))) {
// We already use minimum number of slots possible.
return true;
@@ -307,14 +323,20 @@ class DictObject : public DictBase {
DictSlotsRef new_DictSlots;
+ int i;
+
if (ArrayResize(new_DictSlots.DictSlots, new_size) == -1) return false;
+ for (i = 0; i < new_size; ++i) {
+ new_DictSlots.DictSlots[i].SetFlags(0);
+ }
+
// Copies entire array of DictSlots into new array of DictSlots. Hashes will be rehashed.
- for (unsigned int i = 0; i < (unsigned int)ArraySize(_DictSlots_ref.DictSlots); ++i) {
+ for (i = 0; i < ArraySize(_DictSlots_ref.DictSlots); ++i) {
if (!_DictSlots_ref.DictSlots[i].IsUsed()) continue;
if (_DictSlots_ref.DictSlots[i].HasKey()) {
- if (!InsertInto(new_DictSlots, _DictSlots_ref.DictSlots[i].key, _DictSlots_ref.DictSlots[i].value))
+ if (!InsertInto(new_DictSlots, _DictSlots_ref.DictSlots[i].key, _DictSlots_ref.DictSlots[i].value, false))
return false;
} else {
if (!InsertInto(new_DictSlots, _DictSlots_ref.DictSlots[i].value)) return false;
diff --git a/DictSlotsRef.h b/DictSlotsRef.h
new file mode 100644
index 000000000..140140c49
--- /dev/null
+++ b/DictSlotsRef.h
@@ -0,0 +1,72 @@
+//+------------------------------------------------------------------+
+//| EA31337 framework |
+//| Copyright 2016-2021, EA31337 Ltd |
+//| https://github.com/EA31337 |
+//+------------------------------------------------------------------+
+
+/*
+ * This file is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+/**
+ * @file
+ * DictSlotsRef struct.
+ */
+
+#ifndef __MQL__
+// Allows the preprocessor to include a header file when it is needed.
+#pragma once
+#endif
+
+// Includes.
+#include "Dict.enum.h"
+
+template
+class DictSlot;
+
+template
+struct DictSlotsRef {
+ DictSlot DictSlots[];
+
+ // Incremental index for dict operating in list mode.
+ int _list_index;
+
+ int _num_used;
+
+ int _num_conflicts;
+
+ float _avg_conflicts;
+
+ DictSlotsRef() {
+ _list_index = 0;
+ _num_used = 0;
+ _num_conflicts = 0;
+ _avg_conflicts = 0;
+ }
+
+ /**
+ * Adds given number of conflicts for an insert action, so we can store average number of conflicts.
+ */
+ void AddConflicts(int num) {
+ if (num != 0) {
+ _avg_conflicts += float(num) / ++_num_conflicts;
+ }
+ }
+
+ /**
+ * Checks whethere there is no performance problems with slots.
+ */
+ bool IsPerformant() { return _avg_conflicts < DICT_PERFORMANCE_PROBLEM_AVG_CONFLICTS; }
+};
diff --git a/DictStruct.mqh b/DictStruct.mqh
index 9c9647cdf..99d39cc90 100644
--- a/DictStruct.mqh
+++ b/DictStruct.mqh
@@ -126,7 +126,7 @@ class DictStruct : public DictBase {
* Inserts or replaces value for a given key.
*/
bool Set(K key, V& value) {
- if (!InsertInto(_DictSlots_ref, key, value)) return false;
+ if (!InsertInto(_DictSlots_ref, key, value, true)) return false;
return true;
}
@@ -234,7 +234,14 @@ class DictStruct : public DictBase {
/**
* Inserts value into given array of DictSlots.
*/
- bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value) {
+ bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value, bool allow_resize) {
+ // Will resize dict if there were performance problems before.
+ if (allow_resize && !dictSlotsRef.IsPerformant()) {
+ if (!GrowUp()) {
+ return false;
+ }
+ }
+
if (_mode == DictModeUnknown)
_mode = DictModeDict;
else if (_mode != DictModeDict) {
@@ -256,8 +263,8 @@ class DictStruct : public DictBase {
}
if (keySlot == NULL) {
- // We need to expand array of DictSlotsRef.DictSlots (by 25%).
- if (!Resize(MathMax(10, (int)((float)ArraySize(dictSlotsRef.DictSlots) * 1.25)))) return false;
+ // We need to expand array of DictSlotsRef.DictSlots.
+ if (!GrowUp()) return false;
}
}
@@ -297,6 +304,8 @@ class DictStruct : public DictBase {
// Slot overwrite is not needed. Using empty slot.
++dictSlotsRef._num_used;
}
+
+ dictSlotsRef.AddConflicts(_num_conflicts);
}
dictSlotsRef.DictSlots[position].key = key;
@@ -317,8 +326,8 @@ class DictStruct : public DictBase {
}
if (dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots)) {
- // No DictSlotsRef.DictSlots available, we need to expand array of DictSlotsRef.DictSlots (by 25%).
- if (!Resize(MathMax(10, (int)((float)ArraySize(dictSlotsRef.DictSlots) * 1.25)))) return false;
+ // No DictSlotsRef.DictSlots available, we need to expand array of DictSlotsRef.DictSlots.
+ if (!GrowUp()) return false;
}
unsigned int position = Hash((unsigned int)dictSlotsRef._list_index) % ArraySize(dictSlotsRef.DictSlots);
@@ -337,10 +346,17 @@ class DictStruct : public DictBase {
return true;
}
+ /**
+ * Expands array of DictSlots by given percentage value.
+ */
+ bool GrowUp(int percent = DICT_GROW_UP_PERCENT_DEFAULT) {
+ return Resize(MathMax(10, (int)((float)ArraySize(_DictSlots_ref.DictSlots) * ((float)(percent + 100) / 100.0f))));
+ }
+
/**
* Shrinks or expands array of DictSlots.
*/
- bool Resize(unsigned int new_size) {
+ bool Resize(int new_size) {
if (new_size <= MathMin(_DictSlots_ref._num_used, ArraySize(_DictSlots_ref.DictSlots))) {
// We already use minimum number of slots possible.
return true;
@@ -350,12 +366,18 @@ class DictStruct : public DictBase {
if (ArrayResize(new_DictSlots.DictSlots, new_size) == -1) return false;
+ int i;
+
+ for (i = 0; i < new_size; ++i) {
+ new_DictSlots.DictSlots[i].SetFlags(0);
+ }
+
// Copies entire array of DictSlots into new array of DictSlots. Hashes will be rehashed.
- for (unsigned int i = 0; i < (unsigned int)ArraySize(_DictSlots_ref.DictSlots); ++i) {
+ for (i = 0; i < ArraySize(_DictSlots_ref.DictSlots); ++i) {
if (!_DictSlots_ref.DictSlots[i].IsUsed()) continue;
if (_DictSlots_ref.DictSlots[i].HasKey()) {
- if (!InsertInto(new_DictSlots, _DictSlots_ref.DictSlots[i].key, _DictSlots_ref.DictSlots[i].value))
+ if (!InsertInto(new_DictSlots, _DictSlots_ref.DictSlots[i].key, _DictSlots_ref.DictSlots[i].value, false))
return false;
} else {
if (!InsertInto(new_DictSlots, _DictSlots_ref.DictSlots[i].value)) return false;
diff --git a/Draw.mqh b/Draw.mqh
index bcfcfe647..350f89bf6 100644
--- a/Draw.mqh
+++ b/Draw.mqh
@@ -31,6 +31,7 @@ class Draw;
// Includes.
#include "Chart.mqh"
+#include "Data.define.h"
#ifndef __MQL4__
// Defines macros (for MQL4 backward compatibility).
diff --git a/DrawIndicator.mqh b/DrawIndicator.mqh
index 35e5557b7..7017a81eb 100644
--- a/DrawIndicator.mqh
+++ b/DrawIndicator.mqh
@@ -36,7 +36,7 @@
#include "Object.mqh"
// Forward declaration.
-class Indicator;
+class IndicatorBase;
class DrawPoint {
public:
@@ -56,7 +56,7 @@ class DrawIndicator {
protected:
color color_line;
Draw* draw;
- Indicator* indi;
+ IndicatorBase* indi;
public:
// Object variables.
@@ -67,8 +67,8 @@ class DrawIndicator {
/**
* Class constructor.
*/
- DrawIndicator(Indicator* _indi) : indi(_indi) {
- color_line = Object::IsValid(_indi) ? _indi.GetParams().indi_color : clrRed;
+ DrawIndicator(IndicatorBase* _indi) : indi(_indi) {
+ // color_line = Object::IsValid(_indi) ? _indi.GetParams().indi_color : clrRed; // @fixme
draw = new Draw();
}
diff --git a/EA.mqh b/EA.mqh
index ee5d6ce23..9a602ef76 100644
--- a/EA.mqh
+++ b/EA.mqh
@@ -49,6 +49,8 @@
#include "Task.mqh"
#include "Terminal.mqh"
#include "Trade.mqh"
+#include "Trade/TradeSignal.h"
+#include "Trade/TradeSignalManager.h"
class EA {
protected:
@@ -61,7 +63,6 @@ class EA {
// Data variables.
BufferStruct data_chart;
- BufferStruct> strat_signals;
BufferStruct data_symbol;
Dict ddata; // Custom user data.
Dict idata; // Custom user data.
@@ -72,6 +73,7 @@ class EA {
EAParams eparams;
EAProcessResult eresults;
EAState estate;
+ TradeSignalManager tsm;
public:
/**
@@ -126,6 +128,56 @@ class EA {
return trade.GetByKey(_symbol != NULL ? _symbol : _Symbol).Get(_state);
}
+ /**
+ * Gets a strategy's signal entry.
+ *
+ * @param Strategy _strat
+ * Reference to strategy to get the signal from.
+ * @param bool _trade_allowed
+ * True if trade is allowed.
+ * @param int _shift
+ * Bar shift.
+ *
+ * @return
+ * Returns TradeSignalEntry struct.
+ */
+ TradeSignalEntry GetStrategySignalEntry(Strategy *_strat, bool _trade_allowed = true, int _shift = -1) {
+ // float _bf = 1.0;
+ float _scl = _strat.Get(STRAT_PARAM_SCL);
+ float _sol = _strat.Get(STRAT_PARAM_SOL);
+ int _scfm = _strat.Get(STRAT_PARAM_SCFM);
+ int _scft = _strat.Get(STRAT_PARAM_SCFT);
+ int _scm = _strat.Get(STRAT_PARAM_SCM);
+ int _sob = _strat.Get(STRAT_PARAM_SOB);
+ int _sofm = _strat.Get(STRAT_PARAM_SOFM);
+ int _soft = _strat.Get(STRAT_PARAM_SOFT);
+ int _som = _strat.Get(STRAT_PARAM_SOM);
+ int _ss = _shift >= 0 ? _shift : _strat.Get(STRAT_PARAM_SHIFT);
+ unsigned int _signals = 0;
+ // sparams.Get(STRAT_PARAM_WEIGHT));
+ if (_trade_allowed) {
+ // Process boost factor and lot size.
+ // sresult.SetBoostFactor(sparams.IsBoosted() ? SignalOpenBoost(ORDER_TYPE_BUY, _sob) : 1.0f);
+ // sresult.SetLotSize(sparams.GetLotSizeWithFactor());
+ // Process open signals when trade is allowed.
+ _signals |= _strat.SignalOpen(ORDER_TYPE_BUY, _som, _sol, _ss) ? SIGNAL_OPEN_BUY_MAIN : 0;
+ _signals |= !_strat.SignalOpenFilterMethod(ORDER_TYPE_BUY, _sofm) ? SIGNAL_OPEN_BUY_FILTER : 0;
+ _signals |= _strat.SignalOpen(ORDER_TYPE_SELL, _som, _sol, _ss) ? SIGNAL_OPEN_SELL_MAIN : 0;
+ _signals |= !_strat.SignalOpenFilterMethod(ORDER_TYPE_SELL, _sofm) ? SIGNAL_OPEN_SELL_FILTER : 0;
+ _signals |= !_strat.SignalOpenFilterTime(_soft) ? SIGNAL_OPEN_TIME_FILTER : 0;
+ }
+ // Process close signals.
+ _signals |= _strat.SignalClose(ORDER_TYPE_BUY, _scm, _scl, _ss) ? SIGNAL_CLOSE_BUY_MAIN : 0;
+ _signals |= !_strat.SignalCloseFilter(ORDER_TYPE_BUY, _scfm) ? SIGNAL_CLOSE_BUY_FILTER : 0;
+ _signals |= _strat.SignalClose(ORDER_TYPE_SELL, _scm, _scl, _ss) ? SIGNAL_CLOSE_SELL_MAIN : 0;
+ _signals |= !_strat.SignalCloseFilter(ORDER_TYPE_SELL, _scfm) ? SIGNAL_CLOSE_SELL_FILTER : 0;
+ _signals |= !_strat.SignalCloseFilterTime(_scfm) ? SIGNAL_OPEN_TIME_FILTER : 0;
+ TradeSignalEntry _sentry(_signals, _strat.Get(STRAT_PARAM_TF), _strat.Get(STRAT_PARAM_ID));
+ _sentry.Set(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_PROP_STRENGTH), _strat.SignalOpen(_sofm, _sol, _ss));
+ _sentry.Set(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_PROP_TIME), ::TimeGMT());
+ return _sentry;
+ }
+
/* Setters */
/**
@@ -192,32 +244,34 @@ class EA {
bool _result = true;
int _last_error = ERR_NO_ERROR;
ResetLastError();
- DictStruct _ds = strat_signals.GetByKey(_tick.time);
- for (DictStructIterator _dsi = _ds.Begin(); _dsi.IsValid(); ++_dsi) {
- StrategySignal _signal = _dsi.Value();
- if (_signal.CheckSignals(STRAT_SIGNAL_PROCESSED)) {
+ for (DictObjectIterator _iter = tsm.GetIterSignalsActive(); _iter.IsValid(); ++_iter) {
+ bool _result_local = true;
+ TradeSignal *_signal = _iter.Value();
+ if (_signal.Get(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_FLAG_PROCESSED))) {
// Ignores already processed signals.
- // @todo: Not in use yet.
continue;
}
- Strategy *_strat = _signal.GetStrategy();
Trade *_trade = trade.GetByKey(_Symbol);
+ Strategy *_strat =
+ strats.GetByKey(_signal.Get(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_PROP_MAGIC_ID))).Ptr();
if (_trade.Get(TRADE_STATE_ORDERS_ACTIVE)) {
float _sig_close = _signal.GetSignalClose();
+ string _comment_close =
+ _strat != NULL && _sig_close != 0.0f ? _strat.GetOrderCloseComment() : __FUNCTION_LINE__;
// Check if we should close the orders.
if (_sig_close >= 0.5f) {
// Close signal for buy order.
- _result &= _trade.OrdersCloseViaProp2(
- ORDER_MAGIC, _strat.Get(STRAT_PARAM_ID), ORDER_TYPE, ORDER_TYPE_BUY, MATH_COND_EQ,
- ORDER_REASON_CLOSED_BY_SIGNAL, _strat.GetOrderCloseComment());
+ _trade.OrdersCloseViaProp2(
+ ORDER_MAGIC, _signal.Get(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_PROP_MAGIC_ID)), ORDER_TYPE,
+ ORDER_TYPE_BUY, MATH_COND_EQ, ORDER_REASON_CLOSED_BY_SIGNAL, _comment_close);
// Buy orders closed.
_strat.OnOrderClose(ORDER_TYPE_BUY);
}
if (_sig_close <= -0.5f) {
// Close signal for sell order.
- _result &= _trade.OrdersCloseViaProp2(
- ORDER_MAGIC, _strat.Get(STRAT_PARAM_ID), ORDER_TYPE, ORDER_TYPE_SELL, MATH_COND_EQ,
- ORDER_REASON_CLOSED_BY_SIGNAL, _strat.GetOrderCloseComment());
+ _trade.OrdersCloseViaProp2(
+ ORDER_MAGIC, _signal.Get(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_PROP_MAGIC_ID)), ORDER_TYPE,
+ ORDER_TYPE_SELL, MATH_COND_EQ, ORDER_REASON_CLOSED_BY_SIGNAL, _comment_close);
// Sell orders closed.
_strat.OnOrderClose(ORDER_TYPE_SELL);
}
@@ -225,15 +279,17 @@ class EA {
if (_trade_allowed) {
float _sig_open = _signal.GetSignalOpen();
unsigned int _sig_f = eparams.Get(STRUCT_ENUM(EAParams, EA_PARAM_PROP_SIGNAL_FILTER));
+ string _comment_open = _strat != NULL && _sig_open != 0.0f ? _strat.GetOrderOpenComment() : __FUNCTION_LINE__;
// Open orders on signals.
if (_sig_open >= 0.5f) {
// Open signal for buy.
// When H1 or H4 signal filter is enabled, do not open minute-based orders on opposite or neutral signals.
- if (_sig_f == 0 || GetSignalOpenFiltered(_signal, _sig_f) >= 0.5f) {
- _strat.Set(TRADE_PARAM_ORDER_COMMENT, _strat.GetOrderOpenComment("B:"));
+ if (_sig_f == 0) { // @fixme: || GetSignalOpenFiltered(_signal, _sig_f) >= 0.5f) {
+ _strat.Set(TRADE_PARAM_ORDER_COMMENT, _comment_open);
// Buy order open.
- _result &= TradeRequest(ORDER_TYPE_BUY, _Symbol, _strat);
- if (_result && eparams.CheckSignalFilter(STRUCT_ENUM(EAParams, EA_PARAM_SIGNAL_FILTER_FIRST))) {
+ _result_local &= TradeRequest(ORDER_TYPE_BUY, _Symbol, _strat);
+ if (_result_local && eparams.CheckSignalFilter(STRUCT_ENUM(EAParams, EA_PARAM_SIGNAL_FILTER_FIRST))) {
+ _signal.Set(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_FLAG_PROCESSED), true);
break;
}
}
@@ -241,16 +297,19 @@ class EA {
if (_sig_open <= -0.5f) {
// Open signal for sell.
// When H1 or H4 signal filter is enabled, do not open minute-based orders on opposite or neutral signals.
- if (_sig_f == 0 || GetSignalOpenFiltered(_signal, _sig_f) <= -0.5f) {
- _strat.Set(TRADE_PARAM_ORDER_COMMENT, _strat.GetOrderOpenComment("S:"));
+ if (_sig_f == 0) { // @fixme: || GetSignalOpenFiltered(_signal, _sig_f) <= -0.5f) {
+ _strat.Set(TRADE_PARAM_ORDER_COMMENT, _comment_open);
// Sell order open.
- _result &= TradeRequest(ORDER_TYPE_SELL, _Symbol, _strat);
- if (_result && eparams.CheckSignalFilter(STRUCT_ENUM(EAParams, EA_PARAM_SIGNAL_FILTER_FIRST))) {
+ _result_local &= TradeRequest(ORDER_TYPE_SELL, _Symbol, _strat);
+ if (_result_local && eparams.CheckSignalFilter(STRUCT_ENUM(EAParams, EA_PARAM_SIGNAL_FILTER_FIRST))) {
+ _signal.Set(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_FLAG_PROCESSED), true);
break;
}
}
}
- if (!_result) {
+ if (_result_local) {
+ _signal.Set(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_FLAG_PROCESSED), true);
+ } else {
_last_error = GetLastError();
switch (_last_error) {
case ERR_NOT_ENOUGH_MEMORY:
@@ -262,13 +321,14 @@ class EA {
}
}
}
+ _result &= _result_local;
}
_last_error = GetLastError();
if (_last_error > 0) {
logger.Warning(StringFormat("Processing signals failed! Code: %d", _last_error), __FUNCTION_LINE__);
}
- // Remove signals after processing.
- strat_signals.Unset(_tick.time);
+ // Refresh signals after processing.
+ tsm.Refresh();
return _result && _last_error == 0;
}
@@ -328,20 +388,22 @@ class EA {
_can_trade &= _can_trade && !_strat.IsSuspended();
_can_trade &= _can_trade && !_strat.CheckCondition(STRAT_COND_TRADE_COND, TRADE_COND_HAS_STATE,
TRADE_STATE_TRADE_CANNOT);
- StrategySignal _signal = _strat.ProcessSignals(_can_trade);
- if (_signal.GetSignalClose() != _signal.GetSignalOpen()) {
- SignalAdd(_signal, _tick.time);
+ TradeSignalEntry _sentry = GetStrategySignalEntry(_strat, _can_trade, _strat.Get(STRAT_PARAM_SHIFT));
+ if (_sentry.Get(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_PROP_SIGNALS)) > 0) {
+ TradeSignal _signal(_sentry);
+ if (_signal.GetSignalClose() != _signal.GetSignalOpen()) {
+ tsm.SignalAdd(_signal); //, _tick.time);
+ }
+ StgProcessResult _strat_result = _strat.GetProcessResult();
+ eresults.last_error = fmax(eresults.last_error, _strat_result.last_error);
+ eresults.stg_errored += (int)_strat_result.last_error > ERR_NO_ERROR;
+ eresults.stg_processed++;
}
- StgProcessResult _strat_result = _strat.GetProcessResult();
- eresults.last_error = fmax(eresults.last_error, _strat_result.last_error);
- eresults.stg_errored += (int)_strat_result.last_error > ERR_NO_ERROR;
- eresults.stg_processed++;
}
}
}
// Process all strategies' signals and trigger trading orders.
- ProcessSignals(GetMarket().GetLastTick(),
- eparams.Get(STRUCT_ENUM(EAParams, EA_PARAM_PROP_SIGNAL_FILTER)));
+ ProcessSignals(_tick, eparams.Get(STRUCT_ENUM(EAParams, EA_PARAM_PROP_SIGNAL_FILTER)));
if (eresults.last_error > ERR_NO_ERROR) {
// On error, print logs.
logger.Flush();
@@ -373,7 +435,7 @@ class EA {
if (eparams.CheckFlagDataStore(EA_DATA_STORE_INDICATOR)) {
for (DictStructIterator> iter = strats.Begin(); iter.IsValid(); ++iter) {
Strategy *_strati = iter.Value().Ptr();
- Indicator *_indi = _strati.GetParams().GetIndicator();
+ IndicatorBase *_indi = _strati.GetIndicator();
if (_indi != NULL) {
ENUM_TIMEFRAMES _itf = _indi.GetParams().tf.GetTf();
IndicatorDataEntry _ientry = _indi.GetEntry();
@@ -574,6 +636,7 @@ class EA {
* @return
* Returns 1 when buy signal exists, -1 for sell, otherwise 0 for neutral signal.
*/
+ /* @fixme: Convert into TradeSignal format.
float GetSignalOpenFiltered(StrategySignal &_signal, unsigned int _sf) {
float _result = _signal.GetSignalOpen();
ENUM_TIMEFRAMES _sig_tf = _signal.Get(STRUCT_ENUM(StrategySignal, STRATEGY_SIGNAL_PROP_TF));
@@ -600,15 +663,7 @@ class EA {
}
return _result;
}
-
- /**
- * Adds strategy's signal for further processing.
- */
- bool SignalAdd(StrategySignal &_signal, long _time) {
- DictStruct _ds = strat_signals.GetByKey(_time);
- _ds.Push(_signal);
- return strat_signals.Set(_time, _ds);
- }
+ */
/* Tasks */
@@ -742,7 +797,9 @@ class EA {
Order *_order = oiter.Value().Ptr();
if (!_order.ShouldUpdate()) {
continue;
- } else if (_order.IsClosed()) {
+ }
+ _order.ProcessConditions();
+ if (_order.IsClosed()) {
_trade.OrderMoveToHistory(_order);
continue;
}
@@ -1042,6 +1099,7 @@ class EA {
}
if ((estate.new_periods & DATETIME_HOUR) != 0) {
// New hour started.
+ tsm.Refresh();
}
if ((estate.new_periods & DATETIME_DAY) != 0) {
// New day started.
@@ -1052,7 +1110,6 @@ class EA {
}
if ((estate.new_periods & DATETIME_WEEK) != 0) {
// New week started.
- strat_signals.Clear();
}
if ((estate.new_periods & DATETIME_MONTH) != 0) {
// New month started.
diff --git a/EA.struct.h b/EA.struct.h
index aa36c2e5e..cc8cda2ba 100644
--- a/EA.struct.h
+++ b/EA.struct.h
@@ -222,16 +222,16 @@ struct EAProcessResult {
/* Defines EA state variables. */
struct EAState {
- unsigned short flags; // Action flags.
+ unsigned int flags; // Action flags.
unsigned int new_periods; // Started periods.
DateTime last_updated; // Last updated.
// Constructor.
- EAState() { AddFlags(EA_STATE_FLAG_ACTIVE | EA_STATE_FLAG_ENABLED); }
+ EAState() { EAState::AddFlags(EA_STATE_FLAG_ACTIVE | EA_STATE_FLAG_ENABLED); }
// Struct methods.
// Flag methods.
- bool CheckFlag(unsigned short _flag) { return bool(flags & _flag); }
- void AddFlags(unsigned short _flags) { flags |= _flags; }
- void RemoveFlags(unsigned short _flags) { flags &= ~_flags; }
+ bool CheckFlag(unsigned int _flag) { return bool(flags & _flag); }
+ void AddFlags(unsigned int _flags) { flags |= _flags; }
+ void RemoveFlags(unsigned int _flags) { flags &= ~_flags; }
void SetFlag(ENUM_EA_STATE_FLAGS _flag, bool _value) {
if (_value) {
AddFlags(_flag);
@@ -239,7 +239,7 @@ struct EAState {
RemoveFlags(_flag);
}
}
- void SetFlags(unsigned short _flags) { flags = _flags; }
+ void SetFlags(unsigned int _flags) { flags = _flags; }
// State methods.
bool IsActive() { return CheckFlag(EA_STATE_FLAG_ACTIVE); }
bool IsConnected() { return CheckFlag(EA_STATE_FLAG_CONNECTED); }
diff --git a/Indicator.define.h b/Indicator.define.h
index 40e0d811c..08c221d64 100644
--- a/Indicator.define.h
+++ b/Indicator.define.h
@@ -41,14 +41,14 @@
return EMPTY_VALUE; \
} \
} \
- int _bars_calc = BarsCalculated(_handle); \
+ int _bars_calc = ::BarsCalculated(_handle); \
if (GetLastError() > 0) { \
return EMPTY_VALUE; \
} else if (_bars_calc <= 2) { \
SetUserError(ERR_USER_INVALID_BUFF_NUM); \
return EMPTY_VALUE; \
} \
- if (CopyBuffer(_handle, _mode, _shift, 1, _res) < 0) { \
+ if (::CopyBuffer(_handle, _mode, _shift, 1, _res) < 0) { \
return EMPTY_VALUE; \
} \
return _res[0];
diff --git a/Indicator.enum.h b/Indicator.enum.h
index 1ed25def6..fe21abd88 100644
--- a/Indicator.enum.h
+++ b/Indicator.enum.h
@@ -82,6 +82,7 @@ enum ENUM_INDICATOR_TYPE {
INDI_GATOR, // Gator Oscillator
INDI_HEIKENASHI, // Heiken Ashi
INDI_ICHIMOKU, // Ichimoku Kinko Hyo
+ INDI_KILLZONES, // Killzones
INDI_MA, // Moving Average
INDI_MACD, // MACD
INDI_MA_ON_PRICE, // Moving Average (on Price).
@@ -127,10 +128,12 @@ enum ENUM_INDICATOR_TYPE {
/* Defines type of source data for indicator. */
enum ENUM_IDATA_SOURCE_TYPE {
- IDATA_BUILTIN, // Platform built-in
+ IDATA_BUILTIN = 0, // Platform built-in
+ IDATA_CHART, // Chart calculation
IDATA_ICUSTOM, // iCustom: Custom indicator file
IDATA_ICUSTOM_LEGACY, // iCustom: Custom, legacy, provided by MT indicator file
IDATA_INDICATOR, // OnIndicator: Another indicator as a source of data
+ IDATA_ONCALCULATE, // OnCalculate: Custom calculation function
IDATA_MATH // Math-based indicator
};
diff --git a/Indicator.mqh b/Indicator.mqh
index b13030b5f..3717c10aa 100644
--- a/Indicator.mqh
+++ b/Indicator.mqh
@@ -39,6 +39,7 @@ class Chart;
#include "Indicator.struct.h"
#include "Indicator.struct.serialize.h"
#include "Indicator.struct.signal.h"
+#include "IndicatorBase.h"
#include "Math.h"
#include "Object.mqh"
#include "Refs.mqh"
@@ -49,34 +50,14 @@ class Chart;
#include "Storage/ValueStorage.indicator.h"
#include "Storage/ValueStorage.native.h"
-#ifndef __MQL4__
-// Defines global functions (for MQL4 backward compatibility).
-bool IndicatorBuffers(int _count) { return Indicator::SetIndicatorBuffers(_count); }
-int IndicatorCounted(int _value = 0) {
- static int prev_calculated = 0;
- // https://docs.mql4.com/customind/indicatorcounted
- prev_calculated = _value > 0 ? _value : prev_calculated;
- return prev_calculated;
-}
-#endif
-
/**
* Class to deal with indicators.
*/
-class Indicator : public Chart {
+template
+class Indicator : public IndicatorBase {
protected:
// Structs.
- BufferStruct idata;
- DrawIndicator* draw;
- IndicatorParams iparams;
- IndicatorState istate;
- void* mydata;
- bool is_feeding; // Whether FeedHistoryEntries is already working.
- bool is_fed; // Whether FeedHistoryEntries already done its job.
- DictStruct> indicators; // Indicators list keyed by id.
- bool indicator_builtin;
- ARRAY(ValueStorage*, value_storages);
- IndicatorCalculateCache cache;
+ TS iparams;
public:
/* Indicator enumerations */
@@ -96,20 +77,22 @@ class Indicator : public Chart {
/**
* Class constructor.
*/
- Indicator() {}
- Indicator(IndicatorParams& _iparams) : Chart(_iparams.GetTf()), draw(NULL), is_feeding(false), is_fed(false) {
+ Indicator(const TS& _iparams, IndicatorBase* _indi_src = NULL, bool _indi_managed = true, int _indi_mode = 0)
+ : IndicatorBase(_iparams.GetTf(), NULL) {
iparams = _iparams;
SetName(_iparams.name != "" ? _iparams.name : EnumToString(iparams.itype));
+ if (_indi_src != NULL) {
+ SetDataSource(_indi_src, _indi_managed, _indi_mode);
+ }
Init();
}
- Indicator(const IndicatorParams& _iparams, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT)
- : Chart(_tf), draw(NULL), is_feeding(false), is_fed(false) {
+ Indicator(const TS& _iparams, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : IndicatorBase(_tf) {
iparams = _iparams;
SetName(_iparams.name != "" ? _iparams.name : EnumToString(iparams.itype));
Init();
}
Indicator(ENUM_INDICATOR_TYPE _itype, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0, string _name = "")
- : Chart(_tf), draw(NULL), is_feeding(false), is_fed(false) {
+ : IndicatorBase(_tf) {
iparams.SetIndicatorType(_itype);
iparams.SetShift(_shift);
SetName(_name != "" ? _name : EnumToString(iparams.itype));
@@ -120,22 +103,15 @@ class Indicator : public Chart {
* Class deconstructor.
*/
~Indicator() {
- ReleaseHandle();
DeinitDraw();
- for (int i = 0; i < ArraySize(value_storages); ++i) {
- if (value_storages[i] != NULL) {
- delete value_storages[i];
- }
- }
-
- if (iparams.indi_data_source != NULL && iparams.indi_managed) {
+ if (indi_src != NULL && iparams.indi_managed) {
// User selected custom, managed data source.
- if (CheckPointer(iparams.indi_data_source) == POINTER_INVALID) {
+ if (CheckPointer(indi_src) == POINTER_INVALID) {
DebugBreak();
}
- delete iparams.indi_data_source;
- iparams.indi_data_source = NULL;
+ delete indi_src;
+ indi_src = NULL;
}
}
@@ -143,6 +119,18 @@ class Indicator : public Chart {
bool Init() {
ArrayResize(value_storages, iparams.GetMaxModes());
+ switch (iparams.GetDataSourceType()) {
+ case IDATA_BUILTIN:
+ break;
+ case IDATA_ICUSTOM:
+ break;
+ case IDATA_INDICATOR:
+ if (indi_src == NULL) {
+ // Indi_Price* _indi_price = Indi_Price::GetCached(GetSymbol(), GetTf(), iparams.GetShift());
+ // SetDataSource(_indi_price, true, PRICE_OPEN);
+ }
+ break;
+ }
return InitDraw();
}
@@ -151,7 +139,7 @@ class Indicator : public Chart {
*/
bool InitDraw() {
if (iparams.is_draw && !Object::IsValid(draw)) {
- draw = new DrawIndicator(&this);
+ draw = new DrawIndicator(THIS_PTR);
draw.SetColorLine(iparams.indi_color);
}
return iparams.is_draw;
@@ -168,124 +156,14 @@ class Indicator : public Chart {
}
}
- /* Defines MQL backward compatible methods */
-
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(DUMMY);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, int _mode,
- int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, int _mode,
- int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e,
- int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
- int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
- G _g, int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
- G _g, H _h, int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _h, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
- G _g, H _h, I _i, int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _h, _i, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h COMMA _i);
-#endif
- }
-
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
- G _g, H _h, I _i, J _j, int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h COMMA _i COMMA _j);
-#endif
- }
+ /* Getters */
- template
- double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
- G _g, H _h, I _i, J _j, K _k, int _mode, int _shift) {
-#ifdef __MQL4__
- return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _mode, _shift);
-#else // __MQL5__
- ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h COMMA _i COMMA _j COMMA _k);
-#endif
+ /**
+ * Gets an indicator property flag.
+ */
+ bool GetFlag(INDICATOR_ENTRY_FLAGS _prop, int _shift = -1) {
+ IndicatorDataEntry _entry = GetEntry(_shift >= 0 ? _shift : iparams.GetShift());
+ return _entry.CheckFlag(_prop);
}
/* Buffer methods */
@@ -419,6 +297,39 @@ class Indicator : public Chart {
return _is_valid;
}
+ /**
+ * CopyBuffer() method to be used on Indicator instance with ValueStorage buffer.
+ *
+ * Note that data will be copied so that the oldest element will be located at the start of the physical memory
+ * allocated for the array
+ */
+ /*
+ static int CopyBuffer(IndicatorBase * _indi, int _mode, int _start, int _count, ValueStorage& _buffer,
+ int _rates_total) { int _num_copied = 0; int _buffer_size = ArraySize(_buffer);
+
+ if (_buffer_size < _rates_total) {
+ _buffer_size = ArrayResize(_buffer, _rates_total);
+ }
+
+ for (int i = _start; i < _count; ++i) {
+ IndicatorDataEntry _entry = _indi.GetEntry(i);
+
+ if (!_entry.IsValid()) {
+ break;
+ }
+
+ T _value = _entry.GetValue(_mode);
+
+ // Print(_value);
+
+ _buffer[_buffer_size - i - 1] = _value;
+ ++_num_copied;
+ }
+
+ return _num_copied;
+ }
+ */
+
/**
* Validates currently selected indicator used as data source.
*/
@@ -431,7 +342,7 @@ class Indicator : public Chart {
/**
* Loads and validates built-in indicators whose can be used as data source.
*/
- void ValidateDataSource(Indicator* _target, Indicator* _source) {
+ void ValidateDataSource(IndicatorBase* _target, IndicatorBase* _source) {
if (_target == NULL) {
Alert("Internal Error! _target is NULL in ", __FUNCTION_LINE__, ".");
DebugBreak();
@@ -449,19 +360,18 @@ class Indicator : public Chart {
return;
}
- if (_source.iparams.max_modes > 1 && _target.GetDataSourceMode() == -1) {
+ if (_source.GetModeCount() > 1 && _target.GetDataSourceMode() == -1) {
// Mode must be selected if source indicator has more that one mode.
Alert("Warning! ", GetName(),
" must select source indicator's mode via SetDataSourceMode(int). Defaulting to mode 0.");
- _target.iparams.SetDataSourceMode(0);
+ _target.SetDataSourceMode(0);
DebugBreak();
- } else if (_source.iparams.max_modes == 1 && _target.GetDataSourceMode() == -1) {
- _target.iparams.SetDataSourceMode(0);
- } else if (_target.GetDataSourceMode() < 0 ||
- (unsigned int)_target.GetDataSourceMode() > _source.iparams.max_modes) {
+ } else if (_source.GetModeCount() == 1 && _target.GetDataSourceMode() == -1) {
+ _target.SetDataSourceMode(0);
+ } else if (_target.GetDataSourceMode() < 0 || _target.GetDataSourceMode() > _source.GetModeCount()) {
Alert("Error! ", _target.GetName(),
" must select valid source indicator's mode via SetDataSourceMode(int) between 0 and ",
- _source.iparams.GetMaxModes(), ".");
+ _source.GetModeCount(), ".");
DebugBreak();
}
}
@@ -474,14 +384,15 @@ class Indicator : public Chart {
void ValidateDataSourceMode(int& _out_mode) {
if (_out_mode == -1) {
// First mode will be used by default, or, if selected indicator has more than one mode, error will happen.
- if (iparams.max_modes != 1) {
+ if (iparams.GetMaxModes() != 1) {
Alert("Error: ", GetName(), " must have exactly one possible mode in order to skip using SetDataSourceMode()!");
DebugBreak();
}
_out_mode = 0;
- } else if (_out_mode + 1 > (int)iparams.max_modes) {
- Alert("Error: ", GetName(), " have ", iparams.max_modes, " mode(s) buy you tried to reference mode with index ",
- _out_mode, "! Ensure that you properly set mode via SetDataSourceMode().");
+ } else if (_out_mode + 1 > (int)iparams.GetMaxModes()) {
+ Alert("Error: ", GetName(), " have ", iparams.GetMaxModes(),
+ " mode(s) buy you tried to reference mode with index ", _out_mode,
+ "! Ensure that you properly set mode via SetDataSourceMode().");
DebugBreak();
}
}
@@ -489,47 +400,7 @@ class Indicator : public Chart {
/**
* Provides built-in indicators whose can be used as data source.
*/
- virtual Indicator* FetchDataSource(ENUM_INDICATOR_TYPE _id) { return NULL; }
-
- /**
- * Whether data source is selected.
- */
- bool HasDataSource() { return iparams.GetDataSource() != NULL || iparams.GetDataSourceId() != -1; }
-
- /**
- * Returns currently selected data source without any validation.
- */
- Indicator* GetDataSourceRaw() { return iparams.GetDataSource(); }
-
- /**
- * Returns currently selected data source doing validation.
- */
- Indicator* GetDataSource() {
- Indicator* _result = NULL;
- if (iparams.GetDataSource() != NULL) {
- _result = iparams.GetDataSource();
- } else if (iparams.GetDataSourceId() != -1) {
- int _source_id = iparams.GetDataSourceId();
-
- if (indicators.KeyExists(_source_id)) {
- _result = indicators[_source_id].Ptr();
- } else {
- Ref _source = FetchDataSource((ENUM_INDICATOR_TYPE)_source_id);
-
- if (!_source.IsSet()) {
- Alert(GetName(), " has no built-in source indicator ", _source_id);
- } else {
- indicators.Set(_source_id, _source);
-
- _result = _source.Ptr();
- }
- }
- }
-
- ValidateDataSource(&this, _result);
-
- return _result;
- }
+ virtual IndicatorBase* FetchDataSource(ENUM_INDICATOR_TYPE _id) { return NULL; }
/* Operator overloading methods */
@@ -542,160 +413,6 @@ class Indicator : public Chart {
/* Getters */
- /**
- * Returns buffers' cache.
- */
- IndicatorCalculateCache* GetCache() { return &cache; }
-
- /**
- * Gets an indicator's chart parameter value.
- */
- template
- T Get(ENUM_CHART_PARAM _param) {
- return Chart::Get(_param);
- }
-
- /**
- * Gets an indicator's state property value.
- */
- template
- T Get(STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) _prop) {
- return istate.Get(_prop);
- }
-
- /**
- * Gets an indicator property flag.
- */
- bool GetFlag(INDICATOR_ENTRY_FLAGS _prop, int _shift = 0) {
- IndicatorDataEntry _entry = GetEntry(_shift);
- return _entry.CheckFlag(_prop);
- }
-
- /* State methods */
-
- /**
- * Checks for crossover.
- *
- * @return
- * Returns true when values are crossing over, otherwise false.
- */
- bool IsCrossover(int _shift1 = 0, int _shift2 = 1, int _mode1 = 0, int _mode2 = 0) {
- double _curr_value1 = GetEntry(_shift1)[_mode1];
- double _prev_value1 = GetEntry(_shift2)[_mode1];
- double _curr_value2 = GetEntry(_shift1)[_mode2];
- double _prev_value2 = GetEntry(_shift2)[_mode2];
- return ((_curr_value1 > _prev_value1 && _curr_value2 < _prev_value2) ||
- (_prev_value1 > _curr_value1 && _prev_value2 < _curr_value2));
- }
-
- /**
- * Checks if values are decreasing.
- *
- * @param int _rows
- * Numbers of rows to check.
- * @param int _mode
- * Indicator index mode to check.
- * @param int _shift
- * Shift which is the final value to take into the account.
- *
- * @return
- * Returns true when values are increasing.
- */
- bool IsDecreasing(int _rows = 1, int _mode = 0, int _shift = 0) {
- bool _result = true;
- for (int i = _shift + _rows - 1; i >= _shift && _result; i--) {
- IndicatorDataEntry _entry_curr = GetEntry(i);
- IndicatorDataEntry _entry_prev = GetEntry(i + 1);
- _result &= _entry_curr.IsValid() && _entry_prev.IsValid() && _entry_curr[_mode] < _entry_prev[_mode];
- if (!_result) {
- break;
- }
- }
- return _result;
- }
-
- /**
- * Checks if value decreased by the given percentage value.
- *
- * @param int _pct
- * Percentage value to use for comparison.
- * @param int _mode
- * Indicator index mode to use.
- * @param int _shift
- * Indicator value shift to use.
- * @param int _count
- * Count of bars to compare change backward.
- * @param int _hundreds
- * When true, use percentage in hundreds, otherwise 1 is 100%.
- *
- * @return
- * Returns true when value increased.
- */
- bool IsDecByPct(float _pct, int _mode = 0, int _shift = 0, int _count = 1, bool _hundreds = true) {
- bool _result = true;
- IndicatorDataEntry _v0 = GetEntry(_shift);
- IndicatorDataEntry _v1 = GetEntry(_shift + _count);
- _result &= _v0.IsValid() && _v1.IsValid();
- _result &= _result && Math::ChangeInPct(_v1[_mode], _v0[_mode], _hundreds) < _pct;
- return _result;
- }
-
- /**
- * Checks if values are increasing.
- *
- * @param int _rows
- * Numbers of rows to check.
- * @param int _mode
- * Indicator index mode to check.
- * @param int _shift
- * Shift which is the final value to take into the account.
- *
- * @return
- * Returns true when values are increasing.
- */
- bool IsIncreasing(int _rows = 1, int _mode = 0, int _shift = 0) {
- bool _result = true;
- for (int i = _shift + _rows - 1; i >= _shift && _result; i--) {
- IndicatorDataEntry _entry_curr = GetEntry(i);
- IndicatorDataEntry _entry_prev = GetEntry(i + 1);
- _result &= _entry_curr.IsValid() && _entry_prev.IsValid() && _entry_curr[_mode] > _entry_prev[_mode];
- if (!_result) {
- break;
- }
- }
- return _result;
- }
-
- /**
- * Checks if value increased by the given percentage value.
- *
- * @param int _pct
- * Percentage value to use for comparison.
- * @param int _mode
- * Indicator index mode to use.
- * @param int _shift
- * Indicator value shift to use.
- * @param int _count
- * Count of bars to compare change backward.
- * @param int _hundreds
- * When true, use percentage in hundreds, otherwise 1 is 100%.
- *
- * @return
- * Returns true when value increased.
- */
- bool IsIncByPct(float _pct, int _mode = 0, int _shift = 0, int _count = 1, bool _hundreds = true) {
- bool _result = true;
- IndicatorDataEntry _v0 = GetEntry(_shift);
- IndicatorDataEntry _v1 = GetEntry(_shift + _count);
- _result &= _v0.IsValid() && _v1.IsValid();
- _result &= _result && Math::ChangeInPct(_v1[_mode], _v0[_mode], _hundreds) > _pct;
- return _result;
- }
-
- /* Getters */
-
- int GetDataSourceMode() { return iparams.GetDataSourceMode(); }
-
/**
* Returns the highest bar's index (shift).
*/
@@ -706,7 +423,7 @@ class Indicator : public Chart {
int last_bar = count == WHOLE_ARRAY ? (int)(GetBarShift(GetLastBarTime())) : (start_bar + count - 1);
for (int shift = start_bar; shift <= last_bar; ++shift) {
- double value = GetEntry(shift).GetMax(iparams.max_modes);
+ double value = GetEntry(shift).GetMax(GetModeCount());
if (value > max) {
max = value;
max_idx = shift;
@@ -726,7 +443,7 @@ class Indicator : public Chart {
int last_bar = count == WHOLE_ARRAY ? (int)(GetBarShift(GetLastBarTime())) : (start_bar + count - 1);
for (int shift = start_bar; shift <= last_bar; ++shift) {
- double value = GetEntry(shift).GetMin(iparams.max_modes);
+ double value = GetEntry(shift).GetMin(GetModeCount());
if (value < min) {
min = value;
min_idx = shift;
@@ -745,7 +462,7 @@ class Indicator : public Chart {
int last_bar = count == WHOLE_ARRAY ? (int)(GetBarShift(GetLastBarTime())) : (start_bar + count - 1);
for (int shift = start_bar; shift <= last_bar; ++shift) {
- double value = GetEntry(shift).GetMax(iparams.max_modes);
+ double value = GetEntry(shift).GetMax(iparams.GetMaxModes());
if (max == NULL || value > max) {
max = value;
}
@@ -763,7 +480,7 @@ class Indicator : public Chart {
int last_bar = count == WHOLE_ARRAY ? (int)(GetBarShift(GetLastBarTime())) : (start_bar + count - 1);
for (int shift = start_bar; shift <= last_bar; ++shift) {
- double value = GetEntry(shift).GetMin(iparams.max_modes);
+ double value = GetEntry(shift).GetMin(iparams.GetMaxModes());
if (min == NULL || value < min) {
min = value;
}
@@ -782,8 +499,8 @@ class Indicator : public Chart {
int last_bar = count == WHOLE_ARRAY ? (int)(GetBarShift(GetLastBarTime())) : (start_bar + count - 1);
for (int shift = start_bar; shift <= last_bar; ++shift) {
- double value_min = GetEntry(shift).GetMin(iparams.max_modes);
- double value_max = GetEntry(shift).GetMax(iparams.max_modes);
+ double value_min = GetEntry(shift).GetMin(iparams.GetMaxModes());
+ double value_max = GetEntry(shift).GetMax(iparams.GetMaxModes());
sum += value_min + value_max;
num_values += 2;
@@ -806,7 +523,7 @@ class Indicator : public Chart {
ArrayResize(array, num_bars);
for (int shift = start_bar; shift <= last_bar; ++shift) {
- array[index++] = GetEntry(shift).GetAvg(iparams.max_modes);
+ array[index++] = GetEntry(shift).GetAvg(iparams.GetMaxModes());
}
ArraySort(array);
@@ -821,6 +538,48 @@ class Indicator : public Chart {
return median;
}
+ /**
+ * Returns currently selected data source doing validation.
+ */
+ IndicatorBase* GetDataSource() {
+ IndicatorBase* _result = NULL;
+
+ if (GetDataSourceRaw() != NULL) {
+ _result = GetDataSourceRaw();
+ } else if (iparams.GetDataSourceId() != -1) {
+ int _source_id = iparams.GetDataSourceId();
+
+ if (indicators.KeyExists(_source_id)) {
+ _result = indicators[_source_id].Ptr();
+ } else {
+ Ref _source = FetchDataSource((ENUM_INDICATOR_TYPE)_source_id);
+
+ if (!_source.IsSet()) {
+ Alert(GetName(), " has no built-in source indicator ", _source_id);
+ DebugBreak();
+ } else {
+ indicators.Set(_source_id, _source);
+
+ _result = _source.Ptr();
+ }
+ }
+ }
+
+ ValidateDataSource(&this, _result);
+
+ return _result;
+ }
+
+ /**
+ * Gets number of modes available to retrieve by GetValue().
+ */
+ virtual int GetModeCount() { return 0; }
+
+ /**
+ * Whether data source is selected.
+ */
+ virtual bool HasDataSource() { return GetDataSourceRaw() != NULL || iparams.GetDataSourceId() != -1; }
+
/**
* Gets indicator's params.
*/
@@ -854,21 +613,11 @@ class Indicator : public Chart {
return _signals;
}
- /**
- * Get indicator type.
- */
- ENUM_INDICATOR_TYPE GetType() { return iparams.itype; }
-
/**
* Get pointer to data of indicator.
*/
BufferStruct* GetData() { return GetPointer(idata); }
- /**
- * Get data type of indicator.
- */
- ENUM_DATATYPE GetDataType() { return iparams.dtype; }
-
/**
* Get name of the indicator.
*/
@@ -879,9 +628,7 @@ class Indicator : public Chart {
*/
string GetFullName() {
return iparams.name + "[" + IntegerToString(iparams.GetMaxModes()) + "]" +
- (HasDataSource() ? (" (over " + GetDataSource().GetName() + "[" +
- IntegerToString(GetDataSource().GetParams().GetMaxModes()) + "])")
- : "");
+ (HasDataSource() ? (" (over " + GetDataSource().GetFullName() + ")") : "");
}
/**
@@ -890,7 +637,7 @@ class Indicator : public Chart {
string GetDescriptiveName() {
string name = iparams.name + " (";
- switch (iparams.idstype) {
+ switch (iparams.GetDataSourceType()) {
case IDATA_BUILTIN:
name += "built-in, ";
break;
@@ -902,7 +649,7 @@ class Indicator : public Chart {
break;
}
- name += IntegerToString(iparams.max_modes) + (iparams.max_modes == 1 ? " mode" : " modes");
+ name += IntegerToString(iparams.GetMaxModes()) + (iparams.GetMaxModes() == 1 ? " mode" : " modes");
return name + ")";
}
@@ -917,6 +664,14 @@ class Indicator : public Chart {
Chart::Set(_param, _value);
}
+ /**
+ * Sets indicator data source.
+ */
+ void SetDataSource(IndicatorBase* _indi, bool _managed, int _input_mode) {
+ indi_src = _indi;
+ iparams.SetDataSource(-1, _input_mode, _managed);
+ }
+
/**
* Sets name of the indicator.
*/
@@ -1117,124 +872,16 @@ class Indicator : public Chart {
return true;
}
- /**
- * Feed history entries.
- */
- void FeedHistoryEntries(int period, int shift = 0) {
- if (is_feeding || is_fed) {
- // Avoiding forever loop.
- return;
- }
-
- is_feeding = true;
-
- for (int i = shift + period; i > shift; --i) {
- if (ChartStatic::iPrice(PRICE_OPEN, Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF), i) <=
- 0) {
- // No data for that entry
- continue;
- }
-
- GetEntry(i);
- }
-
- is_feeding = false;
- is_fed = true;
- }
-
- ValueStorage* GetValueStorage(int _mode = 0) {
- if (value_storages[_mode] == NULL) {
- value_storages[_mode] = new IndicatorBufferValueStorage(THIS_PTR, _mode);
- }
- return value_storages[_mode];
- }
-
- /**
- * Returns indicator value for a given shift and mode.
- */
- template
- T GetValue(int _shift = 0, int _mode = -1) {
- T _result;
- int _index = _mode != -1 ? _mode : iparams.indi_mode;
- GetEntry(_shift).values[_index].Get(_result);
- ResetLastError();
- return _result;
- }
-
- /**
- * Returns price corresponding to indicator value for a given shift and mode.
- *
- * Can be useful for calculating trailing stops based on the indicator.
- *
- * @return
- * Returns price value of the corresponding indicator values.
- */
- template
- float GetValuePrice(int _shift = 0, int _mode = 0, ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL) {
- float _price = 0;
- if (iparams.GetIDataValueRange() != IDATA_RANGE_PRICE) {
- _price = (float)GetPrice(_ap, _shift);
- } else if (iparams.GetIDataValueRange() == IDATA_RANGE_PRICE) {
- // When indicator values are the actual prices.
- T _values[4];
- if (!CopyValues(_values, 4, _shift, _mode)) {
- // When values aren't valid, return 0.
- return _price;
- }
- datetime _bar_time = GetBarTime(_shift);
- float _value = 0;
- BarOHLC _ohlc(_values, _bar_time);
- _price = _ohlc.GetAppliedPrice(_ap);
- }
- return _price;
- }
-
- /**
- * Returns values for a given shift.
- *
- * Note: Remember to check if shift exists by HasValidEntry(shift).
- */
- template
- bool GetValues(int _shift, T& _out1, T& _out2) {
- IndicatorDataEntry _entry = GetEntry(_shift);
- _out1 = _entry.values[0];
- _out2 = _entry.values[1];
- bool _result = GetLastError() != 4401;
- ResetLastError();
- return _result;
- }
-
- template
- bool GetValues(int _shift, T& _out1, T& _out2, T& _out3) {
- IndicatorDataEntry _entry = GetEntry(_shift);
- _out1 = _entry.values[0];
- _out2 = _entry.values[1];
- _out3 = _entry.values[2];
- bool _result = GetLastError() != 4401;
- ResetLastError();
- return _result;
- }
-
- template
- bool GetValues(int _shift, T& _out1, T& _out2, T& _out3, T& _out4) {
- IndicatorDataEntry _entry = GetEntry(_shift);
- _out1 = _entry.values[0];
- _out2 = _entry.values[1];
- _out3 = _entry.values[2];
- _out4 = _entry.values[3];
- bool _result = GetLastError() != 4401;
- ResetLastError();
- return _result;
- }
+ ENUM_IDATA_VALUE_RANGE GetIDataValueRange() { return iparams.idvrange; }
virtual void OnTick() {
Chart::OnTick();
if (iparams.is_draw) {
// Print("Drawing ", GetName(), iparams.indi_data != NULL ? (" (over " + iparams.indi_data.GetName() + ")") : "");
- for (int i = 0; i < (int)iparams.max_modes; ++i)
- draw.DrawLineTo(GetName() + "_" + IntegerToString(i) + "_" + IntegerToString(iparams.indi_mode), GetBarTime(0),
- GetEntry(0)[i], iparams.draw_window);
+ for (int i = 0; i < (int)iparams.GetMaxModes(); ++i)
+ draw.DrawLineTo(GetName() + "_" + IntegerToString(i) + "_" + IntegerToString(iparams.GetDataSourceMode()),
+ GetBarTime(0), GetEntry(0)[i], iparams.draw_window);
}
}
@@ -1252,6 +899,26 @@ class Indicator : public Chart {
*/
virtual bool IsDataSourceModeSelectable() { return true; }
+ /**
+ * Checks if indicator entry is valid.
+ *
+ * @return
+ * Returns true if entry is valid (has valid values), otherwise false.
+ */
+ virtual bool IsValidEntry(IndicatorDataEntry& _entry) {
+ bool _result = true;
+ _result &= !_entry.HasValue(NULL);
+ _result &= !_entry.HasValue(EMPTY_VALUE);
+ if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_DOUBLE)) {
+ _result &= !_entry.HasValue(DBL_MAX);
+ } else if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_FLOAT)) {
+ _result &= !_entry.HasValue(FLT_MAX);
+ } else if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_INT)) {
+ _result &= !_entry.HasValue(INT_MAX);
+ }
+ return _result;
+ }
+
/**
* Update indicator.
*/
@@ -1262,12 +929,31 @@ class Indicator : public Chart {
/**
* Returns the indicator's struct value.
+ *
+ * @see: IndicatorDataEntry.
+ *
+ * @return
+ * Returns IndicatorDataEntry struct filled with indicator values.
*/
virtual IndicatorDataEntry GetEntry(int _shift = 0) {
- IndicatorDataEntry _entry(iparams.max_modes);
- _entry = idata.GetByKey(GetBarTime(_shift), _entry);
+ long _bar_time = GetBarTime(_shift);
+ IndicatorDataEntry _entry = idata.GetByKey(_bar_time);
+ if (!_entry.IsValid() && !_entry.CheckFlag(INDI_ENTRY_FLAG_INSUFFICIENT_DATA)) {
+ _entry.Resize(iparams.GetMaxModes());
+ _entry.timestamp = GetBarTime(_shift);
+ for (int _mode = 0; _mode < (int)iparams.GetMaxModes(); _mode++) {
+ _entry.values[_mode] = GetValue(_mode, _shift);
+ }
+ _entry.AddFlags(_entry.GetDataTypeFlag(iparams.GetDataValueType()));
+ _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, IsValidEntry(_entry));
+ if (_entry.IsValid()) {
+ idata.Add(_entry, _bar_time);
+ } else {
+ _entry.AddFlags(INDI_ENTRY_FLAG_INSUFFICIENT_DATA);
+ }
+ }
return _entry;
- };
+ }
/**
* Returns the indicator's entry value.
@@ -1278,6 +964,15 @@ class Indicator : public Chart {
return _param;
}
+ /**
+ * Returns the indicator's value.
+ */
+ virtual double GetValue(int _mode = 0, int _shift = 0) {
+ istate.is_changed = false;
+ istate.is_ready = false;
+ return EMPTY_VALUE;
+ }
+
/**
* Returns the indicator's value in plain format.
*/
@@ -1291,68 +986,4 @@ class Indicator : public Chart {
}
};
-/**
- * BarsCalculated() method to be used on Indicator instance.
- */
-int BarsCalculated(Indicator* _indi, int _bars_required) {
- if (_bars_required == 0) {
- return _bars_required;
- }
-
- IndicatorDataEntry _entry = _indi.GetEntry(_bars_required - 1);
- // GetEntry() could end up with an error. It is okay.
- ResetLastError();
-
- int _valid_history_count = 0;
-
- if (!_entry.IsValid()) {
- // We don't have sufficient data. Counting how much data we have.
-
- for (int i = 0; i < _bars_required; ++i) {
- IndicatorDataEntry _check_entry = _indi.GetEntry(i);
- if (!_check_entry.IsValid()) {
- break;
- }
- ++_valid_history_count;
- }
- } else {
- _valid_history_count = _bars_required;
- }
-
- return _valid_history_count;
-}
-
-/**
- * CopyBuffer() method to be used on Indicator instance with ValueStorage buffer.
- *
- * Note that data will be copied so that the oldest element will be located at the start of the physical memory
- * allocated for the array
- */
-template
-int CopyBuffer(Indicator* _indi, int _mode, int _start, int _count, ValueStorage& _buffer, int _rates_total) {
- int _num_copied = 0;
- int _buffer_size = ArraySize(_buffer);
-
- if (_buffer_size < _rates_total) {
- _buffer_size = ArrayResize(_buffer, _rates_total);
- }
-
- for (int i = _start; i < _count; ++i) {
- IndicatorDataEntry _entry = _indi.GetEntry(i);
-
- if (!_entry.IsValid()) {
- break;
- }
-
- T _value = _entry.GetValue(_mode);
-
- // Print(_value);
-
- _buffer[_buffer_size - i - 1] = _value;
- ++_num_copied;
- }
-
- return _num_copied;
-}
-
#endif
diff --git a/Indicator.struct.cache.h b/Indicator.struct.cache.h
index 209983beb..2c4df7097 100644
--- a/Indicator.struct.cache.h
+++ b/Indicator.struct.cache.h
@@ -32,6 +32,7 @@
// Includes.
#include "Refs.mqh"
+#include "Storage/ValueStorage.h"
/**
* Holds buffers used to cache values calculated via OnCalculate methods.
@@ -113,6 +114,11 @@ class IndicatorCalculateCache : public Dynamic {
*/
bool HasBuffers() { return ArraySize(buffers) != 0; }
+ /**
+ * Returns number of added buffers.
+ */
+ int NumBuffers() { return ArraySize(buffers); }
+
/**
* Returns existing or new cache as a child of current one. Useful when indicator uses other indicators and requires
* unique caches for them.
diff --git a/Indicator.struct.h b/Indicator.struct.h
index 51c99d028..8bedbd5b0 100644
--- a/Indicator.struct.h
+++ b/Indicator.struct.h
@@ -31,6 +31,7 @@
#endif
// Forward declaration.
+template
class Indicator;
struct ChartParams;
@@ -353,6 +354,7 @@ struct IndicatorDataEntry {
/* Structure for indicator parameters. */
struct IndicatorParams {
+ public: // @todo: Change it to protected.
string name; // Name of the indicator.
int shift; // Shift (relative to the current bar, 0 - default).
unsigned int max_buffers; // Max buffers to store.
@@ -367,33 +369,32 @@ struct IndicatorParams {
color indi_color; // Indicator color.
int indi_data_source_id; // Id of the indicator to be used as data source.
int indi_data_source_mode; // Mode used as input from data source.
- Indicator *indi_data_source; // Custom indicator to be used as data source.
bool indi_managed; // Whether indicator should be owned by indicator.
ARRAY(DataParamEntry, input_params); // Indicator input params.
- int indi_mode; // Index of indicator data to be used as data source.
bool is_draw; // Draw active.
int draw_window; // Drawing window.
string custom_indi_name; // Name of the indicator passed to iCustom() method.
+ public:
/* Special methods */
// Constructor.
- IndicatorParams(ENUM_INDICATOR_TYPE _itype = INDI_NONE, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN,
- string _name = "")
+ IndicatorParams(ENUM_INDICATOR_TYPE _itype = INDI_NONE, unsigned int _max_modes = 1,
+ ENUM_DATATYPE _dtype = TYPE_DOUBLE, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT,
+ ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN, string _name = "")
: custom_indi_name(""),
+ dtype(_dtype),
name(_name),
shift(0),
- max_modes(1),
+ max_modes(_max_modes),
max_buffers(10),
idstype(_idstype),
idvrange(IDATA_RANGE_UNKNOWN),
- indi_data_source(NULL),
indi_data_source_id(-1),
indi_data_source_mode(0),
itype(_itype),
is_draw(false),
indi_color(clrNONE),
- indi_mode(0),
- draw_window(0) {
- SetDataSourceType(_idstype);
+ draw_window(0),
+ tf(_tf) {
Init();
};
IndicatorParams(string _name, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN)
@@ -404,32 +405,35 @@ struct IndicatorParams {
max_buffers(10),
idstype(_idstype),
idvrange(IDATA_RANGE_UNKNOWN),
- indi_data_source(NULL),
indi_data_source_id(-1),
indi_data_source_mode(0),
is_draw(false),
indi_color(clrNONE),
- indi_mode(0),
draw_window(0) {
- SetDataSourceType(_idstype);
Init();
};
+ // Copy constructor.
+ IndicatorParams(IndicatorParams &_iparams, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : tf(_tf) {
+ this = _iparams;
+ if (_tf != PERIOD_CURRENT) {
+ tf.SetTf(_tf);
+ }
+ }
void Init() {}
/* Getters */
- string GetCustomIndicatorName() { return custom_indi_name; }
- Indicator *GetDataSource() { return indi_data_source; }
- int GetDataSourceId() { return indi_data_source_id; }
- int GetDataSourceMode() { return indi_data_source_mode; }
- color GetIndicatorColor() { return indi_color; }
- int GetMaxModes() { return (int)max_modes; }
- int GetMaxParams() { return (int)max_params; }
- int GetShift() { return shift; }
- ENUM_DATATYPE GetDataValueType() { return dtype; }
- ENUM_IDATA_SOURCE_TYPE GetDataSourceType() { return idstype; }
- ENUM_IDATA_VALUE_RANGE GetIDataValueRange() { return idvrange; }
- ENUM_TIMEFRAMES GetTf() { return tf.GetTf(); }
+ string GetCustomIndicatorName() const { return custom_indi_name; }
+ int GetDataSourceId() const { return indi_data_source_id; }
+ int GetDataSourceMode() const { return indi_data_source_mode; }
+ color GetIndicatorColor() const { return indi_color; }
+ int GetMaxModes() const { return (int)max_modes; }
+ int GetMaxParams() const { return (int)max_params; }
+ int GetShift() const { return shift; }
+ ENUM_DATATYPE GetDataValueType() const { return dtype; }
+ ENUM_IDATA_SOURCE_TYPE GetDataSourceType() const { return idstype; }
+ ENUM_IDATA_VALUE_RANGE GetIDataValueRange() const { return idvrange; }
+ ENUM_TIMEFRAMES GetTf() const { return tf.GetTf(); }
template
- T GetInputParam(int _index, T _default) {
+ T GetInputParam(int _index, T _default) const {
DataParamEntry _param = input_params[_index];
switch (_param.type) {
case TYPE_BOOL:
@@ -465,15 +469,9 @@ struct IndicatorParams {
draw_window = _window;
}
void SetIndicatorColor(color _clr) { indi_color = _clr; }
- void SetDataSource(int _id, int _input_mode = -1) {
+ void SetDataSource(int _id, int _input_mode = -1, bool _managed = true) {
indi_data_source_id = _id;
indi_data_source_mode = _input_mode;
- idstype = IDATA_INDICATOR;
- }
- void SetDataSource(Indicator *_indi, bool _managed = true, int _input_mode = -1) {
- indi_data_source_id = -1;
- indi_data_source = _indi;
- indi_data_source_mode = _input_mode;
indi_managed = _managed;
idstype = IDATA_INDICATOR;
}
diff --git a/Indicator.struct.serialize.h b/Indicator.struct.serialize.h
index 0b47a476a..d7bd36a23 100644
--- a/Indicator.struct.serialize.h
+++ b/Indicator.struct.serialize.h
@@ -36,9 +36,9 @@ SerializerNodeType IndicatorDataEntry::Serialize(Serializer &_s) {
_s.Pass(THIS_REF, "datetime", timestamp, SERIALIZER_FIELD_FLAG_DYNAMIC);
_s.Pass(THIS_REF, "flags", flags, SERIALIZER_FIELD_FLAG_DYNAMIC);
for (int i = 0; i < _asize; i++) {
- // _s.Pass(THIS_REF, (string)i, values[i], SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); // Can this
- // work? _s.Pass(THIS_REF, (string)i, GetEntry(i), SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); //
- // Can this work?
+ // _s.Pass(THIS_REF, (string)i, values[i], SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); // Can
+ // this work? _s.Pass(THIS_REF, (string)i, GetEntry(i), SERIALIZER_FIELD_FLAG_DYNAMIC |
+ // SERIALIZER_FIELD_FLAG_FEATURE); // Can this work?
if (CheckFlags(INDI_ENTRY_FLAG_IS_DOUBLE)) {
_s.Pass(THIS_REF, (string)i, values[i].vdbl, SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE);
@@ -92,7 +92,6 @@ SerializerNodeType IndicatorParams::Serialize(Serializer &s) {
// s.PassObject(this, "indicator", indi_data); // @todo
// s.Pass(THIS_REF, "indi_data_ownership", indi_data_ownership);
s.Pass(THIS_REF, "indi_color", indi_color, SERIALIZER_FIELD_FLAG_HIDDEN);
- s.Pass(THIS_REF, "indi_mode", indi_mode);
s.Pass(THIS_REF, "is_draw", is_draw);
s.Pass(THIS_REF, "draw_window", draw_window, SERIALIZER_FIELD_FLAG_HIDDEN);
s.Pass(THIS_REF, "custom_indi_name", custom_indi_name);
diff --git a/Indicator.struct.signal.h b/Indicator.struct.signal.h
index 1ac6dcd75..954e02172 100644
--- a/Indicator.struct.signal.h
+++ b/Indicator.struct.signal.h
@@ -56,7 +56,8 @@ struct IndicatorSignal {
// Constructors.
IndicatorSignal(int _signals = 0) : signals(_signals) {}
- IndicatorSignal(ARRAY_REF(IndicatorDataEntry, _data), IndicatorParams &_ip, ChartParams &_cp, int _m1 = 0, int _m2 = 0)
+ IndicatorSignal(ARRAY_REF(IndicatorDataEntry, _data), IndicatorParams &_ip, ChartParams &_cp, int _m1 = 0,
+ int _m2 = 0)
: signals(0) {
CalcSignals(_data, _ip, _cp, _m1, _m2);
}
diff --git a/IndicatorBase.h b/IndicatorBase.h
new file mode 100644
index 000000000..eed35dcdb
--- /dev/null
+++ b/IndicatorBase.h
@@ -0,0 +1,1045 @@
+//+------------------------------------------------------------------+
+//| EA31337 framework |
+//| Copyright 2016-2021, EA31337 Ltd |
+//| https://github.com/EA31337 |
+//+------------------------------------------------------------------+
+
+/*
+ * This file is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+/**
+ * @file
+ * Base interface for Indicator class.
+ */
+
+#ifndef __MQL__
+// Allows the preprocessor to include a header file when it is needed.
+#pragma once
+#endif
+
+// Forward declaration.
+class Chart;
+
+// Includes.
+#include "Array.mqh"
+#include "BufferStruct.mqh"
+#include "Chart.mqh"
+#include "DateTime.mqh"
+#include "DrawIndicator.mqh"
+#include "Indicator.define.h"
+#include "Indicator.enum.h"
+#include "Indicator.struct.cache.h"
+#include "Indicator.struct.h"
+#include "Indicator.struct.serialize.h"
+#include "Indicator.struct.signal.h"
+#include "Math.h"
+#include "Object.mqh"
+#include "Refs.mqh"
+#include "Serializer.mqh"
+#include "SerializerCsv.mqh"
+#include "SerializerJson.mqh"
+#include "Storage/ValueStorage.h"
+#include "Storage/ValueStorage.indicator.h"
+#include "Storage/ValueStorage.native.h"
+
+#ifndef __MQL4__
+// Defines global functions (for MQL4 backward compatibility).
+bool IndicatorBuffers(int _count) { return Indicator::SetIndicatorBuffers(_count); }
+int IndicatorCounted(int _value = 0) {
+ static int prev_calculated = 0;
+ // https://docs.mql4.com/customind/indicatorcounted
+ prev_calculated = _value > 0 ? _value : prev_calculated;
+ return prev_calculated;
+}
+#endif
+
+/**
+ * Class to deal with indicators.
+ */
+class IndicatorBase : public Chart {
+ protected:
+ BufferStruct idata;
+ DrawIndicator* draw;
+ IndicatorState istate;
+ void* mydata;
+ bool is_fed; // Whether calc_start_bar is already calculated.
+ int calc_start_bar; // Index of the first valid bar (from 0).
+ DictStruct> indicators; // Indicators list keyed by id.
+ bool indicator_builtin;
+ ARRAY(ValueStorage*, value_storages);
+ IndicatorBase* indi_src; // // Indicator used as data source.
+ int indi_src_mode; // Mode of source indicator
+ IndicatorCalculateCache cache;
+
+ public:
+ /* Indicator enumerations */
+
+ /*
+ * Default enumerations:
+ *
+ * ENUM_MA_METHOD values:
+ * 0: MODE_SMA (Simple averaging)
+ * 1: MODE_EMA (Exponential averaging)
+ * 2: MODE_SMMA (Smoothed averaging)
+ * 3: MODE_LWMA (Linear-weighted averaging)
+ */
+
+ /* Special methods */
+
+ /**
+ * Class constructor.
+ */
+ IndicatorBase() : indi_src(NULL) { is_fed = false; }
+
+ /**
+ * Class constructor.
+ */
+ IndicatorBase(ChartParams& _cparams) : indi_src(NULL), Chart(_cparams) { is_fed = false; }
+
+ /**
+ * Class constructor.
+ */
+ IndicatorBase(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, string _symbol = NULL) : Chart(_tf, _symbol) {
+ is_fed = false;
+ indi_src = NULL;
+ }
+
+ /**
+ * Class constructor.
+ */
+ IndicatorBase(ENUM_TIMEFRAMES_INDEX _tfi, string _symbol = NULL) : Chart(_tfi, _symbol) {
+ is_fed = false;
+ indi_src = NULL;
+ }
+
+ /**
+ * Class deconstructor.
+ */
+ virtual ~IndicatorBase() {
+ ReleaseHandle();
+
+ for (int i = 0; i < ArraySize(value_storages); ++i) {
+ if (value_storages[i] != NULL) {
+ delete value_storages[i];
+ }
+ }
+ }
+
+ /* Defines MQL backward compatible methods */
+
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(DUMMY);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, int _mode,
+ int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, int _mode,
+ int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e,
+ int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
+ int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
+ G _g, int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
+ G _g, H _h, int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _h, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
+ G _g, H _h, I _i, int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _h, _i, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h COMMA _i);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
+ G _g, H _h, I _i, J _j, int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h COMMA _i COMMA _j);
+#endif
+ }
+
+ template
+ double iCustom(int& _handle, string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f,
+ G _g, H _h, I _i, J _j, K _k, int _mode, int _shift) {
+#ifdef __MQL4__
+ return ::iCustom(_symbol, _tf, _name, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _mode, _shift);
+#else // __MQL5__
+ ICUSTOM_DEF(COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h COMMA _i COMMA _j COMMA _k);
+#endif
+ }
+
+ /* Buffer methods */
+
+ virtual string CacheKey() { return GetName(); }
+
+ /**
+ * Initializes a cached proxy between i*OnArray() methods and OnCalculate()
+ * used by custom indicators.
+ *
+ * Note that OnCalculateProxy() method sets incoming price array as not
+ * series. It will be reverted back by SetPrevCalculated(). It is because
+ * OnCalculate() methods assumes that prices are set as not series.
+ *
+ * For real example how you can use this method, look at
+ * Indi_MA::iMAOnArray() method.
+ *
+ * Usage:
+ *
+ * static double iFooOnArray(double &price[], int total, int period,
+ * int foo_shift, int foo_method, int shift, string cache_name = "")
+ * {
+ * if (cache_name != "") {
+ * String cache_key;
+ * cache_key.Add(cache_name);
+ * cache_key.Add(period);
+ * cache_key.Add(foo_method);
+ *
+ * Ref cache = Indicator::OnCalculateProxy(cache_key.ToString(), price, total);
+ *
+ * int prev_calculated =
+ * Indi_Foo::Calculate(total, cache.Ptr().prev_calculated, 0, price, cache.Ptr().buffer1, ma_method, period);
+ *
+ * cache.Ptr().SetPrevCalculated(price, prev_calculated);
+ *
+ * return cache.Ptr().GetValue(1, shift + ma_shift);
+ * }
+ * else {
+ * // Default iFooOnArray.
+ * }
+ *
+ * WARNING: Do not use shifts when creating cache_key, as this will create many invalid buffers.
+ */
+ /*
+ static IndicatorCalculateCache OnCalculateProxy(string key, double& price[], int& total) {
+ if (total == 0) {
+ total = ArraySize(price);
+ }
+
+ // Stores previously calculated value.
+ static DictStruct cache;
+
+ unsigned int position;
+ IndicatorCalculateCache cache_item;
+
+ if (cache.KeyExists(key, position)) {
+ cache_item = cache.GetByKey(key);
+ } else {
+ IndicatorCalculateCache cache_item_new(1, ArraySize(price));
+ cache_item = cache_item_new;
+ cache.Set(key, cache_item);
+ }
+
+ // Number of bars available in the chart. Same as length of the input `array`.
+ int rates_total = ArraySize(price);
+
+ int begin = 0;
+
+ cache_item.Resize(rates_total);
+
+ cache_item.price_was_as_series = ArrayGetAsSeries(price);
+ ArraySetAsSeries(price, false);
+
+ return cache_item;
+ }
+ */
+
+ /**
+ * Gets indicator data from a buffer and copy into struct array.
+ *
+ * @return
+ * Returns true of successful copy.
+ * Returns false on invalid values.
+ */
+ bool CopyEntries(IndicatorDataEntry& _data[], int _count, int _start_shift = 0) {
+ bool _is_valid = true;
+ if (ArraySize(_data) < _count) {
+ _is_valid &= ArrayResize(_data, _count) > 0;
+ }
+ for (int i = 0; i < _count; i++) {
+ IndicatorDataEntry _entry = GetEntry(_start_shift + i);
+ _is_valid &= _entry.IsValid();
+ _data[i] = _entry;
+ }
+ return _is_valid;
+ }
+
+ /**
+ * Gets indicator data from a buffer and copy into array of values.
+ *
+ * @return
+ * Returns true of successful copy.
+ * Returns false on invalid values.
+ */
+ template
+ bool CopyValues(T& _data[], int _count, int _start_shift = 0, int _mode = 0) {
+ bool _is_valid = true;
+ if (ArraySize(_data) < _count) {
+ _count = ArrayResize(_data, _count);
+ _count = _count > 0 ? _count : ArraySize(_data);
+ }
+ for (int i = 0; i < _count; i++) {
+ IndicatorDataEntry _entry = GetEntry(_start_shift + i);
+ _is_valid &= _entry.IsValid();
+ _data[i] = (T)_entry[_mode];
+ }
+ return _is_valid;
+ }
+
+ /**
+ * Validates currently selected indicator used as data source.
+ */
+ void ValidateSelectedDataSource() {
+ if (HasDataSource()) {
+ ValidateDataSource(THIS_PTR, GetDataSourceRaw());
+ }
+ }
+
+ /**
+ * Loads and validates built-in indicators whose can be used as data source.
+ */
+ virtual void ValidateDataSource(IndicatorBase* _target, IndicatorBase* _source) {}
+
+ /**
+ * Checks whether indicator have given mode index.
+ *
+ * If given mode is -1 (default one) and indicator has exactly one mode, then mode index will be replaced by 0.
+ */
+ virtual void ValidateDataSourceMode(int& _out_mode) {}
+
+ /**
+ * Provides built-in indicators whose can be used as data source.
+ */
+ virtual IndicatorBase* FetchDataSource(ENUM_INDICATOR_TYPE _id) { return NULL; }
+
+ /**
+ * Returns currently selected data source without any validation.
+ */
+ IndicatorBase* GetDataSourceRaw() { return indi_src; }
+
+ /* Operator overloading methods */
+
+ /**
+ * Access indicator entry data using [] operator.
+ */
+ IndicatorDataEntry operator[](int _shift) { return GetEntry(_shift); }
+ IndicatorDataEntry operator[](ENUM_INDICATOR_INDEX _shift) { return GetEntry(_shift); }
+ IndicatorDataEntry operator[](datetime _dt) { return idata[_dt]; }
+
+ /* Getters */
+
+ /**
+ * Returns buffers' cache.
+ */
+ IndicatorCalculateCache* GetCache() { return &cache; }
+
+ /**
+ * Gets an indicator's chart parameter value.
+ */
+ template
+ T Get(ENUM_CHART_PARAM _param) {
+ return Chart::Get(_param);
+ }
+
+ /**
+ * Gets an indicator's state property value.
+ */
+ template
+ T Get(STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) _prop) {
+ return istate.Get(_prop);
+ }
+
+ /**
+ * Gets number of modes available to retrieve by GetValue().
+ */
+ virtual int GetModeCount() { return 0; }
+
+ /* State methods */
+
+ /**
+ * Checks for crossover.
+ *
+ * @return
+ * Returns true when values are crossing over, otherwise false.
+ */
+ bool IsCrossover(int _shift1 = 0, int _shift2 = 1, int _mode1 = 0, int _mode2 = 0) {
+ double _curr_value1 = GetEntry(_shift1)[_mode1];
+ double _prev_value1 = GetEntry(_shift2)[_mode1];
+ double _curr_value2 = GetEntry(_shift1)[_mode2];
+ double _prev_value2 = GetEntry(_shift2)[_mode2];
+ return ((_curr_value1 > _prev_value1 && _curr_value2 < _prev_value2) ||
+ (_prev_value1 > _curr_value1 && _prev_value2 < _curr_value2));
+ }
+
+ /**
+ * Checks if values are decreasing.
+ *
+ * @param int _rows
+ * Numbers of rows to check.
+ * @param int _mode
+ * Indicator index mode to check.
+ * @param int _shift
+ * Shift which is the final value to take into the account.
+ *
+ * @return
+ * Returns true when values are increasing.
+ */
+ bool IsDecreasing(int _rows = 1, int _mode = 0, int _shift = 0) {
+ bool _result = true;
+ for (int i = _shift + _rows - 1; i >= _shift && _result; i--) {
+ IndicatorDataEntry _entry_curr = GetEntry(i);
+ IndicatorDataEntry _entry_prev = GetEntry(i + 1);
+ _result &= _entry_curr.IsValid() && _entry_prev.IsValid() && _entry_curr[_mode] < _entry_prev[_mode];
+ if (!_result) {
+ break;
+ }
+ }
+ return _result;
+ }
+
+ /**
+ * Checks if value decreased by the given percentage value.
+ *
+ * @param int _pct
+ * Percentage value to use for comparison.
+ * @param int _mode
+ * Indicator index mode to use.
+ * @param int _shift
+ * Indicator value shift to use.
+ * @param int _count
+ * Count of bars to compare change backward.
+ * @param int _hundreds
+ * When true, use percentage in hundreds, otherwise 1 is 100%.
+ *
+ * @return
+ * Returns true when value increased.
+ */
+ bool IsDecByPct(float _pct, int _mode = 0, int _shift = 0, int _count = 1, bool _hundreds = true) {
+ bool _result = true;
+ IndicatorDataEntry _v0 = GetEntry(_shift);
+ IndicatorDataEntry _v1 = GetEntry(_shift + _count);
+ _result &= _v0.IsValid() && _v1.IsValid();
+ _result &= _result && Math::ChangeInPct(_v1[_mode], _v0[_mode], _hundreds) < _pct;
+ return _result;
+ }
+
+ /**
+ * Checks if values are increasing.
+ *
+ * @param int _rows
+ * Numbers of rows to check.
+ * @param int _mode
+ * Indicator index mode to check.
+ * @param int _shift
+ * Shift which is the final value to take into the account.
+ *
+ * @return
+ * Returns true when values are increasing.
+ */
+ bool IsIncreasing(int _rows = 1, int _mode = 0, int _shift = 0) {
+ bool _result = true;
+ for (int i = _shift + _rows - 1; i >= _shift && _result; i--) {
+ IndicatorDataEntry _entry_curr = GetEntry(i);
+ IndicatorDataEntry _entry_prev = GetEntry(i + 1);
+ _result &= _entry_curr.IsValid() && _entry_prev.IsValid() && _entry_curr[_mode] > _entry_prev[_mode];
+ if (!_result) {
+ break;
+ }
+ }
+ return _result;
+ }
+
+ /**
+ * Checks if value increased by the given percentage value.
+ *
+ * @param int _pct
+ * Percentage value to use for comparison.
+ * @param int _mode
+ * Indicator index mode to use.
+ * @param int _shift
+ * Indicator value shift to use.
+ * @param int _count
+ * Count of bars to compare change backward.
+ * @param int _hundreds
+ * When true, use percentage in hundreds, otherwise 1 is 100%.
+ *
+ * @return
+ * Returns true when value increased.
+ */
+ bool IsIncByPct(float _pct, int _mode = 0, int _shift = 0, int _count = 1, bool _hundreds = true) {
+ bool _result = true;
+ IndicatorDataEntry _v0 = GetEntry(_shift);
+ IndicatorDataEntry _v1 = GetEntry(_shift + _count);
+ _result &= _v0.IsValid() && _v1.IsValid();
+ _result &= _result && Math::ChangeInPct(_v1[_mode], _v0[_mode], _hundreds) > _pct;
+ return _result;
+ }
+
+ /* Getters */
+
+ /**
+ * Whether data source is selected.
+ */
+ virtual bool HasDataSource() { return false; }
+
+ /**
+ * Returns currently selected data source doing validation.
+ */
+ virtual IndicatorBase* GetDataSource() { return NULL; }
+
+ int GetDataSourceMode() { return indi_src_mode; }
+
+ /**
+ * Gets indicator's symbol.
+ */
+ string GetSymbol() { return Get(CHART_PARAM_SYMBOL); }
+
+ /**
+ * Gets indicator's time-frame.
+ */
+ ENUM_TIMEFRAMES GetTf() { return Get(CHART_PARAM_TF); }
+
+ /**
+ * Gets indicator's signals.
+ *
+ * When indicator values are not valid, returns empty signals.
+ */
+ virtual IndicatorSignal GetSignals(int _count = 3, int _shift = 0, int _mode1 = 0, int _mode2 = 0) {
+ IndicatorSignal _signal;
+ return _signal;
+ }
+
+ /**
+ * Get indicator type.
+ */
+ virtual ENUM_INDICATOR_TYPE GetType() { return INDI_NONE; }
+
+ /**
+ * Get pointer to data of indicator.
+ */
+ BufferStruct* GetData() { return GetPointer(idata); }
+
+ /**
+ * Get data type of indicator.
+ */
+ virtual ENUM_DATATYPE GetDataType() { return (ENUM_DATATYPE)-1; }
+
+ /**
+ * Get name of the indicator.
+ */
+ virtual string GetName() { return ""; }
+
+ /**
+ * Get full name of the indicator (with "over ..." part).
+ */
+ virtual string GetFullName() { return GetName(); }
+
+ /**
+ * Get more descriptive name of the indicator.
+ */
+ virtual string GetDescriptiveName() { return GetName(); }
+
+ /* Setters */
+
+ /**
+ * Sets an indicator's chart parameter value.
+ */
+ template
+ void Set(ENUM_CHART_PARAM _param, T _value) {
+ Chart::Set(_param, _value);
+ }
+
+ /**
+ * Sets indicator data source.
+ */
+ virtual void SetDataSource(IndicatorBase* _indi, bool _managed, int _input_mode) = 0;
+
+ /**
+ * Sets data source's input mode.
+ */
+ void SetDataSourceMode(int _mode) { indi_src_mode = _mode; }
+
+ /**
+ * Sets name of the indicator.
+ */
+ virtual void SetName(string _name) {}
+
+ /**
+ * Sets indicator's handle.
+ *
+ * Note: Not supported in MT4.
+ */
+ virtual void SetHandle(int _handle) {}
+
+ /**
+ * Sets indicator's symbol.
+ */
+ void SetSymbol(string _symbol) { Set(CHART_PARAM_SYMBOL, _symbol); }
+
+ /* Conditions */
+
+ /**
+ * Checks for indicator condition.
+ *
+ * @param ENUM_INDICATOR_CONDITION _cond
+ * Indicator condition.
+ * @param MqlParam[] _args
+ * Condition arguments.
+ * @return
+ * Returns true when the condition is met.
+ */
+ bool CheckCondition(ENUM_INDICATOR_CONDITION _cond, DataParamEntry& _args[]) {
+ switch (_cond) {
+ case INDI_COND_ENTRY_IS_MAX:
+ // @todo: Add arguments, check if the entry value is max.
+ return false;
+ case INDI_COND_ENTRY_IS_MIN:
+ // @todo: Add arguments, check if the entry value is min.
+ return false;
+ case INDI_COND_ENTRY_GT_AVG:
+ // @todo: Add arguments, check if...
+ // Indicator entry value is greater than average.
+ return false;
+ case INDI_COND_ENTRY_GT_MED:
+ // @todo: Add arguments, check if...
+ // Indicator entry value is greater than median.
+ return false;
+ case INDI_COND_ENTRY_LT_AVG:
+ // @todo: Add arguments, check if...
+ // Indicator entry value is lesser than average.
+ return false;
+ case INDI_COND_ENTRY_LT_MED:
+ // @todo: Add arguments, check if...
+ // Indicator entry value is lesser than median.
+ return false;
+ default:
+ GetLogger().Error(StringFormat("Invalid indicator condition: %s!", EnumToString(_cond), __FUNCTION_LINE__));
+ return false;
+ }
+ }
+ bool CheckCondition(ENUM_INDICATOR_CONDITION _cond) {
+ ARRAY(DataParamEntry, _args);
+ return IndicatorBase::CheckCondition(_cond, _args);
+ }
+
+ /**
+ * Execute Indicator action.
+ *
+ * @param ENUM_INDICATOR_ACTION _action
+ * Indicator action to execute.
+ * @param MqlParam _args
+ * Indicator action arguments.
+ * @return
+ * Returns true when the action has been executed successfully.
+ */
+ virtual bool ExecuteAction(ENUM_INDICATOR_ACTION _action, DataParamEntry& _args[]) {
+ bool _result = true;
+ long _arg1 = ArraySize(_args) > 0 ? DataParamEntry::ToInteger(_args[0]) : WRONG_VALUE;
+ switch (_action) {
+ case INDI_ACTION_CLEAR_CACHE:
+ _arg1 = _arg1 > 0 ? _arg1 : TimeCurrent();
+ idata.Clear(_arg1);
+ return true;
+ default:
+ GetLogger().Error(StringFormat("Invalid Indicator action: %s!", EnumToString(_action), __FUNCTION_LINE__));
+ return false;
+ }
+ return _result;
+ }
+ bool ExecuteAction(ENUM_INDICATOR_ACTION _action) {
+ ARRAY(DataParamEntry, _args);
+ return ExecuteAction(_action, _args);
+ }
+ bool ExecuteAction(ENUM_INDICATOR_ACTION _action, long _arg1) {
+ ARRAY(DataParamEntry, _args);
+ DataParamEntry _param1 = _arg1;
+ ArrayPushObject(_args, _param1);
+ _args[0].integer_value = _arg1;
+ return ExecuteAction(_action, _args);
+ }
+
+ /* Other methods */
+
+ /**
+ * Releases indicator's handle.
+ *
+ * Note: Not supported in MT4.
+ */
+ void ReleaseHandle() {
+#ifdef __MQL5__
+ if (istate.handle != INVALID_HANDLE) {
+ IndicatorRelease(istate.handle);
+ }
+#endif
+ istate.handle = INVALID_HANDLE;
+ istate.is_changed = true;
+ }
+
+ /**
+ * Checks whether indicator has a valid value for a given shift.
+ */
+ virtual bool HasValidEntry(int _shift = 0) {
+ unsigned int position;
+ long bar_time = GetBarTime(_shift);
+
+ if (idata.KeyExists(bar_time, position)) {
+ return idata.GetByPos(position).IsValid();
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds entry to the indicator's buffer. Invalid entry won't be added.
+ */
+ bool AddEntry(IndicatorDataEntry& entry, int _shift = 0) {
+ if (!entry.IsValid()) return false;
+
+ datetime timestamp = GetBarTime(_shift);
+ entry.timestamp = timestamp;
+ idata.Add(entry, timestamp);
+
+ return true;
+ }
+
+ /**
+ * Returns shift at which the last known valid entry exists for a given
+ * period (or from the start, when period is not specified).
+ */
+ bool GetLastValidEntryShift(int& out_shift, int period = 0) {
+ out_shift = 0;
+
+ while (true) {
+ if ((period != 0 && out_shift >= period) || !HasValidEntry(out_shift + 1))
+ return out_shift > 0; // Current shift is always invalid.
+
+ ++out_shift;
+ }
+
+ return out_shift > 0;
+ }
+
+ /**
+ * Returns shift at which the oldest known valid entry exists for a given
+ * period (or from the start, when period is not specified).
+ */
+ bool GetOldestValidEntryShift(int& out_shift, int& out_num_valid, int shift = 0, int period = 0) {
+ bool found = false;
+ // Counting from previous up to previous - period.
+ for (out_shift = shift + 1; out_shift < shift + period + 1; ++out_shift) {
+ if (!HasValidEntry(out_shift)) {
+ --out_shift;
+ out_num_valid = out_shift - shift;
+ return found;
+ } else
+ found = true;
+ }
+
+ --out_shift;
+ out_num_valid = out_shift - shift;
+ return found;
+ }
+
+ /**
+ * Checks whether indicator has valid at least given number of last entries
+ * (counting from given shift or 0).
+ */
+ bool HasAtLeastValidLastEntries(int period, int shift = 0) {
+ for (int i = 0; i < period; ++i)
+ if (!HasValidEntry(shift + i)) return false;
+
+ return true;
+ }
+
+ virtual ENUM_IDATA_VALUE_RANGE GetIDataValueRange() = 0;
+
+ ValueStorage* GetValueStorage(int _mode = 0) {
+ if (value_storages[_mode] == NULL) {
+ value_storages[_mode] = new IndicatorBufferValueStorage(THIS_PTR, _mode);
+ }
+ return value_storages[_mode];
+ }
+
+ /**
+ * Returns indicator value for a given shift and mode.
+ */
+ template
+ T GetValue(int _shift = 0, int _mode = -1) {
+ T _result;
+ int _index = _mode != -1 ? _mode : GetDataSourceMode();
+ GetEntry(_shift).values[_index].Get(_result);
+ ResetLastError();
+ return _result;
+ }
+
+ /**
+ * Returns price corresponding to indicator value for a given shift and mode.
+ *
+ * Can be useful for calculating trailing stops based on the indicator.
+ *
+ * @return
+ * Returns price value of the corresponding indicator values.
+ */
+ template
+ float GetValuePrice(int _shift = 0, int _mode = 0, ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL) {
+ float _price = 0;
+ if (GetIDataValueRange() != IDATA_RANGE_PRICE) {
+ _price = (float)GetPrice(_ap, _shift);
+ } else if (GetIDataValueRange() == IDATA_RANGE_PRICE) {
+ // When indicator values are the actual prices.
+ T _values[4];
+ if (!CopyValues(_values, 4, _shift, _mode)) {
+ // When values aren't valid, return 0.
+ return _price;
+ }
+ datetime _bar_time = GetBarTime(_shift);
+ float _value = 0;
+ BarOHLC _ohlc(_values, _bar_time);
+ _price = _ohlc.GetAppliedPrice(_ap);
+ }
+ return _price;
+ }
+
+ /**
+ * Returns values for a given shift.
+ *
+ * Note: Remember to check if shift exists by HasValidEntry(shift).
+ */
+ template
+ bool GetValues(int _shift, T& _out1, T& _out2) {
+ IndicatorDataEntry _entry = GetEntry(_shift);
+ _out1 = _entry.values[0];
+ _out2 = _entry.values[1];
+ bool _result = GetLastError() != 4401;
+ ResetLastError();
+ return _result;
+ }
+
+ template
+ bool GetValues(int _shift, T& _out1, T& _out2, T& _out3) {
+ IndicatorDataEntry _entry = GetEntry(_shift);
+ _out1 = _entry.values[0];
+ _out2 = _entry.values[1];
+ _out3 = _entry.values[2];
+ bool _result = GetLastError() != 4401;
+ ResetLastError();
+ return _result;
+ }
+
+ template
+ bool GetValues(int _shift, T& _out1, T& _out2, T& _out3, T& _out4) {
+ IndicatorDataEntry _entry = GetEntry(_shift);
+ _out1 = _entry.values[0];
+ _out2 = _entry.values[1];
+ _out3 = _entry.values[2];
+ _out4 = _entry.values[3];
+ bool _result = GetLastError() != 4401;
+ ResetLastError();
+ return _result;
+ }
+
+ virtual void OnTick() {}
+
+ /* Data representation methods */
+
+ /* Virtual methods */
+
+ /**
+ * Returns stored data in human-readable format.
+ */
+ // virtual bool ToString() = NULL; // @fixme?
+
+ /**
+ * Whether we can and have to select mode when specifying data source.
+ */
+ virtual bool IsDataSourceModeSelectable() { return true; }
+
+ /**
+ * Update indicator.
+ */
+ virtual bool Update() {
+ // @todo
+ return false;
+ };
+
+ /**
+ * Returns the indicator's struct value.
+ */
+ virtual IndicatorDataEntry GetEntry(int _shift = 0) {
+ IndicatorDataEntry _entry;
+ return _entry;
+ };
+
+ /**
+ * Returns the indicator's entry value.
+ */
+ virtual MqlParam GetEntryValue(int _shift = 0, int _mode = 0) {
+ MqlParam _param = {TYPE_FLOAT};
+ _param.double_value = (float)GetEntry(_shift).GetValue(_mode);
+ return _param;
+ }
+
+ /**
+ * Returns the indicator's value in plain format.
+ */
+ virtual string ToString(int _shift = 0) {
+ IndicatorDataEntry _entry = GetEntry(_shift);
+ int _serializer_flags =
+ SERIALIZER_FLAG_SKIP_HIDDEN | SERIALIZER_FLAG_INCLUDE_DEFAULT | SERIALIZER_FLAG_INCLUDE_DYNAMIC;
+ SerializerConverter _stub_indi =
+ SerializerConverter::MakeStubObject(_serializer_flags, _entry.GetSize());
+ return SerializerConverter::FromObject(_entry, _serializer_flags).ToString(0, &_stub_indi);
+ }
+
+ int GetBarsCalculated() {
+ int _bars = Bars(GetSymbol(), GetTf());
+
+ if (!is_fed) {
+ calc_start_bar = 0;
+
+ // Calculating start_bar.
+ for (int i = 0; i < _bars; ++i) {
+ // Iterating from the oldest.
+ IndicatorDataEntry _entry = GetEntry(_bars - i - 1);
+
+ if (_entry.IsValid()) {
+ // From this point we assume that future entries will be all valid.
+ calc_start_bar = i;
+ is_fed = true;
+
+ return _bars - calc_start_bar;
+ }
+ }
+ }
+
+ // Assuming all entries are calculated (even if have invalid values).
+ return _bars;
+ }
+};
+
+/**
+ * CopyBuffer() method to be used on Indicator instance with ValueStorage buffer.
+ *
+ * Note that data will be copied so that the oldest element will be located at the start of the physical memory
+ * allocated for the array
+ */
+template
+int CopyBuffer(IndicatorBase* _indi, int _mode, int _start, int _count, ValueStorage& _buffer, int _rates_total) {
+ int _num_copied = 0;
+ int _buffer_size = ArraySize(_buffer);
+
+ if (_buffer_size < _rates_total) {
+ _buffer_size = ArrayResize(_buffer, _rates_total);
+ }
+
+ for (int i = _start; i < _count; ++i) {
+ IndicatorDataEntry _entry = _indi.GetEntry(i);
+
+ if (!_entry.IsValid()) {
+ break;
+ }
+
+ T _value = _entry.GetValue(_mode);
+
+ // Print(_value);
+
+ _buffer[_buffer_size - i - 1] = _value;
+ ++_num_copied;
+ }
+
+ return _num_copied;
+}
+
+/**
+ * BarsCalculated()-compatible method to be used on Indicator instance.
+ */
+int BarsCalculated(IndicatorBase* _indi) { return _indi.GetBarsCalculated(); }
diff --git a/Indicators/Bitwise/Indi_Candle.mqh b/Indicators/Bitwise/Indi_Candle.mqh
index bfd955cf2..9ec62d809 100644
--- a/Indicators/Bitwise/Indi_Candle.mqh
+++ b/Indicators/Bitwise/Indi_Candle.mqh
@@ -32,16 +32,14 @@
// Structs.
struct CandleParams : IndicatorParams {
// Struct constructor.
- void CandleParams(int _shift = 0, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- itype = INDI_CANDLE;
- max_modes = 1;
- SetDataValueType(TYPE_INT);
+ CandleParams(int _shift = 0, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : IndicatorParams(INDI_CANDLE, 1, TYPE_INT) {
SetDataValueRange(IDATA_RANGE_RANGE);
+ SetDataSourceType(IDATA_BUILTIN);
shift = _shift;
tf = _tf;
};
- void CandleParams(CandleParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- this = _params;
+ CandleParams(CandleParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
tf = _tf;
};
};
@@ -49,16 +47,13 @@ struct CandleParams : IndicatorParams {
/**
* Implements Candle Pattern Detector.
*/
-class Indi_Candle : public Indicator {
- protected:
- CandleParams params;
-
+class Indi_Candle : public Indicator {
public:
/**
* Class constructor.
*/
- Indi_Candle(CandleParams &_params) : params(_params), Indicator((IndicatorParams)_params){};
- Indi_Candle(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_CANDLE, _tf) { params.tf = _tf; };
+ Indi_Candle(CandleParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src){};
+ Indi_Candle(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_CANDLE, _tf) { iparams.tf = _tf; };
/**
* Returns the indicator's struct value.
@@ -66,7 +61,7 @@ class Indi_Candle : public Indicator {
IndicatorDataEntry GetEntry(int _shift = 0) {
long _bar_time = GetBarTime(_shift);
unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
+ IndicatorDataEntry _entry(iparams.GetMaxModes());
if (idata.KeyExists(_bar_time, _position)) {
_entry = idata.GetByPos(_position);
} else {
@@ -75,7 +70,7 @@ class Indi_Candle : public Indicator {
ResetLastError();
BarOHLC _ohlcs[1];
- switch (params.idstype) {
+ switch (iparams.idstype) {
case IDATA_BUILTIN:
// In this mode, price is fetched from chart.
_ohlcs[0] = Chart::GetOHLC(_shift);
@@ -84,7 +79,7 @@ class Indi_Candle : public Indicator {
// In this mode, price is fetched from given indicator. Such indicator
// must have at least 4 buffers and define OHLC in the first 4 buffers.
// Indi_Price is an example of such indicator.
- if (GetDataSource() == NULL) {
+ if (indi_src == NULL) {
GetLogger().Error(
"In order use custom indicator as a source, you need to select one using SetIndicatorData() method, "
"which is a part of CandleParams structure.",
@@ -98,10 +93,10 @@ class Indi_Candle : public Indicator {
return _entry;
}
- _ohlcs[0].open = GetDataSource().GetValue(_shift, PRICE_OPEN);
- _ohlcs[0].high = GetDataSource().GetValue(_shift, PRICE_HIGH);
- _ohlcs[0].low = GetDataSource().GetValue(_shift, PRICE_LOW);
- _ohlcs[0].close = GetDataSource().GetValue(_shift, PRICE_CLOSE);
+ _ohlcs[0].open = indi_src.GetValue(_shift, PRICE_OPEN);
+ _ohlcs[0].high = indi_src.GetValue(_shift, PRICE_HIGH);
+ _ohlcs[0].low = indi_src.GetValue(_shift, PRICE_LOW);
+ _ohlcs[0].close = indi_src.GetValue(_shift, PRICE_CLOSE);
break;
default:
SetUserError(ERR_INVALID_PARAMETER);
@@ -116,7 +111,7 @@ class Indi_Candle : public Indicator {
istate.is_ready = true;
if (_entry.IsValid()) {
- _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType()));
+ _entry.AddFlags(_entry.GetDataTypeFlag(iparams.GetDataValueType()));
idata.Add(_entry, _bar_time);
}
}
diff --git a/Indicators/Indi_AC.mqh b/Indicators/Indi_AC.mqh
index eecbeb1c0..e280631f4 100644
--- a/Indicators/Indi_AC.mqh
+++ b/Indicators/Indi_AC.mqh
@@ -32,17 +32,13 @@ double iAC(string _symbol, int _tf, int _shift) { return Indi_AC::iAC(_symbol, (
// Structs.
struct ACParams : IndicatorParams {
// Struct constructor.
- void ACParams(int _shift = 0, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- itype = INDI_AC;
- max_modes = 1;
- SetDataValueType(TYPE_DOUBLE);
+ ACParams(int _shift = 0) : IndicatorParams(INDI_AC, 1, TYPE_DOUBLE) {
SetDataValueRange(IDATA_RANGE_MIXED);
SetCustomIndicatorName("Examples\\Accelerator");
shift = _shift;
- tf = _tf;
};
- void ACParams(ACParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- this = _params;
+ ACParams(ACParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
tf = _tf;
};
};
@@ -50,16 +46,13 @@ struct ACParams : IndicatorParams {
/**
* Implements the Bill Williams' Accelerator/Decelerator oscillator.
*/
-class Indi_AC : public Indicator {
- protected:
- ACParams params;
-
+class Indi_AC : public Indicator {
public:
/**
* Class constructor.
*/
- Indi_AC(ACParams &_params) : Indicator((IndicatorParams)_params) { params = _params; };
- Indi_AC(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_AC, _tf) { params.SetTf(_tf); };
+ Indi_AC(ACParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src){};
+ Indi_AC(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_AC, _tf){};
/**
* Returns the indicator value.
@@ -69,7 +62,7 @@ class Indi_AC : public Indicator {
* - https://www.mql5.com/en/docs/indicators/iac
*/
static double iAC(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0,
- Indicator *_obj = NULL) {
+ IndicatorBase *_obj = NULL) {
#ifdef __MQL4__
return ::iAC(_symbol, _tf, _shift);
#else // __MQL5__
@@ -87,7 +80,7 @@ class Indi_AC : public Indicator {
if (Terminal::IsVisualMode()) {
// To avoid error 4806 (ERR_INDICATOR_DATA_NOT_FOUND),
// we check the number of calculated data only in visual mode.
- int _bars_calc = BarsCalculated(_handle);
+ int _bars_calc = ::BarsCalculated(_handle);
if (GetLastError() > 0) {
return EMPTY_VALUE;
} else if (_bars_calc <= 2) {
@@ -105,16 +98,16 @@ class Indi_AC : public Indicator {
/**
* Returns the indicator's value.
*/
- double GetValue(int _mode = 0, int _shift = 0) {
+ virtual double GetValue(int _mode = 0, int _shift = 0) {
ResetLastError();
double _value = EMPTY_VALUE;
- switch (params.idstype) {
+ switch (iparams.idstype) {
case IDATA_BUILTIN:
istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle;
- _value = Indi_AC::iAC(GetSymbol(), GetTf(), _shift, GetPointer(this));
+ _value = Indi_AC::iAC(GetSymbol(), GetTf(), _shift, THIS_PTR);
break;
case IDATA_ICUSTOM:
- _value = iCustom(istate.handle, GetSymbol(), GetTf(), params.GetCustomIndicatorName(), _mode, _shift);
+ _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift);
break;
default:
SetUserError(ERR_INVALID_PARAMETER);
@@ -124,29 +117,6 @@ class Indi_AC : public Indicator {
return _value;
}
- /**
- * Returns the indicator's struct value.
- */
- IndicatorDataEntry GetEntry(int _shift = 0) {
- long _bar_time = GetBarTime(_shift);
- unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
- if (idata.KeyExists(_bar_time, _position)) {
- _entry = idata.GetByPos(_position);
- } else {
- _entry.timestamp = GetBarTime(_shift);
- for (int _mode = 0; _mode < (int)params.max_modes; _mode++) {
- _entry.values[_mode] = GetValue(_mode, _shift);
- }
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, !_entry.HasValue(NULL) && !_entry.HasValue(EMPTY_VALUE));
- if (_entry.IsValid()) {
- _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType()));
- idata.Add(_entry, _bar_time);
- }
- }
- return _entry;
- }
-
/**
* Returns reusable indicator for a given symbol and time-frame.
*/
diff --git a/Indicators/Indi_AD.mqh b/Indicators/Indi_AD.mqh
index afe5b1d50..b935032f5 100644
--- a/Indicators/Indi_AD.mqh
+++ b/Indicators/Indi_AD.mqh
@@ -31,17 +31,13 @@ double iAD(string _symbol, int _tf, int _shift) { return Indi_AD::iAD(_symbol, (
// Structs.
struct ADParams : IndicatorParams {
// Struct constructor.
- ADParams(int _shift = 0, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- itype = INDI_AD;
- max_modes = 1;
- SetDataValueType(TYPE_DOUBLE);
+ ADParams(int _shift = 0) : IndicatorParams(INDI_AD, 1, TYPE_DOUBLE) {
SetDataValueRange(IDATA_RANGE_MIXED);
SetCustomIndicatorName("Examples\\AD");
shift = _shift;
- tf = _tf;
};
- ADParams(ADParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- this = _params;
+ ADParams(ADParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
tf = _tf;
};
};
@@ -49,16 +45,13 @@ struct ADParams : IndicatorParams {
/**
* Implements the Accumulation/Distribution indicator.
*/
-class Indi_AD : public Indicator {
- protected:
- ADParams params;
-
+class Indi_AD : public Indicator {
public:
/**
* Class constructor.
*/
- Indi_AD(ADParams &_p) : Indicator((IndicatorParams)_p) { params = _p; };
- Indi_AD(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_AD, _tf) { params.SetTf(_tf); };
+ Indi_AD(ADParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src){};
+ Indi_AD(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_AD, _tf) { iparams.SetTf(_tf); };
/**
* Returns the indicator value.
@@ -68,7 +61,7 @@ class Indi_AD : public Indicator {
* - https://www.mql5.com/en/docs/indicators/iad
*/
static double iAD(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0,
- Indicator *_obj = NULL) {
+ IndicatorBase *_obj = NULL) {
#ifdef __MQL4__
return ::iAD(_symbol, _tf, _shift);
#else // __MQL5__
@@ -104,18 +97,16 @@ class Indi_AD : public Indicator {
/**
* Returns the indicator's value.
*/
- double GetValue(int _mode = 0, int _shift = 0) {
+ virtual double GetValue(int _mode = 0, int _shift = 0) {
ResetLastError();
double _value = EMPTY_VALUE;
- switch (params.idstype) {
+ switch (iparams.idstype) {
case IDATA_BUILTIN:
istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle;
- _value = Indi_AD::iAD(Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF), _shift,
- GetPointer(this));
+ _value = Indi_AD::iAD(GetSymbol(), GetTf(), _shift, THIS_PTR);
break;
case IDATA_ICUSTOM:
- _value = iCustom(istate.handle, Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF),
- params.GetCustomIndicatorName(), _mode, _shift);
+ _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift);
break;
default:
SetUserError(ERR_INVALID_PARAMETER);
@@ -125,29 +116,6 @@ class Indi_AD : public Indicator {
return _value;
}
- /**
- * Returns the indicator's struct value.
- */
- IndicatorDataEntry GetEntry(int _shift = 0) {
- long _bar_time = GetBarTime(_shift);
- unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
- if (idata.KeyExists(_bar_time, _position)) {
- _entry = idata.GetByPos(_position);
- } else {
- _entry.timestamp = GetBarTime(_shift);
- for (int _mode = 0; _mode < (int)params.max_modes; _mode++) {
- _entry.values[_mode] = GetValue(_mode, _shift);
- }
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, !_entry.HasValue(NULL) && !_entry.HasValue(EMPTY_VALUE));
- if (_entry.IsValid()) {
- _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType()));
- idata.Add(_entry, _bar_time);
- }
- }
- return _entry;
- }
-
/**
* Returns the indicator's entry value.
*/
diff --git a/Indicators/Indi_ADX.mqh b/Indicators/Indi_ADX.mqh
index ed2d792a2..104c0ca15 100644
--- a/Indicators/Indi_ADX.mqh
+++ b/Indicators/Indi_ADX.mqh
@@ -37,14 +37,11 @@ struct ADXParams : IndicatorParams {
unsigned int period;
ENUM_APPLIED_PRICE applied_price;
// Struct constructors.
- void ADXParams(unsigned int _period = 14, ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL, int _shift = 0,
- ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN)
- : period(_period), applied_price(_ap) {
- itype = itype == INDI_NONE ? INDI_ADX : itype;
+ ADXParams(unsigned int _period = 14, ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL, int _shift = 0,
+ ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN)
+ : period(_period), applied_price(_ap), IndicatorParams(INDI_ADX, FINAL_INDI_ADX_LINE_ENTRY, TYPE_DOUBLE) {
SetDataSourceType(_idstype);
- SetDataValueType(TYPE_DOUBLE);
SetDataValueRange(IDATA_RANGE_RANGE);
- SetMaxModes(FINAL_INDI_ADX_LINE_ENTRY);
SetShift(_shift);
switch (idstype) {
case IDATA_ICUSTOM:
@@ -52,15 +49,10 @@ struct ADXParams : IndicatorParams {
SetCustomIndicatorName("Examples\\ADX");
}
break;
- case IDATA_INDICATOR:
- if (indi_data_source == NULL) {
- SetDataSource(Indi_Price::GetCached(_shift, _tf, applied_price, _period));
- }
- break;
}
};
- void ADXParams(ADXParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- this = _params;
+ ADXParams(ADXParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
tf = _tf;
};
};
@@ -68,15 +60,12 @@ struct ADXParams : IndicatorParams {
/**
* Implements the Average Directional Movement Index indicator.
*/
-class Indi_ADX : public Indicator {
- protected:
- ADXParams params;
-
+class Indi_ADX : public Indicator {
public:
/**
* Class constructor.
*/
- Indi_ADX(ADXParams &_p) : params(_p.period, _p.applied_price), Indicator((IndicatorParams)_p) { params = _p; }
+ Indi_ADX(ADXParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src) {}
Indi_ADX(ENUM_TIMEFRAMES _tf) : Indicator(INDI_ADX, _tf) {}
/**
@@ -90,7 +79,7 @@ class Indi_ADX : public Indicator {
ENUM_APPLIED_PRICE _applied_price, // (MT5): not used
int _mode = LINE_MAIN_ADX, // (MT4/MT5): 0 - MODE_MAIN/MAIN_LINE, 1 -
// MODE_PLUSDI/PLUSDI_LINE, 2 - MODE_MINUSDI/MINUSDI_LINE
- int _shift = 0, Indicator *_obj = NULL) {
+ int _shift = 0, IndicatorBase *_obj = NULL) {
#ifdef __MQL4__
return ::iADX(_symbol, _tf, _period, _applied_price, _mode, _shift);
#else // __MQL5__
@@ -126,18 +115,17 @@ class Indi_ADX : public Indicator {
/**
* Returns the indicator's value.
*/
- double GetValue(int _mode = LINE_MAIN_ADX, int _shift = 0) {
+ virtual double GetValue(int _mode = LINE_MAIN_ADX, int _shift = 0) {
ResetLastError();
double _value = EMPTY_VALUE;
- switch (params.idstype) {
+ switch (iparams.idstype) {
case IDATA_BUILTIN:
istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle;
- _value = Indi_ADX::iADX(Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF), GetPeriod(),
- GetAppliedPrice(), _mode, _shift, GetPointer(this));
+ _value = Indi_ADX::iADX(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _mode, _shift, THIS_PTR);
break;
case IDATA_ICUSTOM:
- _value = iCustom(istate.handle, Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF),
- params.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, _mode, _shift);
+ _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/,
+ _mode, _shift);
break;
default:
SetUserError(ERR_INVALID_PARAMETER);
@@ -147,30 +135,6 @@ class Indi_ADX : public Indicator {
return _value;
}
- /**
- * Returns the indicator's struct value.
- */
- IndicatorDataEntry GetEntry(int _shift = 0) {
- long _bar_time = GetBarTime(_shift);
- unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
- if (idata.KeyExists(_bar_time, _position)) {
- _entry = idata.GetByPos(_position);
- } else {
- _entry.timestamp = GetBarTime(_shift);
- for (int _mode = 0; _mode < (int)params.max_modes; _mode++) {
- _entry.values[_mode] = Indi_ADX::GetValue(_mode, _shift);
- }
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, !_entry.HasValue((double)NULL) && !_entry.HasValue(EMPTY_VALUE) &&
- _entry.IsWithinRange(0.0, 100.0));
- if (_entry.IsValid()) {
- _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType()));
- idata.Add(_entry, _bar_time);
- }
- }
- return _entry;
- }
-
/**
* Returns the indicator's entry value.
*/
@@ -180,19 +144,26 @@ class Indi_ADX : public Indicator {
return _param;
}
+ /**
+ * Checks if indicator entry values are valid.
+ */
+ virtual bool IsValidEntry(IndicatorDataEntry &_entry) {
+ return Indicator::IsValidEntry(_entry) && _entry.IsWithinRange(0.0, 100.0);
+ }
+
/* Getters */
/**
* Get period value.
*/
- unsigned int GetPeriod() { return params.period; }
+ unsigned int GetPeriod() { return iparams.period; }
/**
* Get applied price value.
*
* Note: Not used in MT5.
*/
- ENUM_APPLIED_PRICE GetAppliedPrice() { return params.applied_price; }
+ ENUM_APPLIED_PRICE GetAppliedPrice() { return iparams.applied_price; }
/* Setters */
@@ -201,7 +172,7 @@ class Indi_ADX : public Indicator {
*/
void SetPeriod(unsigned int _period) {
istate.is_changed = true;
- params.period = _period;
+ iparams.period = _period;
}
/**
@@ -211,6 +182,6 @@ class Indi_ADX : public Indicator {
*/
void SetAppliedPrice(ENUM_APPLIED_PRICE _applied_price) {
istate.is_changed = true;
- params.applied_price = _applied_price;
+ iparams.applied_price = _applied_price;
}
};
diff --git a/Indicators/Indi_ADXW.mqh b/Indicators/Indi_ADXW.mqh
index 4ca230c58..ee8ac7436 100644
--- a/Indicators/Indi_ADXW.mqh
+++ b/Indicators/Indi_ADXW.mqh
@@ -36,8 +36,8 @@
// Structs.
struct ADXWParams : ADXParams {
// Struct constructor.
- void ADXWParams(int _period = 14, ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL, int _shift = 0,
- ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN)
+ ADXWParams(int _period = 14, ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL, int _shift = 0,
+ ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN)
: ADXParams(_period, _ap, _shift, _tf, _idstype) {
itype = itype == INDI_NONE || itype == INDI_ADX ? INDI_ADXW : itype;
switch (idstype) {
@@ -46,29 +46,28 @@ struct ADXWParams : ADXParams {
break;
}
};
- void ADXWParams(ADXWParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : ADXParams(_params, _tf) {}
- void ADXWParams(ADXParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : ADXParams(_params, _tf) {}
+ ADXWParams(ADXWParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
+ tf = _tf;
+ };
};
/**
* Implements the Average Directional Movement Index indicator by Welles Wilder.
*/
-class Indi_ADXW : public Indicator {
- protected:
- ADXWParams params;
-
+class Indi_ADXW : public Indicator {
public:
/**
* Class constructor.
*/
- Indi_ADXW(ADXWParams &_params) : params(_params.period), Indicator((IndicatorParams)_params) { params = _params; };
- Indi_ADXW(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_ADXW, _tf) { params.tf = _tf; };
+ Indi_ADXW(ADXWParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src){};
+ Indi_ADXW(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_ADXW, _tf){};
/**
* Built-in version of ADX Wilder.
*/
static double iADXWilder(string _symbol, ENUM_TIMEFRAMES _tf, int _ma_period, int _mode = LINE_MAIN_ADX,
- int _shift = 0, Indicator *_obj = NULL) {
+ int _shift = 0, IndicatorBase *_obj = NULL) {
#ifdef __MQL5__
INDICATOR_BUILTIN_CALL_AND_RETURN(::iADXWilder(_symbol, _tf, _ma_period), _mode, _shift);
#else
@@ -216,16 +215,16 @@ class Indi_ADXW : public Indicator {
/**
* Returns the indicator's value.
*/
- double GetValue(int _mode = LINE_MAIN_ADX, int _shift = 0) {
+ virtual double GetValue(int _mode = LINE_MAIN_ADX, int _shift = 0) {
ResetLastError();
double _value = EMPTY_VALUE;
- switch (params.idstype) {
+ switch (iparams.idstype) {
case IDATA_BUILTIN:
istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle;
_value = Indi_ADXW::iADXWilder(GetSymbol(), GetTf(), GetPeriod(), _mode, _shift, THIS_PTR);
break;
case IDATA_ICUSTOM:
- _value = iCustom(istate.handle, GetSymbol(), GetTf(), params.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/,
+ _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/,
_mode, _shift);
break;
default:
@@ -236,29 +235,6 @@ class Indi_ADXW : public Indicator {
return _value;
}
- /**
- * Returns the indicator's struct value.
- */
- IndicatorDataEntry GetEntry(int _shift = 0) {
- long _bar_time = GetBarTime(_shift);
- unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
- if (idata.KeyExists(_bar_time, _position)) {
- _entry = idata.GetByPos(_position);
- } else {
- _entry.timestamp = GetBarTime(_shift);
- for (int _mode = 0; _mode < (int)params.max_modes; _mode++) {
- _entry.values[_mode] = GetValue(_mode, _shift);
- }
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_DOUBLE, true);
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, !_entry.HasValue(NULL) && !_entry.HasValue(EMPTY_VALUE));
- if (_entry.IsValid()) {
- idata.Add(_entry, _bar_time);
- }
- }
- return _entry;
- }
-
/**
* Returns the indicator's entry value.
*/
@@ -273,7 +249,7 @@ class Indi_ADXW : public Indicator {
/**
* Get period value.
*/
- unsigned int GetPeriod() { return params.period; }
+ unsigned int GetPeriod() { return iparams.period; }
/* Setters */
@@ -282,6 +258,6 @@ class Indi_ADXW : public Indicator {
*/
void SetPeriod(unsigned int _period) {
istate.is_changed = true;
- params.period = _period;
+ iparams.period = _period;
}
};
diff --git a/Indicators/Indi_AMA.mqh b/Indicators/Indi_AMA.mqh
index 0041828d0..1e16b0a81 100644
--- a/Indicators/Indi_AMA.mqh
+++ b/Indicators/Indi_AMA.mqh
@@ -35,37 +35,26 @@ struct IndiAMAParams : IndicatorParams {
unsigned int ama_shift;
ENUM_APPLIED_PRICE applied_price;
// Struct constructor.
- void IndiAMAParams(int _period = 10, int _fast_period = 2, int _slow_period = 30, int _ama_shift = 0,
- ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL, int _shift = 0, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT,
- ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN)
+ IndiAMAParams(int _period = 10, int _fast_period = 2, int _slow_period = 30, int _ama_shift = 0,
+ ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL, int _shift = 0)
: period(_period),
fast_period(_fast_period),
slow_period(_slow_period),
ama_shift(_ama_shift),
- applied_price(_ap) {
- itype = itype == INDI_NONE ? INDI_AMA : itype;
- SetDataSourceType(_idstype);
- SetDataValueType(TYPE_DOUBLE);
+ applied_price(_ap),
+ IndicatorParams(INDI_AMA, 1, TYPE_DOUBLE) {
SetDataValueRange(IDATA_RANGE_PRICE);
- SetMaxModes(1);
SetShift(_shift);
- tf = _tf;
switch (idstype) {
case IDATA_ICUSTOM:
if (custom_indi_name == "") {
SetCustomIndicatorName("Examples\\AMA");
}
break;
- case IDATA_INDICATOR:
- if (GetDataSource() == NULL) {
- SetDataSource(Indi_Price::GetCached(_shift, _tf, _ap, _period), false);
- SetDataSourceMode(0);
- }
- break;
}
};
- void IndiAMAParams(IndiAMAParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- this = _params;
+ IndiAMAParams(IndiAMAParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
tf = _tf;
};
};
@@ -73,22 +62,20 @@ struct IndiAMAParams : IndicatorParams {
/**
* Implements the AMA indicator.
*/
-class Indi_AMA : public Indicator {
- protected:
- IndiAMAParams params;
-
+class Indi_AMA : public Indicator {
public:
/**
* Class constructor.
*/
- Indi_AMA(IndiAMAParams &_params) : params(_params.period), Indicator((IndicatorParams)_params) { params = _params; };
- Indi_AMA(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_AMA, _tf) { params.tf = _tf; };
+ Indi_AMA(IndiAMAParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src){};
+ Indi_AMA(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_AMA, _tf){};
/**
* Built-in version of AMA.
*/
static double iAMA(string _symbol, ENUM_TIMEFRAMES _tf, int _ama_period, int _fast_ema_period, int _slow_ema_period,
- int _ama_shift, ENUM_APPLIED_PRICE _ap, int _mode = 0, int _shift = 0, Indicator *_obj = NULL) {
+ int _ama_shift, ENUM_APPLIED_PRICE _ap, int _mode = 0, int _shift = 0,
+ IndicatorBase *_obj = NULL) {
#ifdef __MQL5__
INDICATOR_BUILTIN_CALL_AND_RETURN(
::iAMA(_symbol, _tf, _ama_period, _fast_ema_period, _slow_ema_period, _ama_shift, _ap), _mode, _shift);
@@ -212,16 +199,16 @@ class Indi_AMA : public Indicator {
/**
* Returns the indicator's value.
*/
- double GetValue(int _mode = 0, int _shift = 0) {
+ virtual double GetValue(int _mode = 0, int _shift = 0) {
ResetLastError();
double _value = EMPTY_VALUE;
- switch (params.idstype) {
+ switch (iparams.idstype) {
case IDATA_BUILTIN:
_value = Indi_AMA::iAMA(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetFastPeriod(), GetSlowPeriod(),
GetAMAShift(), GetAppliedPrice() /*]*/, _mode, _shift, THIS_PTR);
break;
case IDATA_ICUSTOM:
- _value = iCustom(istate.handle, GetSymbol(), GetTf(), params.GetCustomIndicatorName(), /*[*/ GetPeriod(),
+ _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(),
GetFastPeriod(), GetSlowPeriod(), GetAMAShift() /*]*/, _mode, _shift);
break;
@@ -237,29 +224,6 @@ class Indi_AMA : public Indicator {
return _value;
}
- /**
- * Returns the indicator's struct value.
- */
- IndicatorDataEntry GetEntry(int _shift = 0) {
- long _bar_time = GetBarTime(_shift);
- unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
- if (idata.KeyExists(_bar_time, _position)) {
- _entry = idata.GetByPos(_position);
- } else {
- _entry.timestamp = GetBarTime(_shift);
- for (int _mode = 0; _mode < (int)params.max_modes; _mode++) {
- _entry.values[_mode] = GetValue(_mode, _shift);
- }
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, !_entry.HasValue(NULL) && !_entry.HasValue(EMPTY_VALUE));
- if (_entry.IsValid()) {
- _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType()));
- idata.Add(_entry, _bar_time);
- }
- }
- return _entry;
- }
-
/**
* Returns the indicator's entry value.
*/
@@ -274,27 +238,27 @@ class Indi_AMA : public Indicator {
/**
* Get AMA shift.
*/
- unsigned int GetAMAShift() { return params.ama_shift; }
+ unsigned int GetAMAShift() { return iparams.ama_shift; }
/**
* Get period.
*/
- unsigned int GetPeriod() { return params.period; }
+ unsigned int GetPeriod() { return iparams.period; }
/**
* Get fast period.
*/
- unsigned int GetFastPeriod() { return params.fast_period; }
+ unsigned int GetFastPeriod() { return iparams.fast_period; }
/**
* Get slow period.
*/
- unsigned int GetSlowPeriod() { return params.slow_period; }
+ unsigned int GetSlowPeriod() { return iparams.slow_period; }
/**
* Get applied price.
*/
- ENUM_APPLIED_PRICE GetAppliedPrice() { return params.applied_price; }
+ ENUM_APPLIED_PRICE GetAppliedPrice() { return iparams.applied_price; }
/* Setters */
@@ -303,7 +267,7 @@ class Indi_AMA : public Indicator {
*/
void SetAMAShift(unsigned int _ama_shift) {
istate.is_changed = true;
- params.ama_shift = _ama_shift;
+ iparams.ama_shift = _ama_shift;
}
/**
@@ -311,7 +275,7 @@ class Indi_AMA : public Indicator {
*/
void SetPeriod(unsigned int _period) {
istate.is_changed = true;
- params.period = _period;
+ iparams.period = _period;
}
/**
@@ -319,7 +283,7 @@ class Indi_AMA : public Indicator {
*/
void SetFastPeriod(unsigned int _fast_period) {
istate.is_changed = true;
- params.fast_period = _fast_period;
+ iparams.fast_period = _fast_period;
}
/**
@@ -327,7 +291,7 @@ class Indi_AMA : public Indicator {
*/
void SetSlowPeriod(unsigned int _slow_period) {
istate.is_changed = true;
- params.slow_period = _slow_period;
+ iparams.slow_period = _slow_period;
}
/**
@@ -335,6 +299,6 @@ class Indi_AMA : public Indicator {
*/
void SetAppliedPrice(ENUM_APPLIED_PRICE _applied_price) {
istate.is_changed = true;
- params.applied_price = _applied_price;
+ iparams.applied_price = _applied_price;
}
};
diff --git a/Indicators/Indi_AO.mqh b/Indicators/Indi_AO.mqh
index d93977120..8d974e9ae 100644
--- a/Indicators/Indi_AO.mqh
+++ b/Indicators/Indi_AO.mqh
@@ -31,21 +31,16 @@ double iAO(string _symbol, int _tf, int _shift) { return Indi_AO::iAO(_symbol, (
// Structs.
struct AOParams : IndicatorParams {
// Struct constructor.
- void AOParams(int _shift = 0, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- itype = INDI_AO;
+ AOParams(int _shift = 0) : IndicatorParams(INDI_AO, 2, TYPE_DOUBLE) {
#ifdef __MQL4__
max_modes = 1;
-#else
- max_modes = 2;
#endif
- SetDataValueType(TYPE_DOUBLE);
SetDataValueRange(IDATA_RANGE_MIXED);
SetCustomIndicatorName("Examples\\Awesome_Oscillator");
shift = _shift;
- tf = _tf;
};
- void AOParams(AOParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- this = _params;
+ AOParams(AOParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
tf = _tf;
};
};
@@ -53,16 +48,13 @@ struct AOParams : IndicatorParams {
/**
* Implements the Awesome oscillator.
*/
-class Indi_AO : public Indicator {
- protected:
- AOParams params;
-
+class Indi_AO : public Indicator {
public:
/**
* Class constructor.
*/
- Indi_AO(AOParams &_p) : Indicator((IndicatorParams)_p) { params = _p; };
- Indi_AO(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : params(_tf), Indicator(INDI_AO, _tf){};
+ Indi_AO(AOParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src){};
+ Indi_AO(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_AO, _tf){};
/**
* Returns the indicator value.
@@ -72,7 +64,7 @@ class Indi_AO : public Indicator {
* - https://www.mql5.com/en/docs/indicators/iao
*/
static double iAO(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0, int _mode = 0,
- Indicator *_obj = NULL) {
+ IndicatorBase *_obj = NULL) {
#ifdef __MQL4__
// Note: In MQL4 _mode is not supported.
return ::iAO(_symbol, _tf, _shift);
@@ -91,7 +83,7 @@ class Indi_AO : public Indicator {
if (Terminal::IsVisualMode()) {
// To avoid error 4806 (ERR_INDICATOR_DATA_NOT_FOUND),
// we check the number of calculated data only in visual mode.
- int _bars_calc = BarsCalculated(_handle);
+ int _bars_calc = ::BarsCalculated(_handle);
if (GetLastError() > 0) {
return EMPTY_VALUE;
} else if (_bars_calc <= 2) {
@@ -109,18 +101,16 @@ class Indi_AO : public Indicator {
/**
* Returns the indicator's value.
*/
- double GetValue(int _mode = 0, int _shift = 0) {
+ virtual double GetValue(int _mode = 0, int _shift = 0) {
ResetLastError();
double _value = EMPTY_VALUE;
- switch (params.idstype) {
+ switch (iparams.idstype) {
case IDATA_BUILTIN:
istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle;
- _value = Indi_AO::iAO(Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF), _shift, _mode,
- GetPointer(this));
+ _value = Indi_AO::iAO(GetSymbol(), GetTf(), _shift, _mode, THIS_PTR);
break;
case IDATA_ICUSTOM:
- _value = iCustom(istate.handle, Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF),
- params.GetCustomIndicatorName(), _mode, _shift);
+ _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift);
break;
default:
SetUserError(ERR_INVALID_PARAMETER);
@@ -130,29 +120,6 @@ class Indi_AO : public Indicator {
return _value;
}
- /**
- * Returns the indicator's struct value.
- */
- IndicatorDataEntry GetEntry(int _shift = 0) {
- long _bar_time = GetBarTime(_shift);
- unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
- if (idata.KeyExists(_bar_time, _position)) {
- _entry = idata.GetByPos(_position);
- } else {
- _entry.timestamp = GetBarTime(_shift);
- for (int _mode = 0; _mode < (int)params.max_modes; _mode++) {
- _entry.values[_mode] = GetValue(_mode, _shift);
- }
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, _entry.values[0].Get() != EMPTY_VALUE);
- if (_entry.IsValid()) {
- _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType()));
- idata.Add(_entry, _bar_time);
- }
- }
- return _entry;
- }
-
/**
* Returns reusable indicator for a given symbol and time-frame.
*/
@@ -165,6 +132,11 @@ class Indi_AO : public Indicator {
return _ptr;
}
+ /**
+ * Checks if indicator entry values are valid.
+ */
+ virtual bool IsValidEntry(IndicatorDataEntry &_entry) { return _entry.values[0].Get() != EMPTY_VALUE; }
+
/**
* Returns the indicator's entry value.
*/
diff --git a/Indicators/Indi_ASI.mqh b/Indicators/Indi_ASI.mqh
index 25019c8df..9e2fa43b4 100644
--- a/Indicators/Indi_ASI.mqh
+++ b/Indicators/Indi_ASI.mqh
@@ -30,19 +30,15 @@ struct ASIParams : IndicatorParams {
unsigned int period;
double mpc;
// Struct constructor.
- void ASIParams(double _mpc = 300.0, int _shift = 0, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- itype = INDI_ASI;
- max_modes = 1;
- SetDataValueType(TYPE_DOUBLE);
+ ASIParams(double _mpc = 300.0, int _shift = 0)
+ : IndicatorParams(INDI_ASI, 1, TYPE_DOUBLE, PERIOD_CURRENT, IDATA_ONCALCULATE) {
SetDataValueRange(IDATA_RANGE_MIXED);
SetCustomIndicatorName("Examples\\ASI");
- SetDataSourceType(IDATA_BUILTIN);
mpc = _mpc;
shift = _shift;
- tf = _tf;
};
- void ASIParams(ASIParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- this = _params;
+ ASIParams(ASIParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
tf = _tf;
};
};
@@ -50,22 +46,19 @@ struct ASIParams : IndicatorParams {
/**
* Implements the Bill Williams' Accelerator/Decelerator oscillator.
*/
-class Indi_ASI : public Indicator {
- protected:
- ASIParams params;
-
+class Indi_ASI : public Indicator {
public:
/**
* Class constructor.
*/
- Indi_ASI(ASIParams &_params) : params(_params.mpc), Indicator((IndicatorParams)_params) { params = _params; };
- Indi_ASI(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_ASI, _tf) { params.tf = _tf; };
+ Indi_ASI(ASIParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src){};
+ Indi_ASI(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_ASI, _tf){};
/**
* Built-in version of ASI.
*/
static double iASI(string _symbol, ENUM_TIMEFRAMES _tf, double _mpc, int _mode = 0, int _shift = 0,
- Indicator *_obj = NULL) {
+ IndicatorBase *_obj = NULL) {
INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_symbol, _tf, Util::MakeKey("Indi_ASI", _mpc));
return iASIOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mpc, _mode, _shift, _cache);
}
@@ -162,17 +155,21 @@ class Indi_ASI : public Indicator {
/**
* Returns the indicator's value.
*/
- double GetValue(int _mode = 0, int _shift = 0) {
+ virtual double GetValue(int _mode = 0, int _shift = 0) {
ResetLastError();
double _value = EMPTY_VALUE;
- switch (params.idstype) {
- case IDATA_BUILTIN:
- _value = Indi_ASI::iASI(GetSymbol(), GetTf(), /*[*/ GetMaximumPriceChanging() /*]*/, _mode, _shift, THIS_PTR);
- break;
+ switch (iparams.idstype) {
case IDATA_ICUSTOM:
- _value = iCustom(istate.handle, GetSymbol(), GetTf(), params.GetCustomIndicatorName(),
+ _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(),
/*[*/ GetMaximumPriceChanging() /*]*/, 0, _shift);
break;
+ case IDATA_ONCALCULATE: {
+ INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(GetSymbol(), GetTf(),
+ Util::MakeKey("Indi_ASI", GetMaximumPriceChanging()));
+ _value =
+ iASIOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, GetMaximumPriceChanging(), _mode, _shift, _cache);
+ break;
+ }
default:
SetUserError(ERR_INVALID_PARAMETER);
}
@@ -181,29 +178,6 @@ class Indi_ASI : public Indicator {
return _value;
}
- /**
- * Returns the indicator's struct value.
- */
- IndicatorDataEntry GetEntry(int _shift = 0) {
- long _bar_time = GetBarTime(_shift);
- unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
- if (idata.KeyExists(_bar_time, _position)) {
- _entry = idata.GetByPos(_position);
- } else {
- _entry.timestamp = GetBarTime(_shift);
- for (int _mode = 0; _mode < (int)params.max_modes; _mode++) {
- _entry.values[_mode] = GetValue(_mode, _shift);
- }
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, !_entry.HasValue(NULL) && !_entry.HasValue(EMPTY_VALUE));
- if (_entry.IsValid()) {
- _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType()));
- idata.Add(_entry, _bar_time);
- }
- }
- return _entry;
- }
-
/**
* Returns the indicator's entry value.
*/
@@ -218,7 +192,7 @@ class Indi_ASI : public Indicator {
/**
* Get maximum price changing value.
*/
- double GetMaximumPriceChanging() { return params.mpc; }
+ double GetMaximumPriceChanging() { return iparams.mpc; }
/* Setters */
@@ -227,6 +201,6 @@ class Indi_ASI : public Indicator {
*/
void GetMaximumPriceChanging(double _mpc) {
istate.is_changed = true;
- params.mpc = _mpc;
+ iparams.mpc = _mpc;
}
};
diff --git a/Indicators/Indi_ATR.mqh b/Indicators/Indi_ATR.mqh
index e005da486..72e536663 100644
--- a/Indicators/Indi_ATR.mqh
+++ b/Indicators/Indi_ATR.mqh
@@ -34,18 +34,13 @@ double iATR(string _symbol, int _tf, int _period, int _shift) {
struct ATRParams : IndicatorParams {
unsigned int period;
// Struct constructors.
- void ATRParams(unsigned int _period = 14, int _shift = 0, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, string _symbol = NULL)
- : period(_period) {
- itype = INDI_ATR;
- max_modes = 1;
+ ATRParams(unsigned int _period = 14, int _shift = 0) : period(_period), IndicatorParams(INDI_ATR, 1, TYPE_DOUBLE) {
shift = _shift;
- SetDataValueType(TYPE_DOUBLE);
SetDataValueRange(IDATA_RANGE_MIXED);
SetCustomIndicatorName("Examples\\ATR");
- tf = _tf;
};
- void ATRParams(ATRParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- this = _params;
+ ATRParams(ATRParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
tf = _tf;
};
};
@@ -55,15 +50,13 @@ struct ATRParams : IndicatorParams {
*
* Note: It doesn't give independent signals. It is used to define volatility (trend strength).
*/
-class Indi_ATR : public Indicator {
+class Indi_ATR : public Indicator {
public:
- ATRParams params;
-
/**
* Class constructor.
*/
- Indi_ATR(ATRParams &_p) : params(_p.period), Indicator((IndicatorParams)_p) { params = _p; }
- Indi_ATR(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_ATR, _tf) { params.SetTf(_tf); };
+ Indi_ATR(ATRParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src) {}
+ Indi_ATR(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_ATR, _tf){};
/**
* Returns the indicator value.
@@ -73,7 +66,7 @@ class Indi_ATR : public Indicator {
* - https://www.mql5.com/en/docs/indicators/iatr
*/
static double iATR(string _symbol, ENUM_TIMEFRAMES _tf, unsigned int _period, int _shift = 0,
- Indicator *_obj = NULL) {
+ IndicatorBase *_obj = NULL) {
#ifdef __MQL4__
return ::iATR(_symbol, _tf, _period, _shift);
#else // __MQL5__
@@ -109,16 +102,16 @@ class Indi_ATR : public Indicator {
/**
* Returns the indicator's value.
*/
- double GetValue(int _mode = 0, int _shift = 0) {
+ virtual double GetValue(int _mode = 0, int _shift = 0) {
ResetLastError();
double _value = EMPTY_VALUE;
- switch (params.idstype) {
+ switch (iparams.idstype) {
case IDATA_BUILTIN:
istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle;
- _value = Indi_ATR::iATR(GetSymbol(), GetTf(), GetPeriod(), _shift, GetPointer(this));
+ _value = Indi_ATR::iATR(GetSymbol(), GetTf(), GetPeriod(), _shift, THIS_PTR);
break;
case IDATA_ICUSTOM:
- _value = iCustom(istate.handle, GetSymbol(), GetTf(), params.GetCustomIndicatorName(), _mode, _shift);
+ _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift);
break;
default:
SetUserError(ERR_INVALID_PARAMETER);
@@ -128,29 +121,6 @@ class Indi_ATR : public Indicator {
return _value;
}
- /**
- * Returns the indicator's struct value.
- */
- IndicatorDataEntry GetEntry(int _shift = 0) {
- long _bar_time = GetBarTime(_shift);
- unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
- if (idata.KeyExists(_bar_time, _position)) {
- _entry = idata.GetByPos(_position);
- } else {
- _entry.timestamp = GetBarTime(_shift);
- for (int _mode = 0; _mode < (int)params.max_modes; _mode++) {
- _entry.values[_mode] = GetValue(_mode, _shift);
- }
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, !_entry.HasValue(NULL) && !_entry.HasValue(EMPTY_VALUE));
- if (_entry.IsValid()) {
- _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType()));
- idata.Add(_entry, _bar_time);
- }
- }
- return _entry;
- }
-
/**
* Returns the indicator's entry value.
*/
@@ -167,8 +137,8 @@ class Indi_ATR : public Indicator {
Indi_ATR *_ptr;
string _key = Util::MakeKey(_symbol, (int)_tf, _period);
if (!Objects::TryGet(_key, _ptr)) {
- ATRParams _params(_period, _tf);
- _ptr = Objects::Set(_key, new Indi_ATR(_params));
+ ATRParams _p(_period, _tf);
+ _ptr = Objects::Set(_key, new Indi_ATR(_p));
_ptr.SetSymbol(_symbol);
}
return _ptr;
@@ -179,7 +149,7 @@ class Indi_ATR : public Indicator {
/**
* Get period value.
*/
- unsigned int GetPeriod() { return params.period; }
+ unsigned int GetPeriod() { return iparams.period; }
/* Setters */
@@ -188,6 +158,6 @@ class Indi_ATR : public Indicator {
*/
void SetPeriod(unsigned int _period) {
istate.is_changed = true;
- params.period = _period;
+ iparams.period = _period;
}
};
diff --git a/Indicators/Indi_Alligator.mqh b/Indicators/Indi_Alligator.mqh
index cb7877526..9784d0876 100644
--- a/Indicators/Indi_Alligator.mqh
+++ b/Indicators/Indi_Alligator.mqh
@@ -70,8 +70,8 @@ struct AlligatorParams : IndicatorParams {
ENUM_MA_METHOD ma_method; // Averaging method.
ENUM_APPLIED_PRICE applied_price; // Applied price.
// Struct constructors.
- void AlligatorParams(int _jp = 13, int _js = 8, int _tp = 8, int _ts = 5, int _lp = 5, int _ls = 3,
- ENUM_MA_METHOD _mm = MODE_SMMA, ENUM_APPLIED_PRICE _ap = PRICE_MEDIAN, int _shift = 0)
+ AlligatorParams(int _jp = 13, int _js = 8, int _tp = 8, int _ts = 5, int _lp = 5, int _ls = 3,
+ ENUM_MA_METHOD _mm = MODE_SMMA, ENUM_APPLIED_PRICE _ap = PRICE_MEDIAN, int _shift = 0)
: jaw_period(_jp),
jaw_shift(_js),
teeth_period(_tp),
@@ -79,17 +79,14 @@ struct AlligatorParams : IndicatorParams {
lips_period(_lp),
lips_shift(_ls),
ma_method(_mm),
- applied_price(_ap) {
- itype = INDI_ALLIGATOR;
- max_modes = FINAL_ALLIGATOR_LINE_ENTRY;
+ applied_price(_ap),
+ IndicatorParams(INDI_ALLIGATOR, FINAL_ALLIGATOR_LINE_ENTRY, TYPE_DOUBLE) {
shift = _shift;
- SetDataValueType(TYPE_DOUBLE);
SetDataValueRange(IDATA_RANGE_PRICE);
- SetDataSourceType(IDATA_BUILTIN);
SetCustomIndicatorName("Examples\\Alligator");
};
- void AlligatorParams(AlligatorParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) {
- this = _params;
+ AlligatorParams(AlligatorParams &_params, ENUM_TIMEFRAMES _tf) {
+ THIS_REF = _params;
tf = _tf;
};
};
@@ -97,15 +94,12 @@ struct AlligatorParams : IndicatorParams {
/**
* Implements the Alligator indicator.
*/
-class Indi_Alligator : public Indicator {
+class Indi_Alligator : public Indicator {
public:
- AlligatorParams params;
-
/**
* Class constructor.
*/
- Indi_Alligator(AlligatorParams &_p) : Indicator((IndicatorParams)_p) { params = _p; }
- Indi_Alligator(AlligatorParams &_p, ENUM_TIMEFRAMES _tf) : Indicator(INDI_ALLIGATOR, _tf) { params = _p; }
+ Indi_Alligator(AlligatorParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src) {}
Indi_Alligator(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : Indicator(INDI_ADX, _tf){};
/**
@@ -129,7 +123,7 @@ class Indi_Alligator : public Indicator {
static double iAlligator(string _symbol, ENUM_TIMEFRAMES _tf, int _jaw_period, int _jaw_shift, int _teeth_period,
int _teeth_shift, int _lips_period, int _lips_shift, ENUM_MA_METHOD _ma_method,
ENUM_APPLIED_PRICE _applied_price, ENUM_ALLIGATOR_LINE _mode, int _shift = 0,
- Indicator *_obj = NULL) {
+ IndicatorBase *_obj = NULL) {
#ifdef __MQL4__
return ::iAlligator(_symbol, _tf, _jaw_period, _jaw_shift, _teeth_period, _teeth_shift, _lips_period, _lips_shift,
_ma_method, _applied_price, _mode, _shift);
@@ -167,7 +161,7 @@ class Indi_Alligator : public Indicator {
/**
* Returns the indicator's value.
*/
- double GetValue(ENUM_ALLIGATOR_LINE _mode, int _shift = 0) {
+ virtual double GetValue(int _mode, int _shift = 0) {
#ifdef __MQL4__
if (_mode == 0) {
// In MQL4 mode 0 should be treated as mode 1 as Alligator buffers starts from index 1.
@@ -176,17 +170,15 @@ class Indi_Alligator : public Indicator {
#endif
ResetLastError();
double _value = EMPTY_VALUE;
- switch (params.idstype) {
+ switch (iparams.idstype) {
case IDATA_BUILTIN:
istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle;
- _value = Indi_Alligator::iAlligator(Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF),
- GetJawPeriod(), GetJawShift(), GetTeethPeriod(), GetTeethShift(),
- GetLipsPeriod(), GetLipsShift(), GetMAMethod(), GetAppliedPrice(), _mode,
- _shift, GetPointer(this));
+ _value = Indi_Alligator::iAlligator(GetSymbol(), GetTf(), GetJawPeriod(), GetJawShift(), GetTeethPeriod(),
+ GetTeethShift(), GetLipsPeriod(), GetLipsShift(), GetMAMethod(),
+ GetAppliedPrice(), (ENUM_ALLIGATOR_LINE)_mode, _shift, THIS_PTR);
break;
case IDATA_ICUSTOM:
- _value = iCustom(istate.handle, Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF),
- params.GetCustomIndicatorName(), /*[*/
+ _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/
GetJawPeriod(), GetJawShift(), GetTeethPeriod(), GetTeethShift(), GetLipsPeriod(),
GetLipsShift(), GetMAMethod(),
GetAppliedPrice()
@@ -207,18 +199,17 @@ class Indi_Alligator : public Indicator {
IndicatorDataEntry GetEntry(int _shift = 0) {
long _bar_time = GetBarTime(_shift);
unsigned int _position;
- IndicatorDataEntry _entry(params.max_modes);
+ IndicatorDataEntry _entry(iparams.GetMaxModes());
if (idata.KeyExists(_bar_time, _position)) {
_entry = idata.GetByPos(_position);
} else {
_entry.timestamp = GetBarTime(_shift);
- for (int _mode = 0; _mode < (int)params.max_modes; _mode++) {
+ for (int _mode = 0; _mode < (int)iparams.GetMaxModes(); _mode++) {
_entry.values[_mode] = GetValue((ENUM_ALLIGATOR_LINE)_mode, _shift);
}
- _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID,
- !_entry.HasValue(NULL) && !_entry.HasValue(EMPTY_VALUE) && _entry.IsGt(0));
+ _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, IsValidEntry(_entry));
if (_entry.IsValid()) {
- _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType()));
+ _entry.AddFlags(_entry.GetDataTypeFlag(iparams.GetDataValueType()));
idata.Add(_entry, _bar_time);
}
}
@@ -238,47 +229,54 @@ class Indi_Alligator : public Indicator {
return _param;
}
+ /**
+ * Checks if indicator entry values are valid.
+ */
+ virtual bool IsValidEntry(IndicatorDataEntry &_entry) {
+ return !_entry.HasValue(NULL) && !_entry.HasValue