diff --git a/.github/workflows/test-indicator.yml b/.github/workflows/test-indicator.yml index aad5bdd57..812437b2e 100644 --- a/.github/workflows/test-indicator.yml +++ b/.github/workflows/test-indicator.yml @@ -35,9 +35,13 @@ jobs: strategy: matrix: test: + - Indicator.test + - IndicatorBase.test - IndicatorCandle.test + - IndicatorData.test - IndicatorTf.test - IndicatorTick.test + - IndicatorRenko.test steps: - uses: actions/download-artifact@v2 with: diff --git a/.github/workflows/test-indicators.yml b/.github/workflows/test-indicators.yml index c6e2cd69e..47782f989 100644 --- a/.github/workflows/test-indicators.yml +++ b/.github/workflows/test-indicators.yml @@ -57,7 +57,6 @@ jobs: - Indi_ColorCandlesDaily.test - Indi_ColorLine.test - Indi_CustomMovingAverage.test - - Indi_DEMA.test - Indi_DeMarker.test - Indi_Demo.test - Indi_DetrendedPrice.test @@ -101,6 +100,8 @@ jobs: - Indi_WilliamsAD.test - Indi_ZigZag.test - Indi_ZigZagColor.test + # Requires refactoring: + # - Indi_DEMA.test steps: - uses: actions/download-artifact@v2 with: diff --git a/.github/workflows/test-serializer.yml b/.github/workflows/test-serializer.yml new file mode 100644 index 000000000..07bccab3c --- /dev/null +++ b/.github/workflows/test-serializer.yml @@ -0,0 +1,49 @@ +--- +name: Test Serializer + +# yamllint disable-line rule:truthy +on: + pull_request: + paths: + - 'Serializer/**' + - '.github/workflows/test-serializer.yml' + push: + paths: + - 'Serializer/**' + - '.github/workflows/test-serializer.yml' + +jobs: + + compile: + name: Compile + uses: ./.github/workflows/compile.yml + with: + artifact_prefix: mt + path: Exchange + skip_cleanup: true + + Serializer-Tests-MQL4: + defaults: + run: + shell: bash + working-directory: serializer/tests + needs: compile + runs-on: ubuntu-latest + strategy: + matrix: + test: + - Serializer.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: 2021 + MtVersion: 4.0.0.1359 + RunOnError: show_logs 200 + TestExpert: ${{ matrix.test }} + timeout-minutes: 10 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 344aead84..ff0153775 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,7 +46,6 @@ jobs: - DatabaseTest # - DrawIndicatorTest - EATest - - IndicatorTest - IndicatorsTest - MailTest - MarketTest @@ -98,7 +97,6 @@ jobs: - OrderQuery - ProfilerTest - RefsTest - - SerializerTest - TerminalTest - TimerTest - ValueStorageTest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b73b0ad64..f9e1999f3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,5 @@ --- +exclude: '\.md$' repos: - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/3D/Chart3D.h b/3D/Chart3D.h index 79182d6ce..692c60f71 100644 --- a/3D/Chart3D.h +++ b/3D/Chart3D.h @@ -26,12 +26,12 @@ */ #include "../Bar.struct.h" -#include "../IndicatorData.mqh" +#include "../Indicator/IndicatorData.h" #include "../Indicators/Indi_MA.mqh" #include "../Instances.h" #include "../Refs.mqh" -#include "../SerializerConverter.mqh" -#include "../SerializerJson.mqh" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerJson.h" #include "Chart3DCandles.h" #include "Chart3DType.h" #include "Cube.h" diff --git a/Account/Account.h b/Account/Account.h index c3410a92c..adc51adac 100644 --- a/Account/Account.h +++ b/Account/Account.h @@ -26,7 +26,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Serializer.mqh" +#include "../Serializer/Serializer.h" #include "AccountBase.h" /** diff --git a/Account/Account.struct.h b/Account/Account.struct.h index 601a9c787..eaa8d466c 100644 --- a/Account/Account.struct.h +++ b/Account/Account.struct.h @@ -35,7 +35,7 @@ class Serializer; // Includes. -#include "../Serializer.mqh" +#include "../Serializer/Serializer.h" #include "../Terminal.define.h" // Struct for account entries. diff --git a/Account/AccountBase.h b/Account/AccountBase.h index 359d4c0ed..4ee21ddf1 100644 --- a/Account/AccountBase.h +++ b/Account/AccountBase.h @@ -25,7 +25,6 @@ #define ACCOUNTBASE_H // Includes. -//#include "../Serializer.mqh" #include "../Refs.mqh" #include "AccountBase.struct.h" diff --git a/Account/AccountBase.struct.h b/Account/AccountBase.struct.h index ca60b056d..14d0773b4 100644 --- a/Account/AccountBase.struct.h +++ b/Account/AccountBase.struct.h @@ -35,7 +35,7 @@ class Serializer; // Includes. -#include "../Serializer.mqh" +#include "../Serializer/Serializer.h" #include "../Terminal.define.h" // Struct for account entries. diff --git a/Account/AccountForex.h b/Account/AccountForex.h index 6dccd7c21..98b65c03f 100644 --- a/Account/AccountForex.h +++ b/Account/AccountForex.h @@ -25,7 +25,7 @@ #define ACCOUNTFOREX_H // Includes. -#include "../Serializer.mqh" +#include "../Serializer/Serializer.h" #include "Account.h" #include "AccountForex.struct.h" diff --git a/Account/AccountForex.struct.h b/Account/AccountForex.struct.h index 85394f650..30575505c 100644 --- a/Account/AccountForex.struct.h +++ b/Account/AccountForex.struct.h @@ -35,7 +35,7 @@ class Serializer; // Includes. -#include "../Serializer.mqh" +#include "../Serializer/Serializer.h" #include "../Terminal.define.h" // Struct for account entries. diff --git a/Account/AccountMt.h b/Account/AccountMt.h index 651f0981a..a444f1084 100644 --- a/Account/AccountMt.h +++ b/Account/AccountMt.h @@ -30,13 +30,12 @@ class AccountMt; // Includes. #include "../Array.mqh" #include "../BufferStruct.mqh" -#include "../Chart.mqh" #include "../Convert.mqh" #include "../Data.struct.h" -#include "../Indicator.struct.h" +#include "../Indicator/Indicator.struct.h" #include "../Order.struct.h" #include "../Orders.mqh" -#include "../Serializer.mqh" +#include "../Serializer/Serializer.h" #include "../SymbolInfo.mqh" #include "../Trade.struct.h" #include "Account.define.h" diff --git a/Array.mqh b/Array.mqh index bfb0a0392..a0eeeac1e 100644 --- a/Array.mqh +++ b/Array.mqh @@ -71,101 +71,101 @@ class Array { */ template static T Sum(ARRAY_REF(T, _arr)) { - int i; - int _size = ArraySize(_arr); - if (_size > 0) { - T _sum = _arr[0]; - for (i = 1; i < _size; i++) { - _sum += _arr[i]; + int i; + int _size = ArraySize(_arr); + if (_size > 0) { + T _sum = _arr[0]; + for (i = 1; i < _size; i++) { + _sum += _arr[i]; + } + return _sum; + } else { + return 0; } - return _sum; - } else { - return 0; } -} -/** - * Finds the highest value in the array of any numeric type. - */ -template -static T Max(ARRAY_REF(T, _arr)) { - int i; - int _size = ArraySize(_arr); - if (_size > 0) { - T _max = _arr[0]; - for (i = 1; i < _size; i++) { - _max = _max < _arr[i] ? _arr[i] : _max; + /** + * Finds the highest value in the array of any numeric type. + */ + template + static T Max(ARRAY_REF(T, _arr)) { + int i; + int _size = ArraySize(_arr); + if (_size > 0) { + T _max = _arr[0]; + for (i = 1; i < _size; i++) { + _max = _max < _arr[i] ? _arr[i] : _max; + } + return _max; + } else { + return 0; } - return _max; - } else { - return 0; } -} -template -static int ArrayCopy(ARRAY_REF(T, dst_array), const ARRAY_REF(T, src_array), const int dst_start = 0, - const int src_start = 0, const int count = WHOLE_ARRAY) { - throw NotImplementedException(); -} + template + static int ArrayCopy(ARRAY_REF(T, dst_array), const ARRAY_REF(T, src_array), const int dst_start = 0, + const int src_start = 0, const int count = WHOLE_ARRAY) { + throw NotImplementedException(); + } -/** - * Return plain text of array values separated by the delimiter. - * - * @param - * int arr[] - array to look for the values - * string sep - delimiter to separate array values - */ -static string GetArrayValues(ARRAY_REF(int, arr), string sep = ", ") { - int i; - string result = ""; - for (i = 0; i < ArraySize(arr); i++) { - result += StringFormat("%d:%d%s", i, arr[i], sep); - } - // Return text without last separator. - return StringSubstr(result, 0, StringLen(result) - StringLen(sep)); -} + /** + * Return plain text of array values separated by the delimiter. + * + * @param + * int arr[] - array to look for the values + * string sep - delimiter to separate array values + */ + static string GetArrayValues(ARRAY_REF(int, arr), string sep = ", ") { + int i; + string result = ""; + for (i = 0; i < ArraySize(arr); i++) { + result += StringFormat("%d:%d%s", i, arr[i], sep); + } + // Return text without last separator. + return StringSubstr(result, 0, StringLen(result) - StringLen(sep)); + } -/** - * Return plain text of array values separated by the delimiter. - * - * @param - * double arr[] - array to look for the values - * string sep - delimiter to separate array values - */ -static string GetArrayValues(ARRAY_REF(double, arr), string sep = ", ") { - int i; - string result = ""; - for (i = 0; i < ArraySize(arr); i++) { - result += StringFormat("%d:%g%s", i, arr[i], sep); - } - // Return text without last separator. - return StringSubstr(result, 0, StringLen(result) - StringLen(sep)); -} + /** + * Return plain text of array values separated by the delimiter. + * + * @param + * double arr[] - array to look for the values + * string sep - delimiter to separate array values + */ + static string GetArrayValues(ARRAY_REF(double, arr), string sep = ", ") { + int i; + string result = ""; + for (i = 0; i < ArraySize(arr); i++) { + result += StringFormat("%d:%g%s", i, arr[i], sep); + } + // Return text without last separator. + return StringSubstr(result, 0, StringLen(result) - StringLen(sep)); + } -/** - * Find lower value within the 1-dim array of floats. - */ -static double LowestArrValue(ARRAY_REF(double, arr)) { return (arr[ArrayMinimum(arr)]); } + /** + * Find lower value within the 1-dim array of floats. + */ + static double LowestArrValue(ARRAY_REF(double, arr)) { return (arr[ArrayMinimum(arr)]); } -/** - * Find higher value within the 1-dim array of floats. - */ -static double HighestArrValue(ARRAY_REF(double, arr)) { return (arr[ArrayMaximum(arr)]); } + /** + * Find higher value within the 1-dim array of floats. + */ + static double HighestArrValue(ARRAY_REF(double, arr)) { return (arr[ArrayMaximum(arr)]); } /** * Find lower value within the 2-dim array of floats by the key. */ #ifdef __MQL4__ -static double LowestArrValue2(double& arr[][], int key1) { - int i; - double lowest = 999; - for (i = 0; i < ArrayRange(arr, 1); i++) { - if (arr[key1][i] < lowest) { - lowest = arr[key1][i]; + static double LowestArrValue2(double& arr[][], int key1) { + int i; + double lowest = 999; + for (i = 0; i < ArrayRange(arr, 1); i++) { + if (arr[key1][i] < lowest) { + lowest = arr[key1][i]; + } } + return lowest; } - return lowest; -} #else // @todo #endif @@ -174,16 +174,16 @@ static double LowestArrValue2(double& arr[][], int key1) { * Find higher value within the 2-dim array of floats by the key. */ #ifdef __MQL4__ -static double HighestArrValue2(double& arr[][], int key1) { - double highest = -1; - int i; - for (i = 0; i < ArrayRange(arr, 1); i++) { - if (arr[key1][i] > highest) { - highest = arr[key1][i]; + static double HighestArrValue2(double& arr[][], int key1) { + double highest = -1; + int i; + for (i = 0; i < ArrayRange(arr, 1); i++) { + if (arr[key1][i] > highest) { + highest = arr[key1][i]; + } } + return highest; } - return highest; -} #else // @todo #endif @@ -192,16 +192,16 @@ static double HighestArrValue2(double& arr[][], int key1) { * Find highest value in 2-dim array of integers by the key. */ #ifdef __MQL4__ -static int HighestValueByKey(int& arr[][], int key) { - int highest = -1; - int i; - for (i = 0; i < ArrayRange(arr, 1); i++) { - if (arr[key][i] > highest) { - highest = arr[key][i]; + static int HighestValueByKey(int& arr[][], int key) { + int highest = -1; + int i; + for (i = 0; i < ArrayRange(arr, 1); i++) { + if (arr[key][i] > highest) { + highest = arr[key][i]; + } } + return highest; } - return highest; -} #else // @todo #endif @@ -210,16 +210,16 @@ static int HighestValueByKey(int& arr[][], int key) { * Find lowest value in 2-dim array of integers by the key. */ #ifdef __MQL4__ -static int LowestValueByKey(int& arr[][], int key) { - int i; - int lowest = 999; - for (i = 0; i < ArrayRange(arr, 1); i++) { - if (arr[key][i] < lowest) { - lowest = arr[key][i]; + static int LowestValueByKey(int& arr[][], int key) { + int i; + int lowest = 999; + for (i = 0; i < ArrayRange(arr, 1); i++) { + if (arr[key][i] < lowest) { + lowest = arr[key][i]; + } } + return lowest; } - return lowest; -} #else // @todo #endif @@ -247,18 +247,18 @@ static int GetLowestArrDoubleValue(double& arr[][], int key) { * Find key in array of integers with the highest value. */ #ifdef __MQL4__ -static int GetArrKey1ByHighestKey2Value(int& arr[][], int key2) { - int i; - int key1 = EMPTY; - int highest = 0; - for (i = 0; i < ArrayRange(arr, 0); i++) { - if (arr[i][key2] > highest) { - highest = arr[i][key2]; - key1 = i; + static int GetArrKey1ByHighestKey2Value(int& arr[][], int key2) { + int i; + int key1 = EMPTY; + int highest = 0; + for (i = 0; i < ArrayRange(arr, 0); i++) { + if (arr[i][key2] > highest) { + highest = arr[i][key2]; + key1 = i; + } } + return key1; } - return key1; -} #else // @todo #endif @@ -267,18 +267,18 @@ static int GetArrKey1ByHighestKey2Value(int& arr[][], int key2) { * Find key in array of integers with the lowest value. */ #ifdef __MQL4__ -static int GetArrKey1ByLowestKey2Value(int& arr[][], int key2) { - int i; - int key1 = EMPTY; - int lowest = 999; - for (i = 0; i < ArrayRange(arr, 0); i++) { - if (arr[i][key2] < lowest) { - lowest = arr[i][key2]; - key1 = i; + static int GetArrKey1ByLowestKey2Value(int& arr[][], int key2) { + int i; + int key1 = EMPTY; + int lowest = 999; + for (i = 0; i < ArrayRange(arr, 0); i++) { + if (arr[i][key2] < lowest) { + lowest = arr[i][key2]; + key1 = i; + } } + return key1; } - return key1; -} #else // @todo #endif @@ -287,18 +287,18 @@ static int GetArrKey1ByLowestKey2Value(int& arr[][], int key2) { * Find key in array of doubles with the highest value. */ #ifdef __MQL4__ -static int GetArrKey1ByHighestKey2ValueD(double& arr[][], int key2) { - int i; - int key1 = EMPTY; - double highest = -1; - for (i = 0; i < ArrayRange(arr, 0); i++) { - if (arr[i][key2] > highest) { - highest = arr[i][key2]; - key1 = i; + static int GetArrKey1ByHighestKey2ValueD(double& arr[][], int key2) { + int i; + int key1 = EMPTY; + double highest = -1; + for (i = 0; i < ArrayRange(arr, 0); i++) { + if (arr[i][key2] > highest) { + highest = arr[i][key2]; + key1 = i; + } } + return key1; } - return key1; -} #else // @todo #endif @@ -307,18 +307,18 @@ static int GetArrKey1ByHighestKey2ValueD(double& arr[][], int key2) { * Find key in array of doubles with the lowest value. */ #ifdef __MQL4__ -static int GetArrKey1ByLowestKey2ValueD(double& arr[][], int key2) { - int i; - int key1 = EMPTY; - double lowest = 999; - for (i = 0; i < ArrayRange(arr, 0); i++) { - if (arr[i][key2] < lowest) { - lowest = arr[i][key2]; - key1 = i; + static int GetArrKey1ByLowestKey2ValueD(double& arr[][], int key2) { + int i; + int key1 = EMPTY; + double lowest = 999; + for (i = 0; i < ArrayRange(arr, 0); i++) { + if (arr[i][key2] < lowest) { + lowest = arr[i][key2]; + key1 = i; + } } + return key1; } - return key1; -} #else // @todo #endif @@ -327,12 +327,12 @@ static int GetArrKey1ByLowestKey2ValueD(double& arr[][], int key2) { * Set array value for double items with specific keys. */ #ifdef __MQL4__ -static void ArrSetValueD(double& arr[][], int key, double value) { - int i; - for (i = 0; i < ArrayRange(arr, 0); i++) { - arr[i][key] = value; + static void ArrSetValueD(double& arr[][], int key, double value) { + int i; + for (i = 0; i < ArrayRange(arr, 0); i++) { + arr[i][key] = value; + } } -} #else // @todo #endif @@ -341,12 +341,12 @@ static void ArrSetValueD(double& arr[][], int key, double value) { * Set array value for integer items with specific keys. */ #ifdef __MQL4__ -static void ArrSetValueI(int& arr[][], int key, int value) { - int i; - for (i = 0; i < ArrayRange(arr, 0); i++) { - arr[i][key] = value; + static void ArrSetValueI(int& arr[][], int key, int value) { + int i; + for (i = 0; i < ArrayRange(arr, 0); i++) { + arr[i][key] = value; + } } -} #else // @todo #endif @@ -355,80 +355,80 @@ static void ArrSetValueI(int& arr[][], int key, int value) { * Calculate sum of 2 dimentional array based on given key. */ #ifdef __MQL4__ -static double GetArrSumKey1(double& arr[][], int key1, int offset = 0) { - int i; - double sum = 0; - offset = MathMin(offset, ArrayRange(arr, 1) - 1); - for (i = offset; i < ArrayRange(arr, 1); i++) { - sum += arr[key1][i]; - } - return sum; -} + static double GetArrSumKey1(double& arr[][], int key1, int offset = 0) { + int i; + double sum = 0; + offset = MathMin(offset, ArrayRange(arr, 1) - 1); + for (i = offset; i < ArrayRange(arr, 1); i++) { + sum += arr[key1][i]; + } + return sum; + } #else // @todo #endif -/** - * Print a one-dimensional array. - * - * @param int arr - * The one dimensional array of integers. - * @param string dlm - * Delimiter to separate the items. - * - * @return string - * String representation of array. - */ -static string ArrToString(ARRAY_REF(int, arr), string dlm = ",") { - int i; - string res = ""; - for (i = 0; i < ArraySize(arr); i++) { - res += IntegerToString(arr[i]) + dlm; - } - res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); - return res; -} + /** + * Print a one-dimensional array. + * + * @param int arr + * The one dimensional array of integers. + * @param string dlm + * Delimiter to separate the items. + * + * @return string + * String representation of array. + */ + static string ArrToString(ARRAY_REF(int, arr), string dlm = ",") { + int i; + string res = ""; + for (i = 0; i < ArraySize(arr); i++) { + res += IntegerToString(arr[i]) + dlm; + } + res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); + return res; + } -/** - * Print a one-dimensional array. - * - * @param double arr - * The one dimensional array of doubles. - * @param string dlm - * Delimiter to separate the items. - * - * @return string - * String representation of array. - */ -static string ArrToString(ARRAY_REF(double, arr), string dlm = ",", int digits = 2) { - int i; - string res = ""; - for (i = 0; i < ArraySize(arr); i++) { - res += StringFormat("%g%s", NormalizeDouble(arr[i], digits), dlm); - } - res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); - return res; -} + /** + * Print a one-dimensional array. + * + * @param double arr + * The one dimensional array of doubles. + * @param string dlm + * Delimiter to separate the items. + * + * @return string + * String representation of array. + */ + static string ArrToString(ARRAY_REF(double, arr), string dlm = ",", int digits = 2) { + int i; + string res = ""; + for (i = 0; i < ArraySize(arr); i++) { + res += StringFormat("%g%s", NormalizeDouble(arr[i], digits), dlm); + } + res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); + return res; + } -/** - * Print a one-dimensional array in hex format. - * - * @param double unsigned char[] - * The one dimensional array of characters. - * @param int count - * If specified, limit the number of printed characters. - * - * @return string - * String representation of array in hexadecimal format. - */ -static string ArrToHex(ARRAY_REF(unsigned char, arr), int count = -1) { - int i; - string res; - for (i = 0; i < (count > 0 ? count : ArraySize(arr)); i++) { - res += StringFormat("%.2X", arr[i]); + /** + * Print a one-dimensional array in hex format. + * + * @param double unsigned char[] + * The one dimensional array of characters. + * @param int count + * If specified, limit the number of printed characters. + * + * @return string + * String representation of array in hexadecimal format. + */ + static string ArrToHex(ARRAY_REF(unsigned char, arr), int count = -1) { + int i; + string res; + for (i = 0; i < (count > 0 ? count : ArraySize(arr)); i++) { + res += StringFormat("%.2X", arr[i]); + } + return res; } - return res; -} /** * Print a two-dimensional array. @@ -444,20 +444,20 @@ static string ArrToHex(ARRAY_REF(unsigned char, arr), int count = -1) { * String representation of array. */ #ifdef __MQL4__ -static string ArrToString2D(double& arr[][], string dlm = ",", int digits = 2) { - string res = ""; - int i, j; - for (i = 0; i < ArrayRange(arr, 0); i++) { - res += "["; - for (j = 0; j < ArrayRange(arr, 1); j++) { - res += StringFormat("%g%s", NormalizeDouble(arr[i][j], digits), dlm); + static string ArrToString2D(double& arr[][], string dlm = ",", int digits = 2) { + string res = ""; + int i, j; + for (i = 0; i < ArrayRange(arr, 0); i++) { + res += "["; + for (j = 0; j < ArrayRange(arr, 1); j++) { + res += StringFormat("%g%s", NormalizeDouble(arr[i][j], digits), dlm); + } + res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); + res += "]" + dlm; } res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); - res += "]" + dlm; + return res; } - res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); - return res; -} #else // @todo #endif @@ -476,71 +476,71 @@ static string ArrToString2D(double& arr[][], string dlm = ",", int digits = 2) { * String representation of array. */ #ifdef __MQL4__ -static string ArrToString3D(double& arr[][][], string dlm = ",", int digits = 2) { - string res = ""; - int i, j, k; - for (i = 0; i < ArrayRange(arr, 0); i++) { - res += "["; - for (j = 0; j < ArrayRange(arr, 1); j++) { + static string ArrToString3D(double& arr[][][], string dlm = ",", int digits = 2) { + string res = ""; + int i, j, k; + for (i = 0; i < ArrayRange(arr, 0); i++) { res += "["; - for (k = 0; k < ArrayRange(arr, 2); k++) { - res += StringFormat("%g%s", NormalizeDouble(arr[i][j][k], digits), dlm); + for (j = 0; j < ArrayRange(arr, 1); j++) { + res += "["; + for (k = 0; k < ArrayRange(arr, 2); k++) { + res += StringFormat("%g%s", NormalizeDouble(arr[i][j][k], digits), dlm); + } + res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); + res += "]" + dlm; } res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); res += "]" + dlm; } res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); - res += "]" + dlm; + return res; } - res = StringSubstr(res, 0, StringLen(res) - StringLen(dlm)); - return res; -} #else // @todo #endif -/** - * Print a one-dimensional array. - * - * @param string arr - * The one dimensional array of strings. - * @param string dlm - * Delimiter to separate the items. - * @param string prefix - * Prefix to add if array is non-empty. - * @param string suffix - * Suffix to add if array is non-empty. - * - * @return string - * String representation of array. - */ -static string ArrToString(ARRAY_REF(string, arr), string dlm = ",", string prefix = "", string suffix = "") { - int i; - string output = ""; - if (ArraySize(arr) > 0) output += prefix; - for (i = 0; i < ArraySize(arr); i++) { - output += (string)arr[i] + dlm; - } - output = StringSubstr(output, 0, StringLen(output) - StringLen(dlm)); - if (ArraySize(arr) > 0) output += suffix; - return output; -} + /** + * Print a one-dimensional array. + * + * @param string arr + * The one dimensional array of strings. + * @param string dlm + * Delimiter to separate the items. + * @param string prefix + * Prefix to add if array is non-empty. + * @param string suffix + * Suffix to add if array is non-empty. + * + * @return string + * String representation of array. + */ + static string ArrToString(ARRAY_REF(string, arr), string dlm = ",", string prefix = "", string suffix = "") { + int i; + string output = ""; + if (ArraySize(arr) > 0) output += prefix; + for (i = 0; i < ArraySize(arr); i++) { + output += (string)arr[i] + dlm; + } + output = StringSubstr(output, 0, StringLen(output) - StringLen(dlm)); + if (ArraySize(arr) > 0) output += suffix; + return output; + } -/** - * Prints an array of a simple type. - * - * @docs: - * - https://www.mql5.com/en/docs/array/arrayprint - */ -template -void ArrayPrint(ARRAY_REF(T, _arr), // Printed array. + /** + * Prints an array of a simple type. + * + * @docs: + * - https://www.mql5.com/en/docs/array/arrayprint + */ + template + void ArrayPrint(ARRAY_REF(T, _arr), // Printed array. int _digits = 0, // Number of decimal places. - const string _dlm = NULL, // Separator of the structure field values. - long _start = 0, // First printed element index. - long _count = WHOLE_ARRAY, // Number of printed elements. - long _flags = NULL) { + const string _dlm = NULL, // Separator of the structure field values. + long _start = 0, // First printed element index. + long _count = WHOLE_ARRAY, // Number of printed elements. + long _flags = NULL) { #ifdef __MQL5__ - ::ArrayPrint(_arr, _digits, _dlm, _start, _count, _flags); + ::ArrayPrint(_arr, _digits, _dlm, _start, _count, _flags); #else int i; string output = ""; @@ -549,54 +549,54 @@ void ArrayPrint(ARRAY_REF(T, _arr), // Printed array. } Print(output); #endif -} + } -/** - * Resize array from the left. - * - * @param string arr - * The one dimensional array of doubles. - * @param int _new_size - * New size of array. - * - * @return bool - * Returns count of all elements contained in the array after resizing, - * otherwise returns -1 without resizing array. - * - * @see: http://www.forexfactory.com/showthread.php?p=2878455#post2878455 - */ -static int ArrayResizeLeft(ARRAY_REF(double, arr), int _new_size, int _reserve_size = 0) { - ArraySetAsSeries(arr, true); - int _res = ArrayResize(arr, _new_size, _reserve_size); - ArraySetAsSeries(arr, false); - return _res; -} + /** + * Resize array from the left. + * + * @param string arr + * The one dimensional array of doubles. + * @param int _new_size + * New size of array. + * + * @return bool + * Returns count of all elements contained in the array after resizing, + * otherwise returns -1 without resizing array. + * + * @see: http://www.forexfactory.com/showthread.php?p=2878455#post2878455 + */ + static int ArrayResizeLeft(ARRAY_REF(double, arr), int _new_size, int _reserve_size = 0) { + ArraySetAsSeries(arr, true); + int _res = ArrayResize(arr, _new_size, _reserve_size); + ArraySetAsSeries(arr, false); + return _res; + } -/** - * Sorts numeric arrays by first dimension. - * - * @param &array[] arr - * Numeric array for sorting. - * @param int count - * Count of elements to sort. By default, it sorts the whole array (WHOLE_ARRAY). - * @param int start - * Starting index to sort. By default, the sort starts at the first element. - * @param int direction - * Sort direction. It can be any of the following values: MODE_ASCEND or MODE_DESCEND. - * - * @return bool - * The function returns true on success, otherwise false. - * - * @docs: - * - https://docs.mql4.com/array/arraysort - * - https://www.mql5.com/en/docs/array/arraysort - * - https://www.mql5.com/en/docs/array/array_reverse - */ -// One dimensional array. -template -static bool ArraySort(ARRAY_REF(T, arr), int count = WHOLE_ARRAY, int start = 0, int direction = MODE_ASCEND) { + /** + * Sorts numeric arrays by first dimension. + * + * @param &array[] arr + * Numeric array for sorting. + * @param int count + * Count of elements to sort. By default, it sorts the whole array (WHOLE_ARRAY). + * @param int start + * Starting index to sort. By default, the sort starts at the first element. + * @param int direction + * Sort direction. It can be any of the following values: MODE_ASCEND or MODE_DESCEND. + * + * @return bool + * The function returns true on success, otherwise false. + * + * @docs: + * - https://docs.mql4.com/array/arraysort + * - https://www.mql5.com/en/docs/array/arraysort + * - https://www.mql5.com/en/docs/array/array_reverse + */ + // One dimensional array. + template + static bool ArraySort(ARRAY_REF(T, arr), int count = WHOLE_ARRAY, int start = 0, int direction = MODE_ASCEND) { #ifdef __MQL4__ - return ::ArraySort(arr, count, start, direction); + return ::ArraySort(arr, count, start, direction); #else if (direction == MODE_DESCEND) { return ::ArrayReverse(arr, start, count); @@ -605,55 +605,55 @@ static bool ArraySort(ARRAY_REF(T, arr), int count = WHOLE_ARRAY, int start = 0, return ::ArraySort(arr); } #endif -} + } // Two dimensional array. #ifdef __MQL4__ -template -static bool ArraySort2D(T& arr[][], int count = WHOLE_ARRAY, int start = 0, int direction = MODE_ASCEND) { + template + static bool ArraySort2D(T& arr[][], int count = WHOLE_ARRAY, int start = 0, int direction = MODE_ASCEND) { #ifdef __MQL4__ - return (bool)::ArraySort(arr, count, start, direction); + return (bool)::ArraySort(arr, count, start, direction); #else - if (direction == MODE_DESCEND) { - return ::ArrayReverse(arr, start, count); - } else { - // @fixme: Add support for _count amd _start. - return ::ArraySort(arr); - } + if (direction == MODE_DESCEND) { + return ::ArrayReverse(arr, start, count); + } else { + // @fixme: Add support for _count amd _start. + return ::ArraySort(arr); + } #endif -} + } #endif -/** - * Resizes array and fills allocated slots with given value. - * - * @param &array[] array - * Single dimensonal array. For multi-dimensional array consider: template int - * ArrayResizeFill(X &array[][2], int new_size, int reserve_size = 0, Y fill_value = EMPTY) { ... } - * @param int new_size - * New array size. - * @param reserve_size - * Reserve size value (excess). - * @param fill_value - * Value to be used as filler for allocated slots. - * @return int - * Returns the same value as ArrayResize function (count of all elements contained in the array after resizing or -1 - * if error occured). - */ -template + /** + * Resizes array and fills allocated slots with given value. + * + * @param &array[] array + * Single dimensonal array. For multi-dimensional array consider: template int + * ArrayResizeFill(X &array[][2], int new_size, int reserve_size = 0, Y fill_value = EMPTY) { ... } + * @param int new_size + * New array size. + * @param reserve_size + * Reserve size value (excess). + * @param fill_value + * Value to be used as filler for allocated slots. + * @return int + * Returns the same value as ArrayResize function (count of all elements contained in the array after resizing or -1 + * if error occured). + */ + template static int ArrayResizeFill(ARRAY_REF(X, array), int new_size, int reserve_size = 0, Y fill_value = NULL) { - const int old_size = ArrayRange(array, 0); + const int old_size = ArrayRange(array, 0); if (new_size <= old_size) return old_size; - // We want to fill all allocated slots (the whole allocated memory). - const int allocated_size = MathMax(new_size, reserve_size); + // We want to fill all allocated slots (the whole allocated memory). + const int allocated_size = MathMax(new_size, reserve_size); - int result = ArrayResize(array, new_size, reserve_size); + int result = ArrayResize(array, new_size, reserve_size); - ArrayFill(array, old_size, allocated_size - old_size, fill_value); + ArrayFill(array, old_size, allocated_size - old_size, fill_value); - return result; -} + return result; + } /** * Initializes a numeric array by a preset value. @@ -705,7 +705,7 @@ template return _peak_index; #endif -} + } /** * Searches for the largest element in the first dimension of a multidimensional numeric array. @@ -738,7 +738,7 @@ template return _peak_index; #endif -} + } /** * Returns the number of elements of a selected array. @@ -762,7 +762,7 @@ template if (_idx >= ArraySize(array)) { ArrayResize(array, MathMax(_idx + 1, ArraySize(array)), reserve_size); } else if (_idx < 0) { - Print("_idx cannot be negative! " + IntegerToString(_idx) + " passed."); + Print("Index cannot be negative! " + IntegerToString(_idx) + " passed."); DebugBreak(); } diff --git a/Bar.struct.h b/Bar.struct.h index 8b66c4898..b66f4e50a 100644 --- a/Bar.struct.h +++ b/Bar.struct.h @@ -36,15 +36,16 @@ class Serializer; // Includes. #include "Bar.enum.h" #include "Chart.enum.h" -#include "ISerializable.h" -#include "Serializer.enum.h" -#include "SerializerNode.enum.h" +#include "Serializer/Serializable.h" +#include "Serializer/Serializer.enum.h" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerNode.enum.h" #include "Std.h" /* Struct for storing OHLC values. */ struct BarOHLC #ifndef __MQL__ - : public ISerializable + : public Serializable #endif { datetime time; @@ -100,14 +101,16 @@ struct BarOHLC break; case PP_FIBONACCI: _pp = GetPivot(); - _r1 = (double)(_pp + 0.382 * _range); - _r2 = (double)(_pp + 0.618 * _range); - _r3 = _pp + _range; - _r4 = _r1 + _range; // ? - _s1 = (double)(_pp - 0.382 * _range); - _s2 = (double)(_pp - 0.618 * _range); - _s3 = _pp - _range; - _s4 = _s1 - _range; // ? + _r1 = (double)(_pp + 0.236 * _range); + _r2 = (double)(_pp + 0.382 * _range); + _r3 = (double)(_pp + 0.500 * _range); + _r4 = (double)(_pp + 0.618 * _range); + // _r5 = (double)(_pp + 0.786 * _range); + _s1 = (double)(_pp - 0.236 * _range); + _s2 = (double)(_pp - 0.382 * _range); + _s3 = (double)(_pp - 0.500 * _range); + _s4 = (double)(_pp - 0.618 * _range); + // _s5 = (double)(_pp - 0.786 * _range); break; case PP_FLOOR: // Most basic and popular type of pivots used in Forex trading technical analysis. @@ -228,10 +231,13 @@ struct BarOHLC SerializerNodeType Serialize(Serializer &s); // Converters. string ToCSV() { return StringFormat("%d,%g,%g,%g,%g", time, open, high, low, close); } + // Operators. + bool operator==(const BarOHLC &_r) { + return time == _r.time && open == _r.time && high == _r.high && low == _r.low && close == _r.close; + } + bool operator!=(const BarOHLC &_r) { return !(THIS_REF == _r); } }; -#include "Serializer.mqh" - /* Method to serialize BarOHLC structure. */ SerializerNodeType BarOHLC::Serialize(Serializer &s) { // s.Pass(THIS_REF, "time", TimeToString(time)); diff --git a/Buffer/BufferCandle.h b/Buffer/BufferCandle.h index b1c02183a..856ffb686 100644 --- a/Buffer/BufferCandle.h +++ b/Buffer/BufferCandle.h @@ -27,6 +27,8 @@ // Includes. #include "../BufferStruct.mqh" #include "../Candle.struct.h" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerJson.h" /** * Class to store struct data. @@ -41,7 +43,7 @@ class BufferCandle : public BufferStruct> { * * Called on constructor. */ - void Init() { SetOverflowListener(BufferCandleOverflowListener, 10); } + void Init() { SetOverflowListener(BufferStructOverflowListener, 10); } public: /* Constructors */ @@ -55,24 +57,10 @@ class BufferCandle : public BufferStruct> { Init(); } - /* Callback methods */ - /** - * Function should return true if resize can be made, or false to overwrite current slot. + * Returns JSON representation of the buffer. */ - static bool BufferCandleOverflowListener(ENUM_DICT_OVERFLOW_REASON _reason, int _size, int _num_conflicts) { - static int cache_limit = 86400; - switch (_reason) { - case DICT_OVERFLOW_REASON_FULL: - // We allow resize if dictionary size is less than 86400 slots. - return _size < cache_limit; - case DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS: - default: - // When there is too many conflicts, we just reject doing resize, so first conflicting slot will be reused. - break; - } - return false; - } + string ToJSON() { return SerializerConverter::FromObject(THIS_REF).ToString(); } }; #endif // BUFFER_CANDLE_H diff --git a/Buffer/BufferTick.h b/Buffer/BufferTick.h index b3e67b1cf..7ae8d7e17 100644 --- a/Buffer/BufferTick.h +++ b/Buffer/BufferTick.h @@ -28,7 +28,7 @@ #include "../BufferStruct.mqh" #include "../Chart.enum.h" #include "../Storage/IValueStorage.h" -#include "../Tick.struct.h" +#include "../Tick/Tick.struct.h" // TV = Type of price stored by BufferTick. RV = Type of property to be retrieved from BufferTick. template @@ -109,7 +109,7 @@ class BufferTick : public BufferStruct> { _vs_spread = NULL; _vs_volume = NULL; _vs_tick_volume = NULL; - SetOverflowListener(BufferTickOverflowListener, 10); + SetOverflowListener(BufferStructOverflowListener, 10); } public: @@ -210,25 +210,6 @@ class BufferTick : public BufferStruct> { // Convert to OHLC in upper method return NULL; } - - /* Callback methods */ - - /** - * Function should return true if resize can be made, or false to overwrite current slot. - */ - static bool BufferTickOverflowListener(ENUM_DICT_OVERFLOW_REASON _reason, int _size, int _num_conflicts) { - static int cache_limit = 86400; - switch (_reason) { - case DICT_OVERFLOW_REASON_FULL: - // We allow resize if dictionary size is less than 86400 slots. - return _size < cache_limit; - case DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS: - default: - // When there is too many conflicts, we just reject doing resize, so first conflicting slot will be reused. - break; - } - return false; - } }; #endif // BUFFER_TICK_H diff --git a/BufferFXT.mqh b/BufferFXT.mqh index 70e448ba4..71d7a5d28 100644 --- a/BufferFXT.mqh +++ b/BufferFXT.mqh @@ -25,9 +25,8 @@ // Includes. #include "Account/AccountMt.h" -#include "Chart.mqh" #include "DictStruct.mqh" -#include "IndicatorBase.h" +#include "Indicator/IndicatorData.h" #include "Object.mqh" // Defines. diff --git a/BufferStruct.mqh b/BufferStruct.mqh index 3e4de5554..80c0253fd 100644 --- a/BufferStruct.mqh +++ b/BufferStruct.mqh @@ -27,7 +27,7 @@ // Includes. #include "DictBase.mqh" #include "DictStruct.mqh" -#include "Serializer.mqh" +#include "Serializer/Serializer.h" /** * Implements BufferStruct's Overflow Listener. @@ -35,8 +35,18 @@ * @see DictBase */ bool BufferStructOverflowListener(ENUM_DICT_OVERFLOW_REASON _reason, int _size, int _num_conflicts) { - // We allow resize if dictionary size is less than 10000 slots. - return _size < 10000; + static int cache_limit = 86400; + switch (_reason) { + case DICT_LISTENER_FULL_CAN_RESIZE: + case DICT_LISTENER_NOT_PERFORMANT_CAN_RESIZE: + // We allow resize if dictionary size is less than 86400 slots. + return _size < cache_limit; + case DICT_LISTENER_CONFLICTS_CAN_OVERWRITE: + // We start to overwrite slots when we can't make dict bigger and there is at least 10 consecutive conflicts while + // inserting new value. + return _size >= cache_limit && _num_conflicts >= 10; + } + return true; } /** diff --git a/Candle.struct.h b/Candle.struct.h index f9f1a1ca7..ebbe5b3ce 100644 --- a/Candle.struct.h +++ b/Candle.struct.h @@ -36,16 +36,17 @@ class Serializer; // Includes. #include "Bar.enum.h" #include "Chart.enum.h" -#include "ISerializable.h" -#include "Serializer.enum.h" -#include "SerializerNode.enum.h" +#include "Serializer/Serializable.h" +#include "Serializer/Serializer.enum.h" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerNode.enum.h" #include "Std.h" /* Structure for storing OHLC values. */ template struct CandleOHLC #ifndef __MQL__ - : public ISerializable + : public Serializable #endif { T open, high, low, close; @@ -221,47 +222,128 @@ struct CandleOHLC * candle. */ template struct CandleOCTOHLC : CandleOHLC { - long open_timestamp, close_timestamp; + bool is_complete; + + // Timestamp of where the candle is placed on the chart. + int start_time; + + // Length of the candle in seconds. + int length; + + // Open and close timestamps of ticks that were part of this candle. + long open_timestamp_ms, close_timestamp_ms; // Number of ticks which formed the candle. Also known as volume. int volume; // Struct constructors. - CandleOCTOHLC(T _open = 0, T _high = 0, T _low = 0, T _close = 0, long _open_timestamp = -1, - long _close_timestamp = -1, int _volume = 0) + CandleOCTOHLC(T _open = 0, T _high = 0, T _low = 0, T _close = 0, int _start_time = -1, int _length = 0, + long _open_timestamp_ms = -1, long _close_timestamp_ms = -1, int _volume = 0) : CandleOHLC(_open, _high, _low, _close), - open_timestamp(_open_timestamp), - close_timestamp(_close_timestamp), + is_complete(true), + start_time(_start_time), + length(_length), + open_timestamp_ms(_open_timestamp_ms), + close_timestamp_ms(_close_timestamp_ms), volume(_volume) { if (_open != 0) { volume = 1; } } - // Updates OHLC values taking into consideration tick's timestamp. - void Update(long _timestamp, T _price) { - if (_timestamp < open_timestamp) { - open_timestamp = _timestamp; + /** + * Initializes candle with a given start time, lenght in seconds, first tick's timestamp and its price. + */ + void Init(int _start_time, int _length, long _timestamp_ms = -1, T _price = 0) { + is_complete = false; + start_time = _start_time; + length = _length; + open_timestamp_ms = _timestamp_ms; + close_timestamp_ms = _timestamp_ms; + volume = _price != 0 ? 1 : 0; + open = high = low = close = _price; + } + + /** + * Updates OHLC values taking into consideration tick's timestamp. + */ + void Update(long _timestamp_ms, T _price) { + if (!ContainsTimeMs(_timestamp_ms)) { + Print("Error: Cannot update candle. Given time doesn't fit in candle's time-frame!"); + DebugBreak(); + } + + bool _is_init = open_timestamp_ms == -1; + + if (_is_init || _timestamp_ms < open_timestamp_ms) { + open_timestamp_ms = _timestamp_ms; open = _price; } - if (_timestamp > close_timestamp) { - close_timestamp = _timestamp; + if (_is_init || _timestamp_ms > close_timestamp_ms) { + close_timestamp_ms = _timestamp_ms; close = _price; } - high = MathMax(high, _price); - low = MathMin(low, _price); + + if (_is_init) { + high = _price; + low = _price; + } else { + high = MathMax(high, _price); + low = MathMin(low, _price); + } // Increasing candle's volume. ++volume; } - // Returns timestamp of open price. - long GetOpenTimestamp() { return open_timestamp; } + /** + * Method used by ItemsHistory. + */ + long GetTimeMs() { return (long)start_time * 1000; } + + /** + * Method used by ItemsHistory. + */ + long GetLengthMs() { return (long)length * 1000; } + + /** + * Returns candle's start time. + */ + datetime GetTime() { return (datetime)start_time; } + + /** + * Returns timestamp of open price. + */ + long GetOpenTimestamp() { return open_timestamp_ms / 1000; } + + /** + * Returns timestamp of close price. + */ + long GetCloseTimestamp() { return close_timestamp_ms / 1000; } + + /** + * Whether given time fits in the candle. + */ + bool ContainsTimeMs(long _time_ms) { + return _time_ms >= (long)start_time * 1000 && _time_ms < (long)(start_time + length) * 1000; + } + + // Serializers. + SerializerNodeType Serialize(Serializer &s); - // Returns timestamp of close price. - long GetCloseTimestamp() { return close_timestamp; } + /** + * Returns text representation of candle. + */ + string ToString() { + return StringFormat("%.5f %.5f %.5f %.5f [%s] @ %s - %s", open, high, low, close, + is_complete ? "Complete" : "Incomplete", + TimeToString(open_timestamp_ms, TIME_DATE | TIME_MINUTES | TIME_SECONDS), + TimeToString(close_timestamp_ms, TIME_DATE | TIME_MINUTES | TIME_SECONDS)); + } }; -/* Structore for storing OHLC values with timestamp. */ +/** + * Structure for storing OHLC values with timestamp. + */ template struct CandleTOHLC : CandleOHLC { datetime time; @@ -276,8 +358,6 @@ struct CandleTOHLC : CandleOHLC { string ToCSV() { return StringFormat("%d,%g,%g,%g,%g", time, open, high, low, close); } }; -#include "Serializer.mqh" - /* Method to serialize CandleEntry structure. */ template SerializerNodeType CandleOHLC::Serialize(Serializer &s) { @@ -299,3 +379,17 @@ SerializerNodeType CandleTOHLC::Serialize(Serializer &s) { s.Pass(THIS_REF, "close", close, SERIALIZER_FIELD_FLAG_DYNAMIC); return SerializerNodeObject; } + +/* Method to serialize CandleEntry structure. */ +template +SerializerNodeType CandleOCTOHLC::Serialize(Serializer &s) { + s.Pass(THIS_REF, "is_complete", is_complete, SERIALIZER_FIELD_FLAG_DYNAMIC); + s.Pass(THIS_REF, "open_timestamp_ms", open_timestamp_ms, SERIALIZER_FIELD_FLAG_DYNAMIC); + s.Pass(THIS_REF, "close_timestamp_ms", close_timestamp_ms, SERIALIZER_FIELD_FLAG_DYNAMIC); + s.Pass(THIS_REF, "open", open, SERIALIZER_FIELD_FLAG_DYNAMIC); + s.Pass(THIS_REF, "high", high, SERIALIZER_FIELD_FLAG_DYNAMIC); + s.Pass(THIS_REF, "low", low, SERIALIZER_FIELD_FLAG_DYNAMIC); + s.Pass(THIS_REF, "close", close, SERIALIZER_FIELD_FLAG_DYNAMIC); + s.Pass(THIS_REF, "volume", volume, SERIALIZER_FIELD_FLAG_DYNAMIC); + return SerializerNodeObject; +} diff --git a/Chart.mqh b/Chart.mqh index 9db5e5419..f8080c2a1 100644 --- a/Chart.mqh +++ b/Chart.mqh @@ -29,10 +29,6 @@ * - https://www.mql5.com/en/docs/series */ -// Class dependencies. -class Chart; -class Market; - // Prevents processing this includes file for the second time. #ifndef CHART_MQH #define CHART_MQH @@ -44,9 +40,13 @@ class Market; #include "Chart.struct.serialize.h" #include "Convert.mqh" #include "Market.mqh" -#include "Serializer.mqh" +#include "Serializer/Serializer.h" #include "Task/TaskCondition.enum.h" +// Forward class declaration. +class Chart; +class Market; + #ifndef __MQL4__ // Defines structs (for MQL4 backward compatibility). // Struct arrays that contains given values of each bar of the current chart. diff --git a/Chart.struct.h b/Chart.struct.h index 6dd08b95b..b0fa365f6 100644 --- a/Chart.struct.h +++ b/Chart.struct.h @@ -32,6 +32,7 @@ // Forward class declaration. class Class; +struct ChartTf; // Includes. #include "Array.mqh" @@ -40,7 +41,8 @@ class Class; #include "Chart.enum.h" #include "Chart.struct.static.h" #include "Chart.struct.tf.h" -#include "Serializer.mqh" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerNode.enum.h" #include "Terminal.define.h" /* Defines struct to store bar entries. */ diff --git a/Chart.struct.serialize.h b/Chart.struct.serialize.h index 36147679d..1c436f916 100644 --- a/Chart.struct.serialize.h +++ b/Chart.struct.serialize.h @@ -29,8 +29,8 @@ class Serializer; // Includes. -#include "Serializer.mqh" -#include "SerializerNode.enum.h" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerNode.enum.h" /* Method to serialize ChartEntry structure. */ SerializerNodeType ChartEntry::Serialize(Serializer& _s) { @@ -42,6 +42,6 @@ SerializerNodeType ChartEntry::Serialize(Serializer& _s) { SerializerNodeType ChartParams::Serialize(Serializer& s) { s.Pass(THIS_REF, "id", id); s.Pass(THIS_REF, "symbol", symbol); - s.PassStruct(THIS_REF, "tf", tf); + // s.PassStruct(THIS_REF, "tf", tf); // @fixme return SerializerNodeObject; } diff --git a/Chart.struct.tf.h b/Chart.struct.tf.h index c1d57a9a6..0d8c4710f 100644 --- a/Chart.struct.tf.h +++ b/Chart.struct.tf.h @@ -28,11 +28,16 @@ #ifndef __MQL__ // Allows the preprocessor to include a header file when it is needed. #pragma once +#include "Platform.h" #endif +// Forward declarations. +class Serializer; + // Includes. #include "Chart.enum.h" -#include "Serializer.mqh" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerNode.enum.h" #include "Terminal.define.h" /* Defines struct for chart timeframe. */ @@ -252,7 +257,7 @@ struct ChartTf { * _tf ENUM_TIMEFRAMES Specify timeframe enum. */ static ENUM_TIMEFRAMES_INDEX TfToIndex(ENUM_TIMEFRAMES _tf) { - _tf = (_tf == 0 || _tf == PERIOD_CURRENT) ? (ENUM_TIMEFRAMES)_Period : _tf; + _tf = (_tf == 0 || _tf == PERIOD_CURRENT) ? (ENUM_TIMEFRAMES)Period() : _tf; for (int i = 0; i < ArraySize(TIMEFRAMES_LIST); i++) { if (TIMEFRAMES_LIST[i] == _tf) { return (ENUM_TIMEFRAMES_INDEX)i; diff --git a/ChartBase.h b/ChartBase.h index 7cf488116..99010949a 100644 --- a/ChartBase.h +++ b/ChartBase.h @@ -96,7 +96,7 @@ class ChartBase : public Dynamic { /** * Returns time of the bar with a given shift. */ - virtual datetime GetBarTime(int _shift = 0) = 0; + virtual datetime GetBarTime(int _rel_shift = 0) = 0; datetime GetLastBarTime() { return last_bar_time; } @@ -170,14 +170,14 @@ class ChartBase : public Dynamic { /** * Gets OHLC price values. */ - virtual BarOHLC GetOHLC(int _shift = 0) { - datetime _time = GetBarTime(_shift); + virtual BarOHLC GetOHLC(int _rel_shift = 0) { + datetime _time = GetBarTime(_rel_shift); float _open = 0, _high = 0, _low = 0, _close = 0; if (_time > 0) { - _open = (float)GetOpen(_shift); - _high = (float)GetHigh(_shift); - _low = (float)GetLow(_shift); - _close = (float)GetClose(_shift); + _open = (float)GetOpen(_rel_shift); + _high = (float)GetHigh(_rel_shift); + _low = (float)GetLow(_rel_shift); + _close = (float)GetClose(_rel_shift); } BarOHLC _ohlc(_open, _high, _low, _close, _time); return _ohlc; diff --git a/ChartMt.h b/ChartMt.h index e87e8ddcc..5f32f0230 100644 --- a/ChartMt.h +++ b/ChartMt.h @@ -31,6 +31,7 @@ #endif // Includes. +#include "Chart.struct.static.h" #include "Chart.symboltf.h" #include "Terminal.define.h" @@ -66,11 +67,11 @@ class ChartMt : public ChartBase { /** * Returns time of the bar with a given shift. */ - virtual datetime GetBarTime(int _shift = 0) override { - datetime _time = ::iTime(GetSymbol(), GetTf(), _shift); + virtual datetime GetBarTime(int _rel_shift = 0) override { + datetime _time = ::iTime(GetSymbol(), GetTf(), _rel_shift); if (_LastError != ERR_NO_ERROR) { - Print("Error: ", _LastError, " while doing ::iTime() in ChartMt::GetBarTime(", _shift, ")"); + Print("Error: ", _LastError, " while doing ::iTime() in ChartMt::GetBarTime(", _rel_shift, ")"); DebugBreak(); } diff --git a/Config.mqh b/Config.mqh index ac54499a5..81872f23f 100644 --- a/Config.mqh +++ b/Config.mqh @@ -33,7 +33,7 @@ #include "DictStruct.mqh" #include "File.mqh" #include "Object.mqh" -#include "Serializer.mqh" +#include "Serializer/Serializer.h" enum CONFIG_FORMAT { CONFIG_FORMAT_JSON, CONFIG_FORMAT_JSON_NO_WHITESPACES, CONFIG_FORMAT_INI }; diff --git a/Convert.basic.h b/Convert.basic.h new file mode 100644 index 000000000..3725a9e13 --- /dev/null +++ b/Convert.basic.h @@ -0,0 +1,151 @@ +//+------------------------------------------------------------------+ +//| 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 . + * + */ + +// Prevents processing this includes file for the second time. +#ifndef __MQL__ +#pragma once +#endif + +// Includes. +#include "Array.mqh" +#include "Common.extern.h" +#include "DateTime.mqh" +#include "Std.h" +#include "String.mqh" + +/** + * Class to provide conversion methods. + */ +class ConvertBasic { + public: + /** + * Convert integer to hex. + */ + static string IntToHex(long long_number) { + string result; + int integer_number = (int)long_number; + for (int i = 0; i < 4; i++) { + int byte = (integer_number >> (i * 8)) & 0xff; + result += StringFormat("%02x", byte); + } + return result; + } + + /** + * Convert character into integer. + */ + static int CharToInt(ARRAY_REF(int, _chars)) { + return ((_chars[0]) | (_chars[1] << 8) | (_chars[2] << 16) | (_chars[3] << 24)); + } + + /** + * Assume: len % 4 == 0. + */ + static int String4ToIntArray(ARRAY_REF(int, output), string in) { + int len; + int i, j; + len = StringLen(in); + if (len % 4 != 0) len = len - len % 4; + int size = ArraySize(output); + if (size < len / 4) { + ArrayResize(output, len / 4); + } + for (i = 0, j = 0; j < len; i++, j += 4) { + output[i] = (StringGetCharacter(in, j)) | ((StringGetCharacter(in, j + 1)) << 8) | + ((StringGetCharacter(in, j + 2)) << 16) | ((StringGetCharacter(in, j + 3)) << 24); + } + return (len / 4); + } + + static void StringToType(string _value, bool& _out) { +#ifdef __MQL__ + _out = _value != "" && _value != NULL && _value != "0" && _value != "false"; +#else + _out = _value != "" && _value != "0" && _value != "false"; +#endif + } + + static void StringToType(string _value, int& _out) { _out = (int)StringToInteger(_value); } + static void StringToType(string _value, unsigned int& _out) { _out = (unsigned int)StringToInteger(_value); } + static void StringToType(string _value, char& _out) { _out = (char)_value[0]; } + static void StringToType(string _value, unsigned char& _out) { _out = (unsigned char)_value[0]; } + static void StringToType(string _value, long& _out) { _out = StringToInteger(_value); } + static void StringToType(string _value, unsigned long& _out) { _out = StringToInteger(_value); } + static void StringToType(string _value, short& _out) { _out = (short)StringToInteger(_value); } + static void StringToType(string _value, unsigned short& _out) { _out = (unsigned short)StringToInteger(_value); } + static void StringToType(string _value, float& _out) { _out = (float)StringToDouble(_value); } + static void StringToType(string _value, double& _out) { _out = StringToDouble(_value); } + static void StringToType(string _value, string& _out) { _out = _value; } + static void StringToType(string _value, color& _out) { _out = 0; } + static void StringToType(string _value, datetime& _out) { +#ifdef __MQL4__ + _out = StrToTime(_value); +#else + _out = StringToTime(_value); +#endif + } + + static void BoolToType(bool _value, bool& _out) { _out = _value; } + static void BoolToType(bool _value, char& _out) { _out = (char)_value; } + static void BoolToType(bool _value, unsigned char& _out) { _out = (unsigned char)_value; } + static void BoolToType(bool _value, int& _out) { _out = (int)_value; } + static void BoolToType(bool _value, unsigned int& _out) { _out = (unsigned int)_value; } + static void BoolToType(bool _value, long& _out) { _out = (long)_value; } + static void BoolToType(bool _value, unsigned long& _out) { _out = (unsigned long)_value; } + static void BoolToType(bool _value, short& _out) { _out = (short)_value; } + static void BoolToType(bool _value, unsigned short& _out) { _out = (unsigned short)_value; } + static void BoolToType(bool _value, float& _out) { _out = (float)_value; } + static void BoolToType(bool _value, double& _out) { _out = (double)_value; } + static void BoolToType(bool _value, string& _out) { _out = _value ? "1" : "0"; } + static void BoolToType(bool _value, color& _out) { _out = 0; } + static void BoolToType(bool _value, datetime& _out) {} + + static void LongToType(long _value, bool& _out) { _out = (bool)_value; } + static void LongToType(long _value, char& _out) { _out = (char)_value; } + static void LongToType(long _value, unsigned char& _out) { _out = (unsigned char)_value; } + static void LongToType(long _value, int& _out) { _out = (int)_value; } + static void LongToType(long _value, unsigned int& _out) { _out = (unsigned int)_value; } + static void LongToType(long _value, long& _out) { _out = (long)_value; } + static void LongToType(long _value, unsigned long& _out) { _out = (unsigned long)_value; } + static void LongToType(long _value, short& _out) { _out = (short)_value; } + static void LongToType(long _value, unsigned short& _out) { _out = (unsigned short)_value; } + static void LongToType(long _value, float& _out) { _out = (float)_value; } + static void LongToType(long _value, double& _out) { _out = (double)_value; } + static void LongToType(long _value, string& _out) { _out = _value ? "1" : "0"; } + static void LongToType(long _value, color& _out) { _out = 0; } + static void LongToType(long _value, datetime& _out) {} + + static void DoubleToType(double _value, bool& _out) { _out = (bool)_value; } + static void DoubleToType(double _value, char& _out) { _out = (char)_value; } + static void DoubleToType(double _value, unsigned char& _out) { _out = (unsigned char)_value; } + static void DoubleToType(double _value, int& _out) { _out = (int)_value; } + static void DoubleToType(double _value, unsigned int& _out) { _out = (unsigned int)_value; } + static void DoubleToType(double _value, long& _out) { _out = (long)_value; } + static void DoubleToType(double _value, unsigned long& _out) { _out = (unsigned long)_value; } + static void DoubleToType(double _value, short& _out) { _out = (short)_value; } + static void DoubleToType(double _value, unsigned short& _out) { _out = (unsigned short)_value; } + static void DoubleToType(double _value, float& _out) { _out = (float)_value; } + static void DoubleToType(double _value, double& _out) { _out = (double)_value; } + static void DoubleToType(double _value, string& _out) { _out = _value ? "1" : "0"; } + static void DoubleToType(double _value, color& _out) { _out = 0; } + static void DoubleToType(double _value, datetime& _out) {} +}; diff --git a/Data.struct.h b/Data.struct.h index 05b81533e..6b6d84415 100644 --- a/Data.struct.h +++ b/Data.struct.h @@ -37,10 +37,11 @@ struct MqlRates; // Includes. #include "Data.enum.h" -#include "DateTime.mqh" -#include "Serializer.enum.h" -#include "SerializerNode.enum.h" +#include "DateTime.extern.h" +#include "Serializer/Serializer.enum.h" +#include "Serializer/SerializerNode.enum.h" #include "Std.h" +#include "String.mqh" #ifndef __MQL__ /** @@ -188,7 +189,7 @@ struct DataParamEntry : public MqlParam { case TYPE_CHAR: case TYPE_STRING: case TYPE_UCHAR: - return (T)::StringToDouble(string_value); + return (T)StringToDouble(string_value); case TYPE_DOUBLE: case TYPE_FLOAT: return (T)ToDouble(THIS_REF); @@ -329,48 +330,3 @@ struct DataParamEntry : public MqlParam { } SerializerNodeType Serialize(Serializer &s); }; - -#include "Serializer.mqh" - -/* Method to serialize DataParamEntry struct. */ -SerializerNodeType DataParamEntry::Serialize(Serializer &s) { - s.PassEnum(THIS_REF, "type", type, SERIALIZER_FIELD_FLAG_HIDDEN); - string aux_string; - - switch (type) { - case TYPE_BOOL: - case TYPE_UCHAR: - case TYPE_CHAR: - case TYPE_USHORT: - case TYPE_SHORT: - case TYPE_UINT: - case TYPE_INT: - case TYPE_ULONG: - case TYPE_LONG: - s.Pass(THIS_REF, "value", integer_value); - break; - - case TYPE_DOUBLE: - s.Pass(THIS_REF, "value", double_value); - break; - - case TYPE_STRING: - s.Pass(THIS_REF, "value", string_value); - break; - - case TYPE_DATETIME: - if (s.IsWriting()) { - aux_string = TimeToString(integer_value); - s.Pass(THIS_REF, "value", aux_string); - } else { - s.Pass(THIS_REF, "value", aux_string); - integer_value = StringToTime(aux_string); - } - break; - - default: - // Unknown type. Serializing anyway. - s.Pass(THIS_REF, "value", aux_string); - } - return SerializerNodeObject; -} diff --git a/Data.struct.serialize.h b/Data.struct.serialize.h new file mode 100644 index 000000000..d55ad12e1 --- /dev/null +++ b/Data.struct.serialize.h @@ -0,0 +1,78 @@ +//+------------------------------------------------------------------+ +//| 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 Data struct serialization methods. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "Data.struct.h" +#include "Serializer/Serializer.h" + +/* Method to serialize DataParamEntry struct. */ +SerializerNodeType DataParamEntry::Serialize(Serializer &s) { + s.PassEnum(THIS_REF, "type", type, SERIALIZER_FIELD_FLAG_HIDDEN); + string aux_string; + + switch (type) { + case TYPE_BOOL: + case TYPE_UCHAR: + case TYPE_CHAR: + case TYPE_USHORT: + case TYPE_SHORT: + case TYPE_UINT: + case TYPE_INT: + case TYPE_ULONG: + case TYPE_LONG: + s.Pass(THIS_REF, "value", integer_value); + break; + + case TYPE_DOUBLE: + s.Pass(THIS_REF, "value", double_value); + break; + + case TYPE_STRING: + s.Pass(THIS_REF, "value", string_value); + break; + + case TYPE_DATETIME: + if (s.IsWriting()) { + aux_string = TimeToString(integer_value); + s.Pass(THIS_REF, "value", aux_string); + } else { + s.Pass(THIS_REF, "value", aux_string); + integer_value = StringToTime(aux_string); + } + break; + + default: + // Unknown type. Serializing anyway. + s.Pass(THIS_REF, "value", aux_string); + } + return SerializerNodeObject; +} diff --git a/DateTime.extern.h b/DateTime.extern.h index ba81ce1fe..a1dbea297 100644 --- a/DateTime.extern.h +++ b/DateTime.extern.h @@ -20,15 +20,18 @@ * */ -// Includes. -#include "DateTime.enum.h" - /** * @file * Includes external declarations related to date and time. */ #ifndef __MQL__ #pragma once + +// Includes. +#include +#include "DateTime.enum.h" +#include "String.mqh" + // Forward declarations. struct MqlDateTime; diff --git a/Dict.enum.h b/Dict.enum.h index 380b9f1eb..99474def2 100644 --- a/Dict.enum.h +++ b/Dict.enum.h @@ -42,8 +42,10 @@ enum DictMode { DictModeUnknown, DictModeDict, DictModeList }; * Reason of call to overflow listener. */ enum ENUM_DICT_OVERFLOW_REASON { - DICT_OVERFLOW_REASON_FULL, - DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS, + DICT_LISTENER_FULL_CAN_RESIZE, // Dict is full. Can we grow up the dict? + DICT_LISTENER_NOT_PERFORMANT_CAN_RESIZE, // Dict is not performant (too many average number of conflicts). Can we + // grow up the dict? + DICT_LISTENER_CONFLICTS_CAN_OVERWRITE // Conflict(s) when inserting new slot. Can we overwrite random used slot? }; /** diff --git a/Dict.mqh b/Dict.mqh index 843a1fc82..5c8099c5a 100644 --- a/Dict.mqh +++ b/Dict.mqh @@ -24,11 +24,11 @@ #ifndef DICT_MQH #define DICT_MQH -#include "Convert.mqh" +#include "Convert.basic.h" #include "DictBase.mqh" #include "Matrix.mqh" -#include "Serializer.mqh" -#include "SerializerNodeIterator.mqh" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerNodeIterator.h" template class DictIterator : public DictIteratorBase { @@ -206,94 +206,105 @@ class Dict : public DictBase { * Inserts value into given array of DictSlots. */ bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V value, bool allow_resize) { - if (_mode == DictModeUnknown) - _mode = DictModeDict; - else if (_mode != DictModeDict) { + if (THIS_ATTR _mode == DictModeUnknown) + THIS_ATTR _mode = DictModeDict; + else if (THIS_ATTR _mode != DictModeDict) { Alert("Warning: Dict already operates as a list, not a dictionary!"); return false; } unsigned int position; - DictSlot* keySlot = GetSlotByKey(dictSlotsRef, key, position); + DictSlot* _slot = THIS_ATTR GetSlotByKey(dictSlotsRef, key, position); - if (keySlot == NULL && !IsGrowUpAllowed()) { - // Resize is prohibited, so we will just overwrite some slot. - allow_resize = false; + // If we have a slot then we can overwrite it. + if (_slot != NULL) { + WriteSlot(_slot, key, value, DICT_SLOT_HAS_KEY | DICT_SLOT_IS_USED | DICT_SLOT_WAS_USED); + // We're done, we don't have to increment number of slots used. + return true; } - if (allow_resize) { - // Will resize dict if there were performance problems before or there is no slots. - if (IsGrowUpAllowed() && !dictSlotsRef.IsPerformant()) { + // If we don't have a slot then we should consider growing up number of slots or overwrite some existing slot. + + bool _is_performant = dictSlotsRef.IsPerformant(); // Whether there is no performance problems. + bool _is_full = + dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots); // Whether we don't have empty slots to use. + + if ((_is_full || !_is_performant) && allow_resize) { + // We have to resize the dict as it is either full or have perfomance problems due to massive number of conflicts + // when inserting new values. + if (overflow_listener == NULL) { + // There is no overflow listener so we can freely grow up the dict. if (!GrowUp()) { + // Can't resize the dict. Error happened. return false; } - // We now have new positions of slots, so we have to take the corrent slot again. - keySlot = GetSlotByKey(dictSlotsRef, key, position); - } - - if (keySlot == NULL && dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots)) { - // No DictSlotsRef.DictSlots available. - if (overflow_listener != NULL) { - if (!overflow_listener(DICT_OVERFLOW_REASON_FULL, dictSlotsRef._num_used, 0)) { - // Overwriting slot pointed exactly by key's position in the hash table (we don't check for possible - // conflicts). - keySlot = &dictSlotsRef.DictSlots[Hash(key) % ArraySize(dictSlotsRef.DictSlots)]; + } else { + // Overflow listener will decide if we can grow up the dict. + if (overflow_listener(_is_full ? DICT_LISTENER_FULL_CAN_RESIZE : DICT_LISTENER_NOT_PERFORMANT_CAN_RESIZE, + dictSlotsRef._num_used, 0)) { + // We can freely grow up the dict. + if (!GrowUp()) { + // Can't resize the dict. Error happened. + return false; } } - - if (keySlot == NULL) { - // We need to expand array of DictSlotsRef.DictSlots (by 25% by default). - if (!GrowUp()) return false; - } } } - if (keySlot == NULL) { - position = Hash(key) % ArraySize(dictSlotsRef.DictSlots); - - unsigned int _starting_position = position; - int _num_conflicts = 0; - bool _overwrite_slot = false; - - // Searching for empty DictSlot or used one with the matching key. It skips used, hashless DictSlots. - while (dictSlotsRef.DictSlots[position].IsUsed() && - (!dictSlotsRef.DictSlots[position].HasKey() || dictSlotsRef.DictSlots[position].key != key)) { - if (overflow_listener_max_conflicts != 0 && ++_num_conflicts == overflow_listener_max_conflicts) { - if (overflow_listener != NULL) { - if (!overflow_listener(DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS, dictSlotsRef._num_used, _num_conflicts)) { - // Overflow listener returned false so we won't search for further empty slot. - _overwrite_slot = true; - break; - } - } else { - // Even if there is no overflow listener function, we stop searching for further empty slot as maximum - // number of conflicts has been reached. - _overwrite_slot = true; - break; - } - } + // At this point we have at least one free slot and we won't be doing any dict's grow up in the loop where we search + // for an empty slot. - // Position may overflow, so we will start from the beginning. - position = (position + 1) % ArraySize(dictSlotsRef.DictSlots); - } + // Position we will start from in order to search free slot. + position = THIS_ATTR Hash(key) % ArraySize(dictSlotsRef.DictSlots); + + // Saving position for further, possible overwrite. + unsigned int _starting_position = position; - if (_overwrite_slot) { - // Overwriting starting position for faster further lookup. - position = _starting_position; - } else if (!dictSlotsRef.DictSlots[position].IsUsed()) { - // If slot isn't already used then we increment number of used slots. - ++dictSlotsRef._num_used; + // How many times we had to skip slot as it was already occupied. + unsigned int _num_conflicts = 0; + + // Searching for empty DictSlot or used one with the matching key. It skips used, hashless DictSlots. + while (dictSlotsRef.DictSlots[position].IsUsed() && + (!dictSlotsRef.DictSlots[position].HasKey() || dictSlotsRef.DictSlots[position].key != key)) { + ++_num_conflicts; + + if (overflow_listener != NULL) { + // We had to skip slot as it is already occupied. Now we are checking if + // there is too many conflicts/skips and thus we can overwrite slot in + // the starting position. + if (overflow_listener(DICT_LISTENER_CONFLICTS_CAN_OVERWRITE, dictSlotsRef._num_used, _num_conflicts)) { + // Looks like dict is working as buffer and we can overwrite slot in the starting position. + position = _starting_position; + break; + } } - dictSlotsRef.AddConflicts(_num_conflicts); + // Position may overflow, so we will start from the beginning. + position = (position + 1) % ArraySize(dictSlotsRef.DictSlots); } - dictSlotsRef.DictSlots[position].key = key; - dictSlotsRef.DictSlots[position].value = value; - dictSlotsRef.DictSlots[position].SetFlags(DICT_SLOT_HAS_KEY | DICT_SLOT_IS_USED | DICT_SLOT_WAS_USED); + // Acknowledging slots array about number of conflicts as it calculates average number of conflicts per insert. + dictSlotsRef.AddConflicts(_num_conflicts); + + // Incrementing number of slots used only if we're writing into empty slot. + if (!dictSlotsRef.DictSlots[position].IsUsed()) { + ++dictSlotsRef._num_used; + } + + // Writing slot in the position of empty slot or, when overwriting, in starting position. + WriteSlot(dictSlotsRef.DictSlots[position], key, value, DICT_SLOT_HAS_KEY | DICT_SLOT_IS_USED | DICT_SLOT_WAS_USED); return true; } + /*** + * Writes slot with given key, value and flags. + */ + void WriteSlot(DictSlot& _slot, const K _key, V _value, unsigned char _slot_flags) { + _slot.key = _key; + _slot.value = _value; + _slot.SetFlags(_slot_flags); + } + /** * Inserts hashless value into given array of DictSlots. */ @@ -393,7 +404,7 @@ class Dict : public DictBase { if (i.HasKey()) { // Converting key to a string. K key; - Convert::StringToType(i.Key(), key); + ConvertBasic::StringToType(i.Key(), key); // Note that we're retrieving value by a key (as we are in an // object!). diff --git a/DictBase.mqh b/DictBase.mqh index 072a8b655..269f9ddd2 100644 --- a/DictBase.mqh +++ b/DictBase.mqh @@ -29,7 +29,6 @@ #include "Dict.enum.h" #include "DictIteratorBase.mqh" #include "DictSlot.mqh" -#include "Serializer.mqh" /** * Dictionary overflow listener. arguments are: @@ -236,18 +235,6 @@ class DictBase { // No key found. } - /** - * Checks whether overflow listener allows dict to grow up. - */ - bool IsGrowUpAllowed() { - if (overflow_listener == NULL) { - return true; - } - - // Checking if overflow listener allows resize from current to higher number of slots. - return overflow_listener(DICT_OVERFLOW_REASON_FULL, Size(), 0); - } - /** * Moves last slot to given one to fill the hole after removing the value. */ diff --git a/DictIteratorBase.mqh b/DictIteratorBase.mqh index 600b41936..80f0340b0 100644 --- a/DictIteratorBase.mqh +++ b/DictIteratorBase.mqh @@ -25,13 +25,14 @@ #pragma once #endif -#include "DictBase.mqh" -#include "DictSlotsRef.h" -#include "SerializerConversions.h" - +// Forward class declaration. template class DictBase; +#include "DictBase.mqh" +#include "DictSlotsRef.h" +#include "Serializer/SerializerConversions.h" + template class DictIteratorBase { protected: diff --git a/DictObject.mqh b/DictObject.mqh index 965025f6b..84f556bcf 100644 --- a/DictObject.mqh +++ b/DictObject.mqh @@ -20,14 +20,15 @@ * */ -// Prevents processing this includes file for the second time. -#ifndef DICT_OBJECT_MQH -#define DICT_OBJECT_MQH +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif -#include "Convert.mqh" +#include "Convert.basic.h" #include "DictBase.mqh" -#include "Serializer.mqh" -#include "SerializerNodeIterator.mqh" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerNodeIterator.h" template class DictObjectIterator : public DictIteratorBase { @@ -210,96 +211,107 @@ class DictObject : public DictBase { * Inserts value into given array of DictSlots. */ bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value, bool allow_resize) { - if (this PTR_DEREF _mode == DictModeUnknown) - this PTR_DEREF _mode = DictModeDict; - else if (this PTR_DEREF _mode != DictModeDict) { + if (THIS_ATTR _mode == DictModeUnknown) + THIS_ATTR _mode = DictModeDict; + else if (THIS_ATTR _mode != DictModeDict) { Alert("Warning: Dict already operates as a list, not a dictionary!"); return false; } unsigned int position; - DictSlot* keySlot = this PTR_DEREF GetSlotByKey(dictSlotsRef, key, position); + DictSlot* _slot = THIS_ATTR GetSlotByKey(dictSlotsRef, key, position); - if (keySlot == NULL && !this PTR_DEREF IsGrowUpAllowed()) { - // Resize is prohibited, so we will just overwrite some slot. - allow_resize = false; + // If we have a slot then we can overwrite it. + if (_slot != NULL) { + WriteSlot(PTR_TO_REF(_slot), key, value, DICT_SLOT_HAS_KEY | DICT_SLOT_IS_USED | DICT_SLOT_WAS_USED); + // We're done, we don't have to increment number of slots used. + return true; } - if (allow_resize) { - // Will resize dict if there were performance problems before or there is no slots. - if (this PTR_DEREF IsGrowUpAllowed() && !dictSlotsRef.IsPerformant()) { + // If we don't have a slot then we should consider growing up number of slots or overwrite some existing slot. + + bool _is_performant = dictSlotsRef.IsPerformant(); // Whether there is no performance problems. + bool _is_full = + dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots); // Whether we don't have empty slots to use. + + if ((_is_full || !_is_performant) && allow_resize) { + // We have to resize the dict as it is either full or have perfomance problems due to massive number of conflicts + // when inserting new values. + if (THIS_ATTR overflow_listener == NULL) { + // There is no overflow listener so we can freely grow up the dict. if (!GrowUp()) { + // Can't resize the dict. Error happened. return false; } - // We now have new positions of slots, so we have to take the corrent slot again. - keySlot = this PTR_DEREF GetSlotByKey(dictSlotsRef, key, position); - } - - if (keySlot == NULL && dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots)) { - // No DictSlotsRef.DictSlots available. - if (this PTR_DEREF overflow_listener != NULL) { - if (!this PTR_DEREF overflow_listener(DICT_OVERFLOW_REASON_FULL, dictSlotsRef._num_used, 0)) { - // Overwriting slot pointed exactly by key's position in the hash table (we don't check for possible - // conflicts). - keySlot = &dictSlotsRef.DictSlots[this PTR_DEREF Hash(key) % ArraySize(dictSlotsRef.DictSlots)]; + } else { + // Overflow listener will decide if we can grow up the dict. + if (THIS_ATTR overflow_listener( + _is_full ? DICT_LISTENER_FULL_CAN_RESIZE : DICT_LISTENER_NOT_PERFORMANT_CAN_RESIZE, + dictSlotsRef._num_used, 0)) { + // We can freely grow up the dict. + if (!GrowUp()) { + // Can't resize the dict. Error happened. + return false; } } - - if (keySlot == NULL) { - // We need to expand array of DictSlotsRef.DictSlots. - if (!GrowUp()) return false; - } } } - if (keySlot == NULL) { - position = this PTR_DEREF Hash(key) % ArraySize(dictSlotsRef.DictSlots); - - unsigned int _starting_position = position; - unsigned int _num_conflicts = 0; - bool _overwrite_slot = false; - - // Searching for empty DictSlot or used one with the matching key. It skips used, hashless DictSlots. - while (dictSlotsRef.DictSlots[position].IsUsed() && - (!dictSlotsRef.DictSlots[position].HasKey() || dictSlotsRef.DictSlots[position].key != key)) { - if (this PTR_DEREF overflow_listener_max_conflicts != 0 && - ++_num_conflicts == this PTR_DEREF overflow_listener_max_conflicts) { - if (this PTR_DEREF overflow_listener != NULL) { - if (!this PTR_DEREF overflow_listener(DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS, dictSlotsRef._num_used, - _num_conflicts)) { - // Overflow listener returned false so we won't search for further empty slot PTR_DEREF - _overwrite_slot = true; - break; - } - } else { - // Even if there is no overflow listener function, we stop searching for further empty slot as maximum - // number of conflicts has been reached. - _overwrite_slot = true; - break; - } - } + // At this point we have at least one free slot and we won't be doing any dict's grow up in the loop where we search + // for an empty slot. - // Position may overflow, so we will start from the beginning. - position = (position + 1) % ArraySize(dictSlotsRef.DictSlots); - } + // Position we will start from in order to search free slot. + position = THIS_ATTR Hash(key) % ArraySize(dictSlotsRef.DictSlots); + + // Saving position for further, possible overwrite. + unsigned int _starting_position = position; - if (_overwrite_slot) { - // Overwriting starting position for faster further lookup. - position = _starting_position; - } else if (!dictSlotsRef.DictSlots[position].IsUsed()) { - // If slot isn't already used then we increment number of used slots. - ++dictSlotsRef._num_used; + // How many times we had to skip slot as it was already occupied. + unsigned int _num_conflicts = 0; + + // Searching for empty DictSlot or used one with the matching key. It skips used, hashless DictSlots. + while (dictSlotsRef.DictSlots[position].IsUsed() && + (!dictSlotsRef.DictSlots[position].HasKey() || dictSlotsRef.DictSlots[position].key != key)) { + ++_num_conflicts; + + if (THIS_ATTR overflow_listener != NULL) { + // We had to skip slot as it is already occupied. Now we are checking if + // there is too many conflicts/skips and thus we can overwrite slot in + // the starting position. + if (THIS_ATTR overflow_listener(DICT_LISTENER_CONFLICTS_CAN_OVERWRITE, dictSlotsRef._num_used, + _num_conflicts)) { + // Looks like dict is working as buffer and we can overwrite slot in the starting position. + position = _starting_position; + break; + } } - dictSlotsRef.AddConflicts(_num_conflicts); + // Position may overflow, so we will start from the beginning. + position = (position + 1) % ArraySize(dictSlotsRef.DictSlots); } - dictSlotsRef.DictSlots[position].key = key; - dictSlotsRef.DictSlots[position].value = value; - dictSlotsRef.DictSlots[position].SetFlags(DICT_SLOT_HAS_KEY | DICT_SLOT_IS_USED | DICT_SLOT_WAS_USED); + // Acknowledging slots array about number of conflicts as it calculates average number of conflicts per insert. + dictSlotsRef.AddConflicts(_num_conflicts); + + // Incrementing number of slots used only if we're writing into empty slot. + if (!dictSlotsRef.DictSlots[position].IsUsed()) { + ++dictSlotsRef._num_used; + } + + // Writing slot in the position of empty slot or, when overwriting, in starting position. + WriteSlot(dictSlotsRef.DictSlots[position], key, value, DICT_SLOT_HAS_KEY | DICT_SLOT_IS_USED | DICT_SLOT_WAS_USED); return true; } + /*** + * Writes slot with given key, value and flags. + */ + void WriteSlot(DictSlot& _slot, const K _key, V& _value, unsigned char _slot_flags) { + _slot.key = _key; + _slot.value = _value; + _slot.SetFlags(_slot_flags); + } + /** * Inserts hashless value into given array of DictSlots. */ @@ -418,7 +430,7 @@ class DictObject : public DictBase { if (i.HasKey()) { // Converting key to a string. K key; - Convert::StringToType(i.Key(), key); + ConvertBasic::StringToType(i.Key(), key); // Note that we're retrieving value by a key (as we are in an // object!). @@ -450,5 +462,3 @@ class DictObject : public DictBase { } } }; - -#endif diff --git a/DictSlot.mqh b/DictSlot.mqh index cfd0632f7..3cbd4a473 100644 --- a/DictSlot.mqh +++ b/DictSlot.mqh @@ -55,4 +55,4 @@ class DictSlot { void RemoveFlags(unsigned char flags) { _flags &= (unsigned char)~flags; } }; -#endif +#endif // DICT_SLOT_MQH diff --git a/DictSlotsRef.h b/DictSlotsRef.h index ec487ec0c..ff90c8b17 100644 --- a/DictSlotsRef.h +++ b/DictSlotsRef.h @@ -37,6 +37,7 @@ #include "Std.h" #include "Util.h" +// Forward class declaration. template class DictSlot; diff --git a/DictStruct.mqh b/DictStruct.mqh index d30fb06bc..b7bb1a173 100644 --- a/DictStruct.mqh +++ b/DictStruct.mqh @@ -21,17 +21,16 @@ */ // Prevents processing this includes file for the second time. -#ifndef DICT_STRUCT_MQH -#define DICT_STRUCT_MQH - -// Forward declarations. -class Dynamic; -class Log; +#ifndef __MQL__ +#pragma once +#endif +// Includes. +#include "Convert.basic.h" #include "DictBase.mqh" #include "DictIteratorBase.mqh" -#include "Serializer.mqh" -#include "SerializerNodeIterator.mqh" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerNodeIterator.h" // DictIterator could be used as DictStruct iterator. #define DictStructIterator DictIteratorBase @@ -290,88 +289,99 @@ class DictStruct : public DictBase { } unsigned int position; - DictSlot* keySlot = THIS_ATTR GetSlotByKey(dictSlotsRef, key, position); + DictSlot* _slot = THIS_ATTR GetSlotByKey(dictSlotsRef, key, position); - if (keySlot == NULL && !THIS_ATTR IsGrowUpAllowed()) { - // Resize is prohibited, so we will just overwrite some slot. - allow_resize = false; + // If we have a slot then we can overwrite it. + if (_slot != NULL) { + WriteSlot(PTR_TO_REF(_slot), key, value, DICT_SLOT_HAS_KEY | DICT_SLOT_IS_USED | DICT_SLOT_WAS_USED); + // We're done, we don't have to increment number of slots used. + return true; } - if (allow_resize) { - // Will resize dict if there were performance problems before or there is no slots. - if (THIS_ATTR IsGrowUpAllowed() && !dictSlotsRef.IsPerformant()) { + // If we don't have a slot then we should consider growing up number of slots or overwrite some existing slot. + + bool _is_performant = dictSlotsRef.IsPerformant(); // Whether there is no performance problems. + bool _is_full = + dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots); // Whether we don't have empty slots to use. + + if ((_is_full || !_is_performant) && allow_resize) { + // We have to resize the dict as it is either full or have perfomance problems due to massive number of conflicts + // when inserting new values. + if (THIS_ATTR overflow_listener == NULL) { + // There is no overflow listener so we can freely grow up the dict. if (!GrowUp()) { + // Can't resize the dict. Error happened. return false; } - // We now have new positions of slots, so we have to take the corrent slot again. - keySlot = THIS_ATTR GetSlotByKey(dictSlotsRef, key, position); - } - - if (keySlot == NULL && dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots)) { - // No DictSlotsRef.DictSlots available. - if (THIS_ATTR overflow_listener != NULL) { - if (!THIS_ATTR overflow_listener(DICT_OVERFLOW_REASON_FULL, dictSlotsRef._num_used, 0)) { - // Overwriting slot pointed exactly by key's position in the hash table (we don't check for possible - // conflicts). - keySlot = &dictSlotsRef.DictSlots[THIS_ATTR Hash(key) % ArraySize(dictSlotsRef.DictSlots)]; + } else { + // Overflow listener will decide if we can grow up the dict. + if (THIS_ATTR overflow_listener( + _is_full ? DICT_LISTENER_FULL_CAN_RESIZE : DICT_LISTENER_NOT_PERFORMANT_CAN_RESIZE, + dictSlotsRef._num_used, 0)) { + // We can freely grow up the dict. + if (!GrowUp()) { + // Can't resize the dict. Error happened. + return false; } } - - if (keySlot == NULL) { - // We need to expand array of DictSlotsRef.DictSlots. - if (!GrowUp()) return false; - } } } - if (keySlot == NULL) { - position = THIS_ATTR Hash(key) % ArraySize(dictSlotsRef.DictSlots); - - unsigned int _starting_position = position; - unsigned int _num_conflicts = 0; - bool _overwrite_slot = false; - - // Searching for empty DictSlot or used one with the matching key. It skips used, hashless DictSlots. - while (dictSlotsRef.DictSlots[position].IsUsed() && - (!dictSlotsRef.DictSlots[position].HasKey() || dictSlotsRef.DictSlots[position].key != key)) { - if (THIS_ATTR overflow_listener_max_conflicts != 0 && - ++_num_conflicts == THIS_ATTR overflow_listener_max_conflicts) { - if (THIS_ATTR overflow_listener != NULL) { - if (!THIS_ATTR overflow_listener(DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS, dictSlotsRef._num_used, - _num_conflicts)) { - // Overflow listener returned false so we won't search for further empty slot. - _overwrite_slot = true; - break; - } - } else { - // Even if there is no overflow listener function, we stop searching for further empty slot as maximum - // number of conflicts has been reached. - _overwrite_slot = true; - break; - } - } + // At this point we have at least one free slot and we won't be doing any dict's grow up in the loop where we search + // for an empty slot. - // Position may overflow, so we will start from the beginning. - position = (position + 1) % ArraySize(dictSlotsRef.DictSlots); - } + // Position we will start from in order to search free slot. + position = THIS_ATTR Hash(key) % ArraySize(dictSlotsRef.DictSlots); + + // Saving position for further, possible overwrite. + unsigned int _starting_position = position; - if (_overwrite_slot) { - // Overwriting starting position for faster further lookup. - position = _starting_position; - } else if (!dictSlotsRef.DictSlots[position].IsUsed()) { - // If slot isn't already used then we increment number of used slots. - ++dictSlotsRef._num_used; + // How many times we had to skip slot as it was already occupied. + unsigned int _num_conflicts = 0; + + // Searching for empty DictSlot or used one with the matching key. It skips used, hashless DictSlots. + while (dictSlotsRef.DictSlots[position].IsUsed() && + (!dictSlotsRef.DictSlots[position].HasKey() || dictSlotsRef.DictSlots[position].key != key)) { + ++_num_conflicts; + + if (THIS_ATTR overflow_listener != NULL) { + // We had to skip slot as it is already occupied. Now we are checking if + // there is too many conflicts/skips and thus we can overwrite slot in + // the starting position. + if (THIS_ATTR overflow_listener(DICT_LISTENER_CONFLICTS_CAN_OVERWRITE, dictSlotsRef._num_used, + _num_conflicts)) { + // Looks like dict is working as buffer and we can overwrite slot in the starting position. + position = _starting_position; + break; + } } - dictSlotsRef.AddConflicts(_num_conflicts); + // Position may overflow, so we will start from the beginning. + position = (position + 1) % ArraySize(dictSlotsRef.DictSlots); } - dictSlotsRef.DictSlots[position].key = key; - dictSlotsRef.DictSlots[position].value = value; - dictSlotsRef.DictSlots[position].SetFlags(DICT_SLOT_HAS_KEY | DICT_SLOT_IS_USED | DICT_SLOT_WAS_USED); + // Acknowledging slots array about number of conflicts as it calculates average number of conflicts per insert. + dictSlotsRef.AddConflicts(_num_conflicts); + + // Incrementing number of slots used only if we're writing into empty slot. + if (!dictSlotsRef.DictSlots[position].IsUsed()) { + ++dictSlotsRef._num_used; + } + + // Writing slot in the position of empty slot or, when overwriting, in starting position. + WriteSlot(dictSlotsRef.DictSlots[position], key, value, DICT_SLOT_HAS_KEY | DICT_SLOT_IS_USED | DICT_SLOT_WAS_USED); return true; } + /*** + * Writes slot with given key, value and flags. + */ + void WriteSlot(DictSlot& _slot, const K _key, V& _value, unsigned char _slot_flags) { + _slot.key = _key; + _slot.value = _value; + _slot.SetFlags(_slot_flags); + } + /** * Inserts hashless value into given array of DictSlots. */ @@ -485,7 +495,7 @@ class DictStruct : public DictBase { if (i.HasKey()) { // Converting key to a string. K key; - Convert::StringToType(i.Key(), key); + ConvertBasic::StringToType(i.Key(), key); // Note that we're retrieving value by a key (as we are in an // object!). @@ -515,5 +525,3 @@ class DictStruct : public DictBase { } } }; - -#endif diff --git a/Draw.mqh b/Draw.mqh index fbb32f2f1..0e5168a01 100644 --- a/Draw.mqh +++ b/Draw.mqh @@ -78,7 +78,7 @@ void SetIndexStyle(int _index, int _type, int _style = EMPTY, int _width = EMPTY /** * Class to provide drawing methods working with graphic objects. */ -class Draw : public Chart { +class Draw : public Object { protected: // Variables. long chart_id; diff --git a/EA.mqh b/EA.mqh index 06786353a..3d7bdb6d4 100644 --- a/EA.mqh +++ b/EA.mqh @@ -40,10 +40,10 @@ #include "Market.mqh" #include "Platform.h" #include "Refs.struct.h" -#include "SerializerConverter.mqh" -#include "SerializerCsv.mqh" -#include "SerializerJson.mqh" -#include "SerializerSqlite.mqh" +#include "Serializer/SerializerConverter.h" +#include "Serializer/SerializerCsv.h" +#include "Serializer/SerializerJson.h" +#include "Serializer/SerializerSqlite.h" #include "Strategy.mqh" #include "SummaryReport.mqh" #include "Task/Task.h" diff --git a/File.mqh b/File.mqh index 03a57aec5..900ce7a31 100644 --- a/File.mqh +++ b/File.mqh @@ -32,9 +32,10 @@ // Includes. #include "File.define.h" #include "File.extern.h" +#include "Std.h" #include "Terminal.define.h" -#include "Terminal.extern.h" #include "Terminal.enum.h" +#include "Terminal.extern.h" #ifndef __MQL__ enum ENUM_FILE_PROPERTY_INTEGER { diff --git a/Flags.h b/Flags.h index cbc9b04f7..341eca289 100644 --- a/Flags.h +++ b/Flags.h @@ -26,7 +26,7 @@ template struct Flags { // Bit-based value. - unsigned T value; + T value; /** * Constructor. diff --git a/Indicator/Details.md b/Indicator/Details.md new file mode 100644 index 000000000..bc258f7b7 --- /dev/null +++ b/Indicator/Details.md @@ -0,0 +1,90 @@ + #Explanation of shift parameters in Indicator / IndicatorData / other classes for following methods: +- `GetEntryValue(int _mode = 0, int _abs_shift = 0)` +- `GetEntryAlter(IndicatorDataEntry &_entry, int _rel_shift)` +- `GetValue(int _mode = 0, int _rel_shift = 0)` +- `GetEntry(int _rel_shift = 0)` +- `GetBarTime(int _rel_shift = 0)` + +## GetEntryValue(int _mode, int _abs_shift) - overridable method + +Method must be overriden in any new indicator and MUST NOT apply shift from `iparams.shift`/`iparams.GetShift()`! Shift 0 must always point to the value for the current tick. + +Returns indicators's value for a given mode and absolute shift (the shift is directly passed to built-in methods such as iATR(), OnCalculate methods such as `iVIDyAOnIndicator()` and also to `iCustom()`). + +For `OnCalculate()` methods such as iVIDyAOnIndicator(), the shift is passed to `return _cache.GetTailValue(_mode, _shift);` so we can return value calculated in the past (or just retrieve **DBL_MAX** in case the value was not yet calculated). +Note that `OnCalculate()` methods uses single/multiple price buffers, e.g., applied price or OHLCs from base indicator. + +In scenario of `VIDyA[shift = 2] <- Candle <- TickMt` call hierarchy looks like: +```cpp +- VIDyA::GetEntry(_rel_shift = 1) // Then per each mode: +- entry.values[_mode] = Indicator::GetValue(_mode, _rel_shift = 1) +- VIDyA::GetEntryValue(_mode, _abs_shift = iparams.shift + 1) +- VIDyA::iVIDyAOnIndicator(..., _mode, _shift = 3) then: // Shift is absolute. +- VIDyA::iVIDyAOnArray(..., _mode, _shift = 3) then: // Shift is absolute. + return _cache.GetTailValue(_mode, _shift = 3); // Shift is absolute. +``` +Last line means that we will retrieve **VIDyA** value shifted by 3 (2 from `iparams.shift` + 1 from `GetEntry()`). It is correct. + +## GetEntryAlter(IndicatorDataEntry &_entry, int _rel_shift) - overridable method + +Shift passed is relative to the shift from `IndicatorParams::shift` (read via `Indicator::iparams.shift`). + +Method calls (seen in **MWMFI**, **CCI**, **Envelopes**, **Momentum**, **Pivot**): +```cpp +- GetValue(_mode, _rel_shift) // GetValue() takes relative shift. +``` + +## GetValue(int _mode = 0, int _rel_shift = 0) - non-overridable method + +Shift passed is relative to the shift from `IndicatorParams::shift` (read via `Indicator::iparams.shift`). + +Method calls: +```cpp +- GetEntryValue(_mode, _abs_shift = iparams.shift + _rel_shift) +``` + +## GetEntry(int _rel_shift = 0) - overridable method + +Shift passed is relative to the shift from `IndicatorParams::shift` (read via `Indicator::iparams.shift`). + +If you need to access entries from absolute shift, use `GetEntryByAbsShift(int _abs_shift)`. + +Note that values accessed via index operator `storage[rel_shift]` e.g., inside `OnCalculate()` methods like `double _o = open[rel_shift = 4].Get()` will take relative shift and retrieve open price shifted by (in this scenario) `4 + iparams.shift` set in base indicator: +```cpp +- double _o = open[_rel_shift = 4] +- IndicatorBufferValueStorage::Fetch(_rel_shift = 4) +- IndicatorData::GetValue(_mode, _rel_shift = 4) +- Indicator::GetEntryValue(_mode, _abs_shift = iparams.shift + 4) // As GetEntryValue() takes absolute shift, we add shift from iparams.shift. +- Indicator::GetEntry(_rel_shift = _abs_shift - iparams.shift)... // Converting absolute shift into relative one for GetEntry(). +- ...[_mode]; // IndicatorDataEntry.values[_mode].Get(...); +``` + +## GetBarTime(int _rel_shift = 0) + +Shift passed is relative to the shift from `IndicatorParams::shift` (read via `Indicator::iparams.shift`). + +## GetPrice(ENUM_APPLIED_PRICE _ap, int _rel_shift = 0) + +Shift passed is relative to the shift from `IndicatorParams::shift` (read via `Indicator::iparams.shift`). + +## GetBars() + +Number of returned bars is decremented by `IndicatorParams::shift` (read via `Indicator::iparams.shift`). Thus if there are **10** bars and *shift* is **8** then `GetBars()` will return **2**. That means that you can safely do `GetEntry()` for relative shifts **0** or **1**. There won't be any value in other relative shifts. + +## HistoryValueStorage::Fetch(int _rel_shift) + +Shift passed is relative to the shift from `IndicatorParams::shift` (read via `Indicator::iparams.shift`). + +## Indi_\*::\*OnIndicator(..., _shift, [_shift1], [_shift2]) + +All shifts passed are relative to the shift from `IndicatorParams::shift` (read via `Indicator::iparams.shift`). + +## IndicatorCalculateCache::GetValue()/GetTailValue(int _buffer_index, int _shift) + +Shift passed may be relative or absolute. It depends on the ValueStorage specialization. + +E.g., `HistoryValueStorage` operates on relative shifts, but `NativeValueStorage` operates on absolute shift, because it is unaware for which indicator values are stored. + +Thus way, `IndicatorCalculateCache::GetValue()` and `IndicatorCalculateCache::GetTailValue()` also don't know which type of shift was passed. However, all current indicators uses `NativeValueStorage` for storing indicator values, so shift passed must is treated as absolute. + +In scenario where indicator (shift **2**) has **8** values in the buffer with (assuming **10** candles have passed), `GetEntry(0)` would retrieve cache value from shift \ No newline at end of file diff --git a/Indicator.define.h b/Indicator/Indicator.define.h similarity index 91% rename from Indicator.define.h rename to Indicator/Indicator.define.h index 8362bb981..b86a877ea 100644 --- a/Indicator.define.h +++ b/Indicator/Indicator.define.h @@ -26,7 +26,7 @@ */ // Includes. -#include "Terminal.define.h" +#include "../Terminal.define.h" #ifndef __MQL__ // Allows the preprocessor to include a header file when it is needed. @@ -95,9 +95,6 @@ #define LOWER_LINE 1 // Bottom line. #endif -// Forward declarations. -class DrawIndicator; - #ifndef __MQL__ // // Empty value in an indicator buffer. @@ -135,3 +132,15 @@ class DrawIndicator; return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; \ } \ return _res[0]; + +#define INDI_REQUIRE_BARS_OR_RETURN(_indi, _period, _ret) \ + if ((int)(_indi)PTR_DEREF GetBars() < (int)_period) { \ + return _ret; \ + } + +#define INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _period) INDI_REQUIRE_BARS_OR_RETURN(_indi, _period, DBL_MAX) + +// We're adding 1 because e.g., shift 1 means that we need two bars to exist in +// history in order to retrieve bar at shift 1. +#define INDI_REQUIRE_SHIFT_OR_RETURN(_indi, _rel_shift, _ret) INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _rel_shift + 1) +#define INDI_REQUIRE_SHIFT_OR_RETURN_EMPTY(_indi, _rel_shift) INDI_REQUIRE_SHIFT_OR_RETURN(_indi, _rel_shift, DBL_MAX) diff --git a/Indicator.enum.h b/Indicator/Indicator.enum.h similarity index 81% rename from Indicator.enum.h rename to Indicator/Indicator.enum.h index 23d383ee5..ae303e27a 100644 --- a/Indicator.enum.h +++ b/Indicator/Indicator.enum.h @@ -130,7 +130,7 @@ enum ENUM_INDICATOR_TYPE { INDI_WPR, // Williams' Percent Range INDI_ZIGZAG, // ZigZag INDI_ZIGZAG_COLOR, // ZigZag Color - FINAL_INDICATOR_TYPE_ENTRY // (None) + FINAL_INDICATOR_TYPE_ENTRY }; // Indicator line identifiers used in ADX and ADXW @@ -147,14 +147,6 @@ enum ENUM_INDI_ADX_LINE { FINAL_INDI_ADX_LINE_ENTRY, }; -/* Define indicator index. */ -enum ENUM_INDICATOR_INDEX { - CURR = 0, - PREV = 1, - PPREV = 2, - FINAL_ENUM_INDICATOR_INDEX = 3 // Should be the last one. Used to calculate the number of enum items. -}; - /* Indicator line identifiers used in Envelopes and Fractals */ enum ENUM_LO_UP_LINE { #ifdef __MQL4__ @@ -198,61 +190,6 @@ enum ENUM_SIGNAL_LINE { enum ENUM_APPLIED_VOLUME { VOLUME_TICK = 0, VOLUME_REAL = 1 }; #endif -/* Indicator entry flags. */ -enum INDICATOR_ENTRY_FLAGS { - INDI_ENTRY_FLAG_NONE = 0 << 0, - INDI_ENTRY_FLAG_IS_BITWISE = 1 << 0, - INDI_ENTRY_FLAG_IS_DOUBLED = 1 << 1, // Type is doubled in size (e.g. double or long). - INDI_ENTRY_FLAG_IS_EXPIRED = 1 << 2, - INDI_ENTRY_FLAG_IS_REAL = 1 << 3, // Type is real (float or double). - INDI_ENTRY_FLAG_IS_PRICE = 1 << 4, - INDI_ENTRY_FLAG_IS_UNSIGNED = 1 << 5, // Type is unsigned (unsigned int or unsigned long). - INDI_ENTRY_FLAG_IS_VALID = 1 << 6, - INDI_ENTRY_FLAG_INSUFFICIENT_DATA = 1 << 7, // Entry has missing value for that shift and probably won't ever have. -}; - -// Storage type for IndicatorBase::GetSpecificValueStorage(). -enum ENUM_INDI_VS_TYPE { - INDI_VS_TYPE_NONE, // Not set. - INDI_VS_TYPE_TIME, // Candle. - INDI_VS_TYPE_TICK_VOLUME, // Candle. - INDI_VS_TYPE_VOLUME, // Candle. - INDI_VS_TYPE_SPREAD, // Candle. - INDI_VS_TYPE_PRICE_OPEN, // Candle. - INDI_VS_TYPE_PRICE_HIGH, // Candle. - INDI_VS_TYPE_PRICE_LOW, // Candle. - INDI_VS_TYPE_PRICE_CLOSE, // Candle. - INDI_VS_TYPE_PRICE_MEDIAN, // Candle. - INDI_VS_TYPE_PRICE_TYPICAL, // Candle. - INDI_VS_TYPE_PRICE_WEIGHTED, // Candle. - INDI_VS_TYPE_PRICE_BID, // Tick. - INDI_VS_TYPE_PRICE_ASK, // Tick. - // Indexed value storages, available if indicator have buffer at this index: - INDI_VS_TYPE_INDEX_0, - INDI_VS_TYPE_INDEX_1, - INDI_VS_TYPE_INDEX_2, - INDI_VS_TYPE_INDEX_4, - INDI_VS_TYPE_INDEX_5, - INDI_VS_TYPE_INDEX_6, - INDI_VS_TYPE_INDEX_7, - INDI_VS_TYPE_INDEX_8, - INDI_VS_TYPE_INDEX_9, - INDI_VS_TYPE_INDEX_FIRST = INDI_VS_TYPE_INDEX_0, - INDI_VS_TYPE_INDEX_LAST = INDI_VS_TYPE_INDEX_9, - - // Account Stats. - INDI_VS_TYPE_ACCOUNT_STATS_DATE_TIME, - INDI_VS_TYPE_ACCOUNT_STATS_BALANCE, - INDI_VS_TYPE_ACCOUNT_STATS_CREDIT, - INDI_VS_TYPE_ACCOUNT_STATS_EQUITY, - INDI_VS_TYPE_ACCOUNT_STATS_PROFIT, - INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_USED, - INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_FREE, - INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_AVAIL, - INDI_VS_TYPE_ACCOUNT_STATS_INDEX_FIRST = INDI_VS_TYPE_ACCOUNT_STATS_DATE_TIME, - INDI_VS_TYPE_ACCOUNT_STATS_INDEX_LAST = INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_AVAIL, -}; - #define INDI_VS_TYPE_ACCOUNT_STATS_BUFFERS_COUNT \ (INDI_VS_TYPE_ACCOUNT_STATS_INDEX_LAST - INDI_VS_TYPE_ACCOUNT_STATS_INDEX_FIRST + 1) diff --git a/Indicator.mqh b/Indicator/Indicator.h similarity index 92% rename from Indicator.mqh rename to Indicator/Indicator.h index a5a85e4d8..53474ff19 100644 --- a/Indicator.mqh +++ b/Indicator/Indicator.h @@ -24,27 +24,30 @@ #ifndef INDICATOR_MQH #define INDICATOR_MQH -// Includes. -#include "Array.mqh" -#include "BufferStruct.mqh" -#include "DateTime.mqh" -#include "DrawIndicator.mqh" -#include "Flags.h" +// Forward class declaration. +struct IndicatorParams; + #include "Indicator.define.h" #include "Indicator.enum.h" -#include "Indicator.struct.cache.h" #include "Indicator.struct.h" #include "Indicator.struct.serialize.h" -#include "IndicatorData.mqh" -#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" +#include "IndicatorData.h" + +// Includes. +#include "../Array.mqh" +#include "../BufferStruct.mqh" +#include "../DateTime.mqh" +#include "../DrawIndicator.mqh" +#include "../Flags.h" +#include "../Math.h" +#include "../Object.mqh" +#include "../Refs.mqh" +#include "../Serializer/Serializer.h" +#include "../Serializer/SerializerCsv.h" +#include "../Serializer/SerializerJson.h" +#include "../Storage/ValueStorage.h" +#include "../Storage/ValueStorage.indicator.h" +#include "../Storage/ValueStorage.native.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compatibility). @@ -225,6 +228,18 @@ class Indicator : public IndicatorData { draw.SetWindow(_window); } + /* Converters */ + + /** + * Converts relative shift into absolute one. + */ + int ToAbsShift(int _rel_shift) override { return _rel_shift + iparams.shift; } + + /** + * Converts absolute shift into relative one. + */ + int ToRelShift(int _abs_shift) override { return _abs_shift - iparams.shift; } + /* Buffer methods */ virtual string CacheKey() { return GetFullName(); } @@ -349,13 +364,6 @@ class Indicator : public IndicatorData { */ // int GetModeCount() override { return (int)iparams.max_modes; } - /** - * Gets indicator's timeframe. - */ - ENUM_TIMEFRAMES GetTf() { - return ChartTf::SecsToTf(iparams.Get(STRUCT_ENUM(IndicatorParams, INDI_PARAMS_PROP_BPS))); - } - /** * Gets indicator's params. */ @@ -620,11 +628,9 @@ class Indicator : public IndicatorData { * @return * Returns IndicatorDataEntry struct filled with indicator values. */ - IndicatorDataEntry GetEntry(long _index = -1) override { + IndicatorDataEntry GetEntry(int _rel_shift = 0) override { ResetLastError(); - int _ishift = _index >= 0 ? (int)_index : iparams.GetShift(); - long _bar_time; - _bar_time = GetBarTime(_ishift); + long _bar_time = GetBarTime(_rel_shift); if (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE)) == IDATA_BUILTIN && (GetPossibleDataModes() & IDATA_BUILTIN) == 0) { @@ -645,10 +651,10 @@ class Indicator : public IndicatorData { _entry.Resize(_max_modes); _entry.timestamp = _bar_time; #ifndef __MQL4__ - if (IndicatorBase::Get(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_CHANGED))) { + if (IndicatorData::Get(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_CHANGED))) { // Resets the handle on any parameter changes. - IndicatorBase::Set(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_HANDLE), INVALID_HANDLE); - IndicatorBase::Set(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_CHANGED), false); + IndicatorData::Set(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_HANDLE), INVALID_HANDLE); + IndicatorData::Set(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_CHANGED), false); } #endif for (int _mode = 0; _mode < _max_modes; _mode++) { @@ -656,22 +662,22 @@ class Indicator : public IndicatorData { case TYPE_BOOL: case TYPE_CHAR: case TYPE_INT: - _entry.values[_mode] = GetValue(_mode, _ishift); + _entry.values[_mode] = GetValue(_mode, _rel_shift); break; case TYPE_LONG: - _entry.values[_mode] = GetValue(_mode, _ishift); + _entry.values[_mode] = GetValue(_mode, _rel_shift); break; case TYPE_UINT: - _entry.values[_mode] = GetValue(_mode, _ishift); + _entry.values[_mode] = GetValue(_mode, _rel_shift); break; case TYPE_ULONG: - _entry.values[_mode] = GetValue(_mode, _ishift); + _entry.values[_mode] = GetValue(_mode, _rel_shift); break; case TYPE_DOUBLE: - _entry.values[_mode] = GetValue(_mode, _ishift); + _entry.values[_mode] = GetValue(_mode, _rel_shift); break; case TYPE_FLOAT: - _entry.values[_mode] = GetValue(_mode, _ishift); + _entry.values[_mode] = GetValue(_mode, _rel_shift); break; case TYPE_STRING: case TYPE_UCHAR: @@ -682,12 +688,12 @@ class Indicator : public IndicatorData { if (_LastError != ERR_SUCCESS) { datetime _bar_dt = (datetime)_bar_time; - Print("Error: Code ", _LastError, " while trying to retrieve entry at shift ", _ishift, ", mode ", _mode, - ", time ", _bar_dt); + Print("Error: Code ", _LastError, " while trying to retrieve entry at shift ", _rel_shift, " (absolute ", + ToAbsShift(_rel_shift), "), mode ", _mode, ", time ", _bar_dt); DebugBreak(); } } - GetEntryAlter(_entry, _ishift); + GetEntryAlter(_entry, _rel_shift); _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, IsValidEntry(_entry)); if (_entry.IsValid()) { idata.Add(_entry, _bar_time); @@ -710,7 +716,7 @@ class Indicator : public IndicatorData { * This method allows user to modify the struct entry before it's added to cache. * This method is called on GetEntry() right after values are set. */ - virtual void GetEntryAlter(IndicatorDataEntry& _entry, int _shift) { + virtual void GetEntryAlter(IndicatorDataEntry& _entry, int _rel_shift) { ENUM_DATATYPE _dtype = Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_DTYPE)); _entry.AddFlags(_entry.GetDataTypeFlags(_dtype)); }; @@ -723,9 +729,8 @@ class Indicator : public IndicatorData { * @return * Returns DataParamEntry struct filled with a single value. */ - IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = 0) override { - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); - return GetEntry(_ishift)[_mode]; + IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) override { + return GetEntry(ToRelShift(_abs_shift))[_mode]; } /* Virtual methods */ diff --git a/Indicator.struct.h b/Indicator/Indicator.struct.h similarity index 73% rename from Indicator.struct.h rename to Indicator/Indicator.struct.h index cde778669..f8ec7608e 100644 --- a/Indicator.struct.h +++ b/Indicator/Indicator.struct.h @@ -35,18 +35,15 @@ template class Indicator; struct ChartParams; -// Defines. -#define STRUCT_ENUM_INDICATOR_STATE_PROP STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) - // Includes. -#include "Array.mqh" -#include "Chart.struct.tf.h" -#include "Data.struct.h" -#include "DateTime.struct.h" +#include "../Array.mqh" +#include "../Chart.struct.tf.h" +#include "../Data.struct.h" +#include "../DateTime.struct.h" +#include "../Serializer/SerializerNode.enum.h" #include "Indicator.enum.h" -#include "Indicator.struct.cache.h" -#include "SerializerNode.enum.h" -#include "Storage/ValueStorage.indicator.h" +#include "IndicatorData.struct.cache.h" +//#include "Indicator.struct.serialize.h" /* Structure for indicator parameters. */ struct IndicatorParams { @@ -162,54 +159,3 @@ struct IndicatorParams { // template <> SerializerNodeType Serialize(Serializer &s); }; - -/* Structure for indicator state. */ -struct IndicatorState { - public: // @todo: Change it to protected. - int handle; // Indicator handle (MQL5 only). - bool is_changed; // Set when params has been recently changed. - bool is_ready; // Set when indicator is ready (has valid values). - public: - enum ENUM_INDICATOR_STATE_PROP { - INDICATOR_STATE_PROP_HANDLE, - INDICATOR_STATE_PROP_IS_CHANGED, - INDICATOR_STATE_PROP_IS_READY, - }; - // Constructor. - IndicatorState() : handle(INVALID_HANDLE), is_changed(true), is_ready(false) {} - // Getters. - template - T Get(STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) _prop) { - switch (_prop) { - case INDICATOR_STATE_PROP_HANDLE: - return (T)handle; - case INDICATOR_STATE_PROP_IS_CHANGED: - return (T)is_changed; - case INDICATOR_STATE_PROP_IS_READY: - return (T)is_ready; - }; - SetUserError(ERR_INVALID_PARAMETER); - return (T)WRONG_VALUE; - } - // Setters. - template - void Set(STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) _prop, T _value) { - switch (_prop) { - case INDICATOR_STATE_PROP_HANDLE: - handle = (T)_value; - break; - case INDICATOR_STATE_PROP_IS_CHANGED: - is_changed = (T)_value; - break; - case INDICATOR_STATE_PROP_IS_READY: - is_ready = (T)_value; - break; - default: - SetUserError(ERR_INVALID_PARAMETER); - break; - }; - } - // State checkers. - bool IsChanged() { return is_changed; } - bool IsReady() { return is_ready; } -}; diff --git a/Indicator.struct.serialize.h b/Indicator/Indicator.struct.serialize.h similarity index 94% rename from Indicator.struct.serialize.h rename to Indicator/Indicator.struct.serialize.h index a3132c6a4..b71f4ede1 100644 --- a/Indicator.struct.serialize.h +++ b/Indicator/Indicator.struct.serialize.h @@ -25,10 +25,9 @@ * Includes Indicator's struct serializers. */ -#include "Serializer.mqh" - -// Forward class declaration. -class Serializer; +#include "../Serializer/Serializer.h" +#include "../Serializer/SerializerNode.enum.h" +#include "Indicator.struct.h" /* Method to serialize IndicatorParams structure. */ SerializerNodeType IndicatorParams::Serialize(Serializer &s) { diff --git a/IndicatorBase.h b/Indicator/IndicatorBase.h similarity index 89% rename from IndicatorBase.h rename to Indicator/IndicatorBase.h index db20447a6..6c1506c2c 100644 --- a/IndicatorBase.h +++ b/Indicator/IndicatorBase.h @@ -30,38 +30,26 @@ #pragma once #endif -// Forward declaration. -class Chart; - // Includes. -#include "Array.mqh" -#include "BufferStruct.mqh" -#include "Chart.mqh" -#include "Chart.struct.tf.h" -#include "ChartBase.h" -#include "ChartMt.h" -#include "DateTime.mqh" -#include "DrawIndicator.mqh" -#include "Flags.h" -#include "Indicator.define.h" -#include "Indicator.enum.h" -#include "Indicator.struct.cache.h" -#include "Indicator.struct.h" -#include "Indicator.struct.serialize.h" -#include "Log.mqh" -#include "Object.mqh" -#include "Refs.mqh" -#include "Serializer.mqh" -#include "SerializerCsv.mqh" -#include "SerializerJson.mqh" -#include "Util.h" +#include "../Array.mqh" +#include "../BufferStruct.mqh" +#include "../Chart.struct.tf.h" +//#include "../ChartBase.h" +#include "../ChartMt.h" +#include "../DateTime.mqh" +#include "../Log.mqh" +#include "../Object.mqh" +#include "../Refs.mqh" +#include "../Serializer/Serializer.h" +#include "../Serializer/SerializerCsv.h" +#include "../Serializer/SerializerJson.h" +#include "../Util.h" /** * Class to deal with indicators. */ class IndicatorBase : public Object { protected: - IndicatorState istate; Ref logger; public: @@ -87,7 +75,7 @@ class IndicatorBase : public Object { /** * Class deconstructor. */ - virtual ~IndicatorBase() { ReleaseHandle(); } + virtual ~IndicatorBase() {} /* Operator overloading methods */ @@ -168,14 +156,6 @@ class IndicatorBase : public Object { /* Getters */ - /** - * Gets an indicator's state property value. - */ - template - T Get(STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) _prop) { - return istate.Get(_prop); - } - /** * Returns logger. */ @@ -190,14 +170,6 @@ class IndicatorBase : public Object { /* Setters */ - /** - * Sets an indicator's state property value. - */ - template - void Set(STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) _prop, T _value) { - istate.Set(_prop, _value); - } - /** * Sets name of the indicator. */ @@ -215,25 +187,6 @@ class IndicatorBase : public Object { */ // void SetSymbol(string _symbol) { Set(CHART_PARAM_SYMBOL, _symbol); } - /* 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; - } - - /* Data representation methods */ - /* Virtual methods */ /** diff --git a/Indicator/IndicatorCandle.h b/Indicator/IndicatorCandle.h index eac367eb7..f8b7fdd0c 100644 --- a/Indicator/IndicatorCandle.h +++ b/Indicator/IndicatorCandle.h @@ -1,6 +1,6 @@ //+------------------------------------------------------------------+ //| EA31337 framework | -//| Copyright 2016-2023, EA31337 Ltd | +//| Copyright 2016-2021, EA31337 Ltd | //| https://github.com/EA31337 | //+------------------------------------------------------------------+ @@ -32,8 +32,7 @@ // Includes. #include "../Buffer/BufferCandle.h" #include "../Candle.struct.h" -#include "../Indicator.enum.h" -#include "../Indicator.mqh" +#include "../Storage/ItemsHistory.h" #include "../Storage/ValueStorage.price_median.h" #include "../Storage/ValueStorage.price_typical.h" #include "../Storage/ValueStorage.price_weighted.h" @@ -41,8 +40,15 @@ #include "../Storage/ValueStorage.tick_volume.h" #include "../Storage/ValueStorage.time.h" #include "../Storage/ValueStorage.volume.h" +#include "Indicator.h" +#include "IndicatorCandle.provider.h" +#include "IndicatorData.h" #include "TickBarCounter.h" +#ifndef INDI_CANDLE_HISTORY_SIZE +#define INDI_CANDLE_HISTORY_SIZE 86400 +#endif + // Indicator modes. enum ENUM_INDI_CANDLE_MODE { INDI_CANDLE_MODE_PRICE_OPEN, @@ -63,11 +69,12 @@ enum ENUM_INDI_CANDLE_MODE { /** * Class to deal with candle indicators. */ -template +template class IndicatorCandle : public Indicator { protected: BufferCandle icdata; TickBarCounter counter; + ItemsHistory, TCP> history; protected: /* Protected methods */ @@ -93,11 +100,11 @@ class IndicatorCandle : public Indicator { */ IndicatorCandle(const TS& _icparams, const IndicatorDataParams& _idparams, IndicatorBase* _indi_src = NULL, int _indi_mode = 0) - : Indicator(_icparams, _idparams, _indi_src, _indi_mode) { + : Indicator(_icparams, _idparams, _indi_src, _indi_mode), history(INDI_CANDLE_HISTORY_SIZE) { Init(); } IndicatorCandle(ENUM_INDICATOR_TYPE _itype = INDI_CANDLE, int _shift = 0, string _name = "") - : Indicator(_itype, _shift, _name) { + : Indicator(_itype, _shift, _name), history(INDI_CANDLE_HISTORY_SIZE) { Init(); } @@ -110,6 +117,11 @@ class IndicatorCandle : public Indicator { /* Getters */ + /** + * Returns buffer where candles are temporarily stored. + */ + ItemsHistory, TCP>* GetHistory() { return &history; } + /** * Gets open price for a given, optional shift. */ @@ -136,27 +148,62 @@ class IndicatorCandle : public Indicator { double GetPrice(ENUM_APPLIED_PRICE _ap, int _shift = 0) override { return GetOHLC(_shift).GetAppliedPrice(_ap); } /** - * Returns current bar index (incremented every OnTick() if IsNewBar() is true). + * Returns current bar index. */ - int GetBarIndex() override { return counter.GetBarIndex(); } + int GetBarIndex() override { return history.GetCurrentIndex(); } /** - * Returns the number of bars on the chart. + * Returns the number of bars on the chart decremented by iparams.shift. */ - int GetBars() override { return (int)icdata.Size(); } + int GetBars() override { + // Will return number of bars prepended and appended to the history, + // even if those bars were cleaned up because of history's candle limit. + return (int)history.GetPeakSize() - iparams.shift; + } /** * Returns current tick index (incremented every OnTick()). */ - int GetTickIndex() override { return counter.GetTickIndex(); } + int GetTickIndex() override { return GetTick() PTR_DEREF GetTickIndex(); } /** * Check if there is a new bar to parse. */ - bool IsNewBar() override { return counter.is_new_bar; } + bool IsNewBar() override { + CandleOCTOHLC _candle; + // We check if last bar has volume 1. If yes, that would mean that new candle was created with a single tick. In + // consecutive ticks the volume will be incremented. + if (history.TryGetItemByShift(0, _candle, false)) { + return _candle.volume == 1; + } + + // No candles means no new bar. + return false; + } /* Virtual method implementations */ + /** + * Removes candle from the buffer. Used mainly for testing purposes. + */ + void InvalidateCandle(int _abs_shift) { + if (_abs_shift != GetBarIndex()) { + Print( + "IndicatorCandle::InvalidateCandle() currently supports specyfing " + "current, absolute candle index and nothing else. You may retrieve current one by calling GetBarIndex()."); + DebugBreak(); + return; + } + + int _num_to_remove = GetBarIndex() - _abs_shift + 1; + history.RemoveRecentItems(_num_to_remove); + } + + /** + * Returns time of the bar for a given shift. + */ + virtual datetime GetBarTime(int _rel_shift = 0) { return history.GetItemTimeByShift(_rel_shift); } + /** * Traverses source indicators' hierarchy and tries to find OHLC-featured * indicator. IndicatorCandle satisfies such requirements. @@ -169,25 +216,15 @@ class IndicatorCandle : public Indicator { /** * Gets OHLC price values. */ - BarOHLC GetOHLC(int _shift = 0) override { - datetime _bar_time = GetBarTime(_shift); - BarOHLC _ohlc; - - if ((long)_bar_time != 0) { - CandleOCTOHLC candle = icdata.GetByKey((long)_bar_time); - _ohlc.open = (float)candle.open; - _ohlc.high = (float)candle.high; - _ohlc.low = (float)candle.low; - _ohlc.close = (float)candle.close; - _ohlc.time = _bar_time; - } + BarOHLC GetOHLC(int _rel_shift = 0) override { + BarOHLC _bar; + CandleOCTOHLC _candle; -#ifdef __debug_verbose__ - Print("Fetching OHLC #", _shift, " from ", TimeToString(_ohlc.time, TIME_DATE | TIME_MINUTES | TIME_SECONDS)); - Print("^- ", _ohlc.open, ", ", _ohlc.high, ", ", _ohlc.low, ", ", _ohlc.close); -#endif + if (history.TryGetItemByShift(ToAbsShift(_rel_shift), _candle)) { + _bar = BarOHLC(_candle.open, _candle.high, _candle.low, _candle.close, _candle.start_time); + } - return _ohlc; + return _bar; } /** @@ -196,14 +233,13 @@ class IndicatorCandle : public Indicator { * If local history is empty (not loaded), function returns 0. */ long GetVolume(int _shift = 0) override { - datetime _bar_time = GetBarTime(_shift); + CandleOCTOHLC _candle; - if ((long)_bar_time == 0) { - return 0; + if (history.TryGetItemByShift(_shift, _candle)) { + return _candle.volume; } - CandleOCTOHLC candle = icdata.GetByKey((long)_bar_time); - return candle.volume; + return 0; } /** @@ -228,22 +264,11 @@ class IndicatorCandle : public Indicator { * @return * Returns IndicatorDataEntry struct filled with indicator values. */ - IndicatorDataEntry GetEntry(long _index = -1) override { + IndicatorDataEntry GetEntry(int _shift = 0) override { ResetLastError(); - int _ishift = _index >= 0 ? (int)_index : iparams.GetShift(); - long _candle_time = GetBarTime(_ishift); - CandleOCTOHLC _candle; - _candle = icdata.GetByKey(_candle_time); - - if (!_candle.IsValid()) { - // No candle found. - DebugBreak(); - Print(GetFullName(), ": Missing candle at shift ", _index, " (", - TimeToString(_candle_time, TIME_DATE | TIME_MINUTES | TIME_SECONDS), "). Lowest timestamp in history is ", - icdata.GetMin()); - } - - return CandleToEntry(_candle_time, _candle); + int _ishift = _shift + iparams.GetShift(); + CandleOCTOHLC _candle = history.GetItemByShift(_ishift); + return CandleToEntry(_candle.GetTime(), _candle); } /** @@ -294,10 +319,10 @@ class IndicatorCandle : public Indicator { */ static bool IndicatorCandleOverflowListener(ENUM_DICT_OVERFLOW_REASON _reason, int _size, int _num_conflicts) { switch (_reason) { - case DICT_OVERFLOW_REASON_FULL: + case DICT_LISTENER_FULL_CAN_RESIZE: // We allow resize if dictionary size is less than 86400 slots. return _size < 86400; - case DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS: + case DICT_LISTENER_CONFLICTS_CAN_OVERWRITE: default: // When there is too many conflicts, we just reject doing resize, so first conflicting slot will be reused. break; @@ -311,7 +336,7 @@ class IndicatorCandle : public Indicator { void EmitHistory() override { for (DictStructIterator> iter(icdata.Begin()); iter.IsValid(); ++iter) { IndicatorDataEntry _entry = CandleToEntry(iter.Key(), iter.Value()); - EmitEntry(_entry, INDI_EMITTED_ENTRY_TYPE_CANDLE); + EmitEntry(_entry); } } @@ -341,67 +366,14 @@ class IndicatorCandle : public Indicator { } /** - * Adds tick's price to the matching candle and updates its OHLC values. - */ - CandleOCTOHLC UpdateCandle(long _tick_timestamp, double _price) { - long _candle_timestamp = CalcCandleTimestamp(_tick_timestamp); - -#ifdef __debug_verbose__ - Print("Updating candle for ", GetFullName(), " at candle ", - TimeToString(_candle_timestamp, TIME_DATE | TIME_MINUTES | TIME_SECONDS), " from tick at ", - TimeToString(_tick_timestamp, TIME_DATE | TIME_MINUTES | TIME_SECONDS), ": ", _price); -#endif - - CandleOCTOHLC _candle(_price, _price, _price, _price, _tick_timestamp, _tick_timestamp); - if (icdata.KeyExists(_candle_timestamp)) { - // Candle already exists. - _candle = icdata.GetByKey(_candle_timestamp); - -#ifdef __debug_verbose__ - Print("Candle was ", _candle.ToCSV()); -#endif - - _candle.Update(_tick_timestamp, _price); - -#ifdef __debug_verbose__ - Print("Candle is ", _candle.ToCSV()); -#endif - } - - icdata.Add(_candle, _candle_timestamp); - - return _candle; - } - - /** - * Calculates candle's timestamp from tick's timestamp. - */ - long CalcCandleTimestamp(long _tick_timestamp) { - return _tick_timestamp - _tick_timestamp % (iparams.GetSecsPerCandle()); - } - - /** - * Called when data source emits new entry (historic or future one). + * Called when data source emits new entry (new one in ascending order). */ - void OnDataSourceEntry(IndicatorDataEntry& entry, - ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT) override { - Indicator::OnDataSourceEntry(entry, type); - - if (type != INDI_EMITTED_ENTRY_TYPE_TICK) { - return; - } - - long _candle_timestamp = CalcCandleTimestamp(entry.timestamp); - - // Updating tick & bar indices. - counter.OnTick(_candle_timestamp); - - // Updating candle from bid price. - CandleOCTOHLC _candle = UpdateCandle(entry.timestamp, entry[1]); - - // Emitting candle for children. - IndicatorDataEntry _candle_entry = CandleToEntry(_candle_timestamp, _candle); - EmitEntry(_candle_entry, INDI_EMITTED_ENTRY_TYPE_CANDLE); + void OnDataSourceEntry(IndicatorDataEntry& entry) override { + // Parent indicator (e.g., Indi_TickMt) emitted an entry containing tick's + // ask and bid price. As an abstract class, we really don't know how to + // update/create candles so we just pass the entry into history's + // ItemsHistoryCandleProvider and it will do all the job. + history.GetItemProvider() PTR_DEREF OnTick(&history, entry.timestamp * 1000, (float)entry[0], (float)entry[1]); }; /** @@ -461,11 +433,7 @@ class IndicatorCandle : public Indicator { } string CandlesToString() { - string _result; - for (DictStructIterator> iter(icdata.Begin()); iter.IsValid(); ++iter) { - IndicatorDataEntry _entry = CandleToEntry(iter.Key(), iter.Value()); - _result += IntegerToString(iter.Key()) + ": " + _entry.ToString() + "\n"; - } + string _result = "CandlesToString() not yet implemented!"; return _result; } diff --git a/Indicator/IndicatorCandle.provider.h b/Indicator/IndicatorCandle.provider.h new file mode 100644 index 000000000..b6737c945 --- /dev/null +++ b/Indicator/IndicatorCandle.provider.h @@ -0,0 +1,67 @@ +//+------------------------------------------------------------------+ +//| 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 . + * + */ + +// Ignore processing of this file if already included. +#ifndef INDICATOR_CANDLE_PROVIDER_H +#define INDICATOR_CANDLE_PROVIDER_H + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Candle.struct.h" +#include "../Storage/ItemsHistory.h" + +/** + * Regenerates candles and updates exising candles from new ticks. Subclasses by IndicatorTf, IndicatorRenko. + */ +template +class ItemsHistoryCandleProvider : public ItemsHistoryItemProvider> { + public: + /** + * Constructor. + */ + ItemsHistoryCandleProvider() {} + + /** + * Called when new tick was emitted from IndicatorTick-based source. + */ + virtual void OnTick(ItemsHistory, ItemsHistoryItemProvider>>* _history, + long _time_ms, float _ask, float _bid) { + // Should be overrided. + } + + /** + * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we + * want previous or next items from selected starting point. + */ + void GetItems(ItemsHistory, ItemsHistoryCandleProvider>* _history, long _from_time_ms, + ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(CandleOCTOHLC, _out_arr)) { + // Method is called if there is a missing item (candle) in the history. We need to regenerate it. + Print("Error: Retrieving items by this item provider is not implemented!"); + DebugBreak(); + } +}; + +#endif diff --git a/Indicator/IndicatorData.enum.h b/Indicator/IndicatorData.enum.h new file mode 100644 index 000000000..ef7414643 --- /dev/null +++ b/Indicator/IndicatorData.enum.h @@ -0,0 +1,117 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2023, 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 IndicatorData's enums. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +/* Indicator entry flags. */ +enum INDICATOR_ENTRY_FLAGS { + INDI_ENTRY_FLAG_NONE = 0 << 0, + INDI_ENTRY_FLAG_IS_BITWISE = 1 << 0, + INDI_ENTRY_FLAG_IS_DOUBLED = 1 << 1, // Type is doubled in size (e.g. double or long). + INDI_ENTRY_FLAG_IS_EXPIRED = 1 << 2, + INDI_ENTRY_FLAG_IS_REAL = 1 << 3, // Type is real (float or double). + INDI_ENTRY_FLAG_IS_PRICE = 1 << 4, + INDI_ENTRY_FLAG_IS_UNSIGNED = 1 << 5, // Type is unsigned (unsigned int or unsigned long). + INDI_ENTRY_FLAG_IS_VALID = 1 << 6, + INDI_ENTRY_FLAG_INSUFFICIENT_DATA = 1 << 7, // Entry has missing value for that shift and probably won't ever have. +}; + +/* Define indicator index. */ +enum ENUM_INDICATOR_INDEX { + CURR = 0, + PREV = 1, + PPREV = 2, + FINAL_ENUM_INDICATOR_INDEX = 3 // Should be the last one. Used to calculate the number of enum items. +}; + +// Storage type for IndicatorBase::GetSpecificValueStorage(). +enum ENUM_INDI_VS_TYPE { + INDI_VS_TYPE_NONE, // Not set. + INDI_VS_TYPE_TIME, // Candle. + INDI_VS_TYPE_TICK_VOLUME, // Candle. + INDI_VS_TYPE_VOLUME, // Candle. + INDI_VS_TYPE_SPREAD, // Candle. + INDI_VS_TYPE_PRICE_OPEN, // Candle. + INDI_VS_TYPE_PRICE_HIGH, // Candle. + INDI_VS_TYPE_PRICE_LOW, // Candle. + INDI_VS_TYPE_PRICE_CLOSE, // Candle. + INDI_VS_TYPE_PRICE_MEDIAN, // Candle. + INDI_VS_TYPE_PRICE_TYPICAL, // Candle. + INDI_VS_TYPE_PRICE_WEIGHTED, // Candle. + INDI_VS_TYPE_PRICE_BID, // Tick. + INDI_VS_TYPE_PRICE_ASK, // Tick. + // Indexed value storages, available if indicator have buffer at this index: + INDI_VS_TYPE_INDEX_0, + INDI_VS_TYPE_INDEX_1, + INDI_VS_TYPE_INDEX_2, + INDI_VS_TYPE_INDEX_4, + INDI_VS_TYPE_INDEX_5, + INDI_VS_TYPE_INDEX_6, + INDI_VS_TYPE_INDEX_7, + INDI_VS_TYPE_INDEX_8, + INDI_VS_TYPE_INDEX_9, + INDI_VS_TYPE_INDEX_FIRST = INDI_VS_TYPE_INDEX_0, + INDI_VS_TYPE_INDEX_LAST = INDI_VS_TYPE_INDEX_9, + + // Account Stats. + INDI_VS_TYPE_ACCOUNT_STATS_DATE_TIME, + INDI_VS_TYPE_ACCOUNT_STATS_BALANCE, + INDI_VS_TYPE_ACCOUNT_STATS_CREDIT, + INDI_VS_TYPE_ACCOUNT_STATS_EQUITY, + INDI_VS_TYPE_ACCOUNT_STATS_PROFIT, + INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_USED, + INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_FREE, + INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_AVAIL, + INDI_VS_TYPE_ACCOUNT_STATS_INDEX_FIRST = INDI_VS_TYPE_ACCOUNT_STATS_DATE_TIME, + INDI_VS_TYPE_ACCOUNT_STATS_INDEX_LAST = INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_AVAIL, +}; + +/* Defines type of source data for. Also used for Indicator::GetPossibleDataModes(). */ +enum ENUM_IDATA_SOURCE_TYPE { + IDATA_BUILTIN = 1 << 0, // Platform built-in + IDATA_CHART = 1 << 1, // Chart calculation + IDATA_ICUSTOM = 1 << 2, // iCustom: Custom indicator file + IDATA_ICUSTOM_LEGACY = 1 << 3, // iCustom: Custom, legacy, provided by MT indicator file + IDATA_INDICATOR = 1 << 4, // OnIndicator: Another indicator as a source of data + IDATA_ONCALCULATE = 1 << 5, // OnCalculate: Custom calculation function + IDATA_MATH = 1 << 6 // Math-based indicator +}; + +/* Defines range value data type for indicator storage. */ +enum ENUM_IDATA_VALUE_RANGE { + IDATA_RANGE_BINARY, // E.g. 0 or 1. + IDATA_RANGE_BITWISE, // Bitwise + IDATA_RANGE_MIXED, + IDATA_RANGE_PRICE, // Values represent price. + IDATA_RANGE_PRICE_DIFF, // Values represent price differences. + IDATA_RANGE_PRICE_ON_SIGNAL, // Values represent price on signal, otherwise zero. + IDATA_RANGE_RANGE, // E.g. 0 to 100. + IDATA_RANGE_UNKNOWN +}; diff --git a/IndicatorData.mqh b/Indicator/IndicatorData.h similarity index 86% rename from IndicatorData.mqh rename to Indicator/IndicatorData.h index e9d253f38..4d69e8c41 100644 --- a/IndicatorData.mqh +++ b/Indicator/IndicatorData.h @@ -20,15 +20,34 @@ * */ +// Ignore processing of this file if already included. +#ifndef INDICATOR_DATA_H +#define INDICATOR_DATA_H + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Forward class declaration. +class IndicatorBase; + // Includes. +#include "../Bar.struct.h" +#include "../DrawIndicator.mqh" +#include "../Flags.h" +#include "../Storage/ItemsHistory.h" +#include "../Storage/ValueStorage.h" +#include "../Storage/ValueStorage.indicator.h" +#include "../Storage/ValueStorage.native.h" +#include "../SymbolInfo.struct.h" +#include "Indicator.enum.h" #include "IndicatorBase.h" #include "IndicatorData.enum.h" +#include "IndicatorData.struct.cache.h" #include "IndicatorData.struct.h" #include "IndicatorData.struct.serialize.h" #include "IndicatorData.struct.signal.h" -#include "Storage/ValueStorage.h" -#include "Storage/ValueStorage.indicator.h" -#include "Storage/ValueStorage.native.h" /** * Implements class to store indicator data. @@ -38,10 +57,11 @@ class IndicatorData : public IndicatorBase { // Class variables. bool do_draw; bool indicator_builtin; - bool is_fed; // Whether calc_start_bar is already calculated. - int calc_start_bar; // Index of the first valid bar (from 0). - int flags; // Flags such as INDI_FLAG_INDEXABLE_BY_SHIFT. - long last_tick_time; // Time of the last Tick() call. + bool is_fed; // Whether calc_start_bar is already calculated. + int calc_start_bar; // Index of the first valid bar (from 0). + int flags; // Flags such as INDI_FLAG_INDEXABLE_BY_SHIFT. + int last_tick_index; // Index of the last tick. + long first_tick_time_ms; // Time of the first ask/bid tick. void* mydata; ENUM_INDI_VS_TYPE retarget_ap_av; // Value storage type to be used as applied price/volume. ARRAY(Ref, value_storages); @@ -51,13 +71,14 @@ class IndicatorData : public IndicatorBase { DrawIndicator* draw; IndicatorCalculateCache cache; IndicatorDataParams idparams; // Indicator data params. - Ref indi_src; // Indicator used as data source. + IndicatorState istate; + Ref indi_src; // Indicator used as data source. protected: /* Protected methods */ bool Init() { - ArrayResize(value_storages, idparams.Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_MAX_MODES))); + ArrayResize(value_storages, GetModeCount()); if (indi_src.IsSet()) { // SetDataSource(_indi_src, _indi_mode); idparams.Set(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE), IDATA_INDICATOR); @@ -68,7 +89,7 @@ class IndicatorData : public IndicatorBase { case IDATA_ICUSTOM: break; case IDATA_INDICATOR: - if (indi_src.IsSet()) { + if (indi_src.IsSet() == NULL) { // Indi_Price* _indi_price = Indi_Price::GetCached(GetSymbol(), GetTf(), iparams.GetShift()); // SetDataSource(_indi_price, true, PRICE_OPEN); } @@ -77,7 +98,7 @@ class IndicatorData : public IndicatorBase { // By default, indicator is indexable only by shift and data source must be also indexable by shift. flags = INDI_FLAG_INDEXABLE_BY_SHIFT | INDI_FLAG_SOURCE_REQ_INDEXABLE_BY_SHIFT; calc_start_bar = 0; - last_tick_time = 0; + last_tick_index = -1; retarget_ap_av = INDI_VS_TYPE_NONE; InitDraw(); return true; @@ -87,11 +108,14 @@ class IndicatorData : public IndicatorBase { * Initialize indicator data drawing on custom data. */ bool InitDraw() { - if (idparams.is_draw && !Object::IsValid(draw)) { - draw = new DrawIndicator(THIS_PTR); - draw.SetColorLine(idparams.indi_color); - } - return idparams.is_draw; + /* @todo: To refactor. + if (idparams.is_draw && !Object::IsValid(draw)) { + draw = new DrawIndicator(THIS_PTR); + draw.SetColorLine(idparams.indi_color); + } + return idparams.is_draw; + */ + return false; } /** @@ -121,37 +145,27 @@ class IndicatorData : public IndicatorBase { /** * Class deconstructor. */ - virtual ~IndicatorData() { DeinitDraw(); } + virtual ~IndicatorData() { + DeinitDraw(); + ReleaseHandle(); + } /* Operator overloading methods */ /** * Access indicator entry data using [] operator via shift. */ - IndicatorDataEntry operator[](int _index) { + IndicatorDataEntry operator[](int _rel_shift) { if (!bool(flags | INDI_FLAG_INDEXABLE_BY_SHIFT)) { Print(GetFullName(), " is not indexable by shift!"); DebugBreak(); IndicatorDataEntry _default; return _default; } - return GetEntry(_index); - } - - /** - * Access indicator entry data using [] operator via datetime. - */ - IndicatorDataEntry operator[](datetime _dt) { - if (!bool(flags | INDI_FLAG_INDEXABLE_BY_TIMESTAMP)) { - Print(GetFullName(), " is not indexable by timestamp!"); - DebugBreak(); - IndicatorDataEntry _default; - return _default; - } - return GetEntry(_dt); + return GetEntry(_rel_shift); } - IndicatorDataEntry operator[](ENUM_INDICATOR_INDEX _index) { return GetEntry((int)_index); } + IndicatorDataEntry operator[](ENUM_INDICATOR_INDEX _rel_shift) { return GetEntry((int)_rel_shift); } /* Getters */ @@ -174,8 +188,8 @@ class IndicatorData : public IndicatorBase { /** * Gets an indicator property flag. */ - bool GetFlag(INDICATOR_ENTRY_FLAGS _prop, int _shift = 0) { - IndicatorDataEntry _entry = GetEntry(_shift); + bool GetFlag(INDICATOR_ENTRY_FLAGS _prop, int _rel_shift = 0) { + IndicatorDataEntry _entry = GetEntry(_rel_shift); return _entry.CheckFlag(_prop); } @@ -184,6 +198,12 @@ class IndicatorData : public IndicatorBase { */ int GetFlags() { return flags; } + /** + * Returns time of the first ask/bid tick (time of first global OnTick()). + * Time is compatible with time generated by IndicatorTick, e.g., Indi_TickMt. + */ + long GetFirstTickTimeMs() { return first_tick_time_ms; } + /** * Get full name of the indicator (with "over ..." part). */ @@ -460,11 +480,11 @@ class IndicatorData : public IndicatorBase { /** * Get current (or by given date and time) open price depending on the operation type. */ - double GetOpenOffer(ENUM_ORDER_TYPE _cmd, datetime _dt = 0) { + double GetOpenOffer(ENUM_ORDER_TYPE _cmd) { // Use the right open price at opening of a market order. For example: // - When selling, only the latest Bid prices can be used. // - When buying, only the latest Ask prices can be used. - return _cmd == ORDER_TYPE_BUY ? GetAsk(_dt) : GetBid(_dt); + return _cmd == ORDER_TYPE_BUY ? GetAsk() : GetBid(); } /** @@ -532,31 +552,7 @@ class IndicatorData : public IndicatorBase { /* Getters */ - int GetBarsCalculated(ENUM_TIMEFRAMES _tf = NULL) { - int _bars = Bars(GetSymbol(), _tf); - - if (!idparams.Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IS_FED))) { - // Calculating start_bar. - for (; calc_start_bar < _bars; ++calc_start_bar) { - // Iterating from the oldest or previously iterated. - IndicatorDataEntry _entry = GetEntry(_bars - calc_start_bar - 1); - - if (_entry.IsValid()) { - // From this point we assume that future entries will be all valid. - idparams.Set(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IS_FED), true); - return _bars - calc_start_bar; - } - } - } - - if (!idparams.Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IS_FED))) { - Print("Can't find valid bars for ", GetFullName()); - return 0; - } - - // Assuming all entries are calculated (even if have invalid values). - return _bars; - } + int GetBarsCalculated() { return GetBars(); } /** * Returns buffers' cache. @@ -701,11 +697,7 @@ class IndicatorData : public IndicatorBase { SetDataSource(OnDataSourceRequest()); } - IndicatorData* _ptr = GetDataSourceRaw(); - - bool _result = _ptr != nullptr; - - return _result; + return GetDataSourceRaw() != nullptr; } /** @@ -732,7 +724,7 @@ class IndicatorData : public IndicatorBase { /** * Checks whether indicator have given mode (max_modes is greater that given mode). */ - bool HasValueStorage(int _mode = 0) { return _mode < GetModeCount(); } + bool HasValueStorage(unsigned int _mode = 0) { return _mode < GetModeCount(); } /** * Whether we can and have to select mode when specifying data source. @@ -765,6 +757,14 @@ class IndicatorData : public IndicatorBase { idparams.Set(_param, _value); } + /** + * Sets an indicator's state property value. + */ + template + void Set(STRUCT_ENUM_INDICATOR_STATE_PROP _prop, T _value) { + istate.Set(_prop, _value); + } + /** * Sets indicator data source. */ @@ -806,7 +806,7 @@ class IndicatorData : public IndicatorBase { indi_src.Ptr().RemoveListener(THIS_PTR); } indi_src = _indi; - if (_indi != nullptr) { + if (_indi != NULL) { indi_src.Ptr().AddListener(THIS_PTR); Set(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_SRC_ID), -1); Set(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_SRC_MODE), _input_mode); @@ -855,29 +855,32 @@ class IndicatorData : public IndicatorBase { HasSpecificValueStorage(INDI_VS_TYPE_TICK_VOLUME); } - void Tick() { - long _current_time = TimeCurrent(); - - if (last_tick_time == _current_time) { + void Tick(int _global_tick_index) { + if (last_tick_index == _global_tick_index) { // We've already ticked. return; } - last_tick_time = _current_time; + if (_global_tick_index == 0) { + // Time of the first tick must be compatible with time generated by IndicatorTick, e.g., Indi_TickMt. + first_tick_time_ms = TimeCurrent() * 1000; + } + + last_tick_index = _global_tick_index; // Checking and potentially initializing new data source. - if (HasDataSource(true)) { + if (HasDataSource(true) != NULL) { // Ticking data source if not yet ticked. - GetDataSource().Tick(); + GetDataSource().Tick(_global_tick_index); } // Also ticking all used indicators if they've not yet ticked. for (DictStructIterator> iter = indicators.Begin(); iter.IsValid(); ++iter) { - iter.Value().Ptr().Tick(); + iter.Value().Ptr().Tick(_global_tick_index); } // Overridable OnTick() method. - OnTick(); + OnTick(_global_tick_index); } /** @@ -909,13 +912,13 @@ class IndicatorData : public IndicatorBase { * Loads and validates built-in indicators whose can be used as data source. */ void ValidateDataSource(IndicatorData* _target, IndicatorData* _source) { - if (_target == nullptr) { + if (_target == NULL) { Alert("Internal Error! _target is NULL in ", __FUNCTION_LINE__, "."); DebugBreak(); return; } - if (_source == nullptr) { + if (_source == NULL) { Alert("Error! You have to select source indicator's via SetDataSource()."); DebugBreak(); return; @@ -976,6 +979,23 @@ class IndicatorData : public IndicatorBase { } } + /* Handle 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; + } + /* Printers */ /** @@ -993,9 +1013,9 @@ class IndicatorData : public IndicatorBase { } template - T GetValue(int _mode = 0, int _index = 0) { + T GetValue(int _mode = 0, int _rel_shift = 0) { T _out; - GetEntryValue(_mode, _index).Get(_out); + GetEntryValue(_mode, ToAbsShift(_rel_shift)).Get(_out); return _out; } @@ -1078,12 +1098,17 @@ class IndicatorData : public IndicatorBase { } /** - * Gets ask price for a given date and time. Return current ask price if _dt wasn't passed or is 0. + * Gets ask price for a given shift. Return current ask price if _shift wasn't passed or is 0. + */ + virtual double GetAsk(int _shift = 0) { return GetTick() PTR_DEREF GetAsk(_shift); } + + /** + * Gets bid price for a given shift. Return current bid price if _shift wasn't passed or is 0. */ - virtual double GetAsk(datetime _dt = 0) { return GetTick() PTR_DEREF GetAsk(_dt); } + virtual double GetBid(int _shift = 0) { return GetTick() PTR_DEREF GetBid(_shift); } /** - * Returns the number of bars on the chart. + * Returns the number of bars on the chart decremented by iparams.shift. */ virtual int GetBars() { return GetCandle() PTR_DEREF GetBars(); } @@ -1095,7 +1120,23 @@ class IndicatorData : public IndicatorBase { /** * Returns time of the bar for a given shift. */ - virtual datetime GetBarTime(int _shift = 0) { return GetCandle() PTR_DEREF GetBarTime(_shift); } + virtual datetime GetBarTime(int _rel_shift = 0) { + IndicatorData* _indi = GetCandle(false); + + if (_indi == nullptr) _indi = GetTick(false); + + if (_indi == nullptr) { + Print("Error: Neither candle nor tick indicator exists in the hierarchy of ", GetFullName(), "!"); + DebugBreak(); + return (datetime)0; + } + +#ifdef __debug_items_history__ + Print("Getting bar time for shift ", _rel_shift, " for ", GetFullName()); +#endif + + return _indi PTR_DEREF GetBarTime(_rel_shift); + } /** * Search for a bar by its time. @@ -1106,11 +1147,6 @@ class IndicatorData : public IndicatorBase { return GetTick() PTR_DEREF GetBarShift(_time, _exact); } - /** - * Gets bid price for a given date and time. Return current bid price if _dt wasn't passed or is 0. - */ - virtual double GetBid(datetime _dt = 0) { return GetTick() PTR_DEREF GetBid(_dt); } - /** * Traverses source indicators' hierarchy and tries to find OHLC-featured * indicator. IndicatorCandle satisfies such requirements. @@ -1150,12 +1186,12 @@ class IndicatorData : public IndicatorBase { /** * Returns the indicator's struct value via index. */ - virtual IndicatorDataEntry GetEntry(long _index = 0) = 0; + virtual IndicatorDataEntry GetEntry(int _rel_shift = 0) = nullptr; /** * Returns the indicator's struct value via timestamp. */ - // virtual IndicatorDataEntry GetEntry(datetime _dt) = NULL; + // virtual IndicatorDataEntry GetEntry(datetime _dt) = nullptr; /** * Gets high price for a given, optional shift. @@ -1175,7 +1211,7 @@ class IndicatorData : public IndicatorBase { /** * Returns the indicator's entry value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = 0) = 0; + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) = nullptr; /** * Returns the shift of the maximum value over a specific number of periods depending on type. @@ -1204,7 +1240,9 @@ class IndicatorData : public IndicatorBase { /** * Gets number of modes available to retrieve by GetValue(). */ - virtual int GetModeCount() { return 0; } + virtual unsigned int GetModeCount() { + return idparams.Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_MAX_MODES)); + } /** * Get name of the indicator. @@ -1219,7 +1257,7 @@ class IndicatorData : public IndicatorBase { /** * Gets OHLC price values. */ - virtual BarOHLC GetOHLC(int _shift = 0) { return GetCandle() PTR_DEREF GetOHLC(_shift); } + virtual BarOHLC GetOHLC(int _rel_shift = 0) { return GetCandle() PTR_DEREF GetOHLC(_rel_shift); } /** * Get peak price at given number of bars. @@ -1233,8 +1271,8 @@ class IndicatorData : public IndicatorBase { /** * Returns the current price value given applied price type, symbol and timeframe. */ - virtual double GetPrice(ENUM_APPLIED_PRICE _ap, int _shift = 0) { - return GetCandle() PTR_DEREF GetPrice(_ap, _shift); + virtual double GetPrice(ENUM_APPLIED_PRICE _ap, int _rel_shift = 0) { + return GetCandle() PTR_DEREF GetPrice(_ap, _rel_shift); } /** @@ -1242,7 +1280,7 @@ class IndicatorData : public IndicatorBase { * * When indicator values are not valid, returns empty signals. */ - virtual IndicatorSignal GetSignals(int _count = 3, int _shift = 0, int _mode1 = 0, int _mode2 = 0) = 0; + virtual IndicatorSignal GetSignals(int _count = 3, int _shift = 0, int _mode1 = 0, int _mode2 = 0) = nullptr; /** * Returns spread for the bar. @@ -1388,6 +1426,98 @@ class IndicatorData : public IndicatorBase { */ virtual long GetTickVolume(int _shift = 0) { return GetCandle() PTR_DEREF GetTickVolume(_shift); } + /** + * Removes candle from the buffer. Used mainly for testing purposes. + */ + virtual void InvalidateCandle(int _abs_shift = 0) { GetCandle() PTR_DEREF InvalidateCandle(_abs_shift); } + + /** + * Fetches historic ticks for a given time range. + */ + virtual bool FetchHistoryByTimeRange(long _from_ms, long _to_ms, ARRAY_REF(TickTAB, _out_ticks)) { + return false; + } + + /** + * Fetches historic ticks for a given start time and minimum number of tick to retrieve. + */ + virtual bool FetchHistoryByStartTimeAndCount(long _from_ms, ENUM_ITEMS_HISTORY_DIRECTION _dir, int _min_count, + ARRAY_REF(TickTAB, _out_ticks)) { + // Print("FetchHistoryByStartTimeAndCount:"); + // Print("- Requested _from_ms = ", _from_ms, ", _dir = ", EnumToString(_dir), ", _min_count = ", _min_count); + + ArrayResize(_out_ticks, 0); + + // Number of ticks still to retrieve to satisfy the caller. + int _num_to_retrieve = _min_count, i, o; + + // Ticks per fixed time range. + static ARRAY(TickTAB, _recv_ticks); + + // Time-frames for which we'll be receiving ticks. + long _recv_range_ms = 1000 * 60 * 30; // 30 min time-frames. + + // Calculating initial time frame. + if (_dir == ITEMS_HISTORY_DIRECTION_BACKWARD) { + // _from_ms will be at start of previous time-frame. + _from_ms -= _recv_range_ms - 1; + } + // _to_ms will be at the last ms of _from_ms's timeframe. + long _to_ms = _from_ms + _recv_range_ms - 1; + + // Print("- Initial _from_ms = ", _from_ms, "_to_ms = ", _to_ms); + + do { + bool _success = FetchHistoryByTimeRange(_from_ms, _to_ms, _recv_ticks); + + int _num_ticks_before = ArraySize(_out_ticks); + int _num_received = ArraySize(_recv_ticks); + + // Our _out_tick must fit additional received ticks. + ArrayResize(_out_ticks, _num_ticks_before + _num_received); + + if (_dir == ITEMS_HISTORY_DIRECTION_BACKWARD) { + // Moving output ticks from the beginning to the end. + // i = input index, o = output index. + for (i = 0, o = _num_ticks_before; i < _num_received; i++, o++) { + _out_ticks[o] = _out_ticks[i]; + } + } + + for (i = 0; i < ArraySize(_recv_ticks); ++i) { + if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { + // Pushing received ticks at the end of the output ticks. + ArrayPushObject(_out_ticks, _recv_ticks[i]); + } else { + // Filling the beginning of the output ticks with received ticks. + _out_ticks[i] = _recv_ticks[i]; + } + } + + if (!_success) { + // An error happended; + break; + } + + _num_to_retrieve -= _num_received; + + if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { + // Going to the next time-frame. + _from_ms += _recv_range_ms; + _to_ms += _recv_range_ms; + } else { + // Going to the previous time-frame. + _from_ms -= _recv_range_ms; + _to_ms -= _recv_range_ms; + } + + } while (_LastError != 0 && _num_to_retrieve > 0); + + // _num_to_retrieve may be negative and it's perfectly fine. We'll just have more ticks than we wanted in the output + // array. + return _num_to_retrieve <= 0; + } + /** * Returns value storage of given kind. */ @@ -1620,10 +1750,10 @@ class IndicatorData : public IndicatorBase { /** * Sends entry to listening indicators. */ - void EmitEntry(IndicatorDataEntry& entry, ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT) { + void EmitEntry(IndicatorDataEntry& entry) { for (int i = 0; i < ArraySize(listeners); ++i) { if (listeners[i].ObjectExists()) { - listeners[i].Ptr().OnDataSourceEntry(entry, type); + listeners[i].Ptr().OnDataSourceEntry(entry); } } } @@ -1687,13 +1817,12 @@ class IndicatorData : public IndicatorBase { /** * Called when data source emits new entry (historic or future one). */ - virtual void OnDataSourceEntry(IndicatorDataEntry& entry, - ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT) { - // Sending entry to all chilren listeners (from highest parent to lowest child). - EmitEntry(entry, type); - }; + virtual void OnDataSourceEntry(IndicatorDataEntry& entry){}; - virtual void OnTick() {} + /** + * Called when new tick is retrieved from attached data source. + */ + virtual void OnTick(int _global_tick_index) {} /** * Called if data source is requested, but wasn't yet set. May be used to initialize indicators that must operate on @@ -1704,7 +1833,7 @@ class IndicatorData : public IndicatorBase { " without explicitly selecting an indicator, ", GetFullName(), " must override OnDataSourceRequest() method and return new instance of data source to be used by default."); DebugBreak(); - return nullptr; + return NULL; } /** @@ -1712,7 +1841,7 @@ class IndicatorData : public IndicatorBase { */ virtual IndicatorData* DataSourceRequestReturnDefault(int _applied_price) { DebugBreak(); - return nullptr; + return NULL; } /** @@ -1730,9 +1859,9 @@ class IndicatorData : public IndicatorBase { virtual void SetSymbolProps(const SymbolInfoProp& _props) {} /** - * Stores entry in the buffer for later rerieval. + * Appends given entry into the history. */ - virtual void StoreEntry(IndicatorDataEntry& entry) {} + virtual void AppendEntry(IndicatorDataEntry& entry) {} /** * Update indicator. @@ -1742,6 +1871,16 @@ class IndicatorData : public IndicatorBase { return false; }; + /** + * Converts relative shift into absolute one. + */ + virtual int ToAbsShift(int _rel_shift) = 0; + + /** + * Converts absolute shift into relative one. + */ + virtual int ToRelShift(int _abs_shift) = 0; + /** * Loads and validates built-in indicators whose can be used as data source. */ @@ -1783,3 +1922,5 @@ int CopyBuffer(IndicatorData* _indi, int _mode, int _start, int _count, ValueSto return _num_copied; } + +#endif // INDICATOR_DATA_H diff --git a/Indicator.struct.cache.h b/Indicator/IndicatorData.struct.cache.h similarity index 96% rename from Indicator.struct.cache.h rename to Indicator/IndicatorData.struct.cache.h index 8dac31dac..dba8e9a8e 100644 --- a/Indicator.struct.cache.h +++ b/Indicator/IndicatorData.struct.cache.h @@ -31,8 +31,8 @@ #endif // Includes. -#include "Refs.mqh" -#include "Storage/ValueStorage.h" +#include "../Refs.mqh" +#include "../Storage/ValueStorage.h" /** * Holds buffers used to cache values calculated via OnCalculate methods. @@ -234,7 +234,9 @@ class IndicatorCalculateCache : public Dynamic { } /** + * Retrieves _shift-th (0 = most recent) cached value from the given buffer. * + * @todo Return DBL_MAX in case the index is out of array boundary. */ template D GetTailValue(int _buffer_index, int _shift) { diff --git a/IndicatorData.struct.h b/Indicator/IndicatorData.struct.h similarity index 89% rename from IndicatorData.struct.h rename to Indicator/IndicatorData.struct.h index 451cbd770..61d55d72d 100644 --- a/IndicatorData.struct.h +++ b/Indicator/IndicatorData.struct.h @@ -27,9 +27,11 @@ // Defines. #define STRUCT_ENUM_IDATA_PARAM STRUCT_ENUM(IndicatorDataParams, ENUM_IDATA_PARAM) +#define STRUCT_ENUM_INDICATOR_STATE_PROP STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) // Includes. -#include "SerializerNode.enum.h" +#include "../Serializer/SerializerNode.enum.h" +#include "IndicatorData.enum.h" // Type-less value for IndicatorDataEntryValue structure. union IndicatorDataEntryTypelessValue { @@ -528,3 +530,54 @@ struct IndicatorDataParams { } void SetIndicatorColor(color _clr) { indi_color = _clr; } }; + +/* Structure for indicator state. */ +struct IndicatorState { + public: // @todo: Change it to protected. + int handle; // Indicator handle (MQL5 only). + bool is_changed; // Set when params has been recently changed. + bool is_ready; // Set when indicator is ready (has valid values). + public: + enum ENUM_INDICATOR_STATE_PROP { + INDICATOR_STATE_PROP_HANDLE, + INDICATOR_STATE_PROP_IS_CHANGED, + INDICATOR_STATE_PROP_IS_READY, + }; + // Constructor. + IndicatorState() : handle(INVALID_HANDLE), is_changed(true), is_ready(false) {} + // Getters. + template + T Get(STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) _prop) { + switch (_prop) { + case INDICATOR_STATE_PROP_HANDLE: + return (T)handle; + case INDICATOR_STATE_PROP_IS_CHANGED: + return (T)is_changed; + case INDICATOR_STATE_PROP_IS_READY: + return (T)is_ready; + }; + SetUserError(ERR_INVALID_PARAMETER); + return (T)WRONG_VALUE; + } + // Setters. + template + void Set(STRUCT_ENUM(IndicatorState, ENUM_INDICATOR_STATE_PROP) _prop, T _value) { + switch (_prop) { + case INDICATOR_STATE_PROP_HANDLE: + handle = (T)_value; + break; + case INDICATOR_STATE_PROP_IS_CHANGED: + is_changed = (T)_value; + break; + case INDICATOR_STATE_PROP_IS_READY: + is_ready = (T)_value; + break; + default: + SetUserError(ERR_INVALID_PARAMETER); + break; + }; + } + // State checkers. + bool IsChanged() { return is_changed; } + bool IsReady() { return is_ready; } +}; diff --git a/IndicatorData.struct.serialize.h b/Indicator/IndicatorData.struct.serialize.h similarity index 99% rename from IndicatorData.struct.serialize.h rename to Indicator/IndicatorData.struct.serialize.h index aeac18748..06cff24bf 100644 --- a/IndicatorData.struct.serialize.h +++ b/Indicator/IndicatorData.struct.serialize.h @@ -25,7 +25,7 @@ * Includes IndicatorData's struct serializers. */ -#include "Serializer.mqh" +#include "../Serializer/Serializer.h" // Forward class declaration. class Serializer; diff --git a/IndicatorData.struct.signal.h b/Indicator/IndicatorData.struct.signal.h similarity index 100% rename from IndicatorData.struct.signal.h rename to Indicator/IndicatorData.struct.signal.h diff --git a/Indicator/IndicatorRenko.h b/Indicator/IndicatorRenko.h new file mode 100644 index 000000000..df4a403da --- /dev/null +++ b/Indicator/IndicatorRenko.h @@ -0,0 +1,288 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2022, 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 + * An abstract class to implement Renko indicators. + */ + +// Ignore processing of this file if already included. +#ifndef INDICATOR_RENKO_H +#define INDICATOR_RENKO_H + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Chart.struct.tf.h" +#include "IndicatorCandle.h" +#include "IndicatorRenko.provider.h" +#include "IndicatorRenko.struct.h" + +/** + * Renko candle type. + */ +enum ENUM_INDI_RENKO_CANDLE_TYPE { + INDI_RENKO_CANDLE_TYPE_NONE, // Empty candle. + INDI_RENKO_CANDLE_TYPE_RAISE, // Green candle. + INDI_RENKO_CANDLE_TYPE_DROP, // Red candle. +}; + +/** + * Renko indicator parameters. + */ +struct RenkoParams : IndicatorTfParams { + int pips_limit; + + RenkoParams(int _pips_limit = 10, int _shift = 0) : IndicatorTfParams("Renko") { + pips_limit = _pips_limit; + shift = _shift; + }; + RenkoParams(RenkoParams &_params) : IndicatorTfParams("Renko") { THIS_REF = _params; }; + + // Getters. + unsigned int GetSecsPerCandle() { + // Renko doesn't use timeframe-based candles. + return 0; + } +}; + +/** + * Renko candles. + * + * Note that Renko acts as a Candle indicator and thus has the same number of + * modes and same list of ValueStorage buffers as IndicatorCandle one. + */ +class IndicatorRenko : public IndicatorCandle> { + protected: + // @todo Time-frame used to create candles. + ENUM_TIMEFRAMES tf; + + long last_entry_ts; + long last_completed_candle_ts; + long last_incomplete_candle_ts; + + /* Protected methods */ + + /** + * Initialize class. + * + * Called on constructor. + */ + void Init() { + last_entry_ts = 0; + last_completed_candle_ts = 0; + last_incomplete_candle_ts = 0; + } + + public: + /* Special methods */ + + /** + * Class constructor with timeframe enum. + */ + IndicatorRenko(int _pips_limit = 10) { + iparams.pips_limit = _pips_limit; + Init(); + } + + /** + * Class constructor with parameters. + */ + IndicatorRenko(RenkoParams &_params) + : IndicatorCandle>( + _params, IndicatorDataParams(FINAL_INDI_CANDLE_MODE_ENTRY)) { + Init(); + } + + /** + * Checks whether given candle is a raise (green) or drop (red). + */ + ENUM_INDI_RENKO_CANDLE_TYPE GetCandleType(CandleOCTOHLC &_candle) { + if (_candle.close > _candle.open) { + return INDI_RENKO_CANDLE_TYPE_RAISE; + } + + if (_candle.close < _candle.open) { + return INDI_RENKO_CANDLE_TYPE_DROP; + } + + Print("Error: Candle's close price cannot equal open price!"); + DebugBreak(); + return INDI_RENKO_CANDLE_TYPE_NONE; + } + + /** + * Checks for pips limit. + */ + bool RenkoConditionMet(ENUM_INDI_RENKO_CANDLE_TYPE _last_candle_type, CandleOCTOHLC &_candle, double _price) { + double _price_diff_limit = GetSymbolProps().GetPipValue() * iparams.pips_limit; + double _price_diff = _price - _candle.open; + + if (_last_candle_type == INDI_RENKO_CANDLE_TYPE_NONE) { + // No candle and then raise/drop requires just normal difference of pips. + if (MathAbs(_price_diff) > _price_diff_limit) { + return true; + } + } else { + if (_last_candle_type == INDI_RENKO_CANDLE_TYPE_RAISE) { + if (_price_diff > _price_diff_limit) { + // Raising after raise. + return true; + } else if (_price_diff < -_price_diff_limit * 2) { + // Drop after raise requires 2 x pips drop. + return true; + } + } else if (_last_candle_type == INDI_RENKO_CANDLE_TYPE_DROP) { + if (_price_diff > _price_diff_limit * 2) { + // Raising after drop requires 2 x pips raise. + return true; + } else if (_price_diff < -_price_diff_limit) { + // Drop after drop. + return true; + } + } + } + + return false; + } + + /** + * Called when data source emits new entry (historic or future one). + */ + void OnDataSourceEntry(IndicatorDataEntry &entry) override{ + /* + @todo Move logic into ItemsHistoryRenkoCandleProvider class. + + if (entry.timestamp < last_entry_ts) { + Print("Error: IndicatorRenko doesn't support sending entries in non-ascending order!"); + DebugBreak(); + } + + // We'll be updating candle from bid price. + double _price = entry[1]; + + CandleOCTOHLC _candle; + CandleOCTOHLC _last_completed_candle; + ENUM_INDI_RENKO_CANDLE_TYPE _last_completed_candle_type; + + if (last_completed_candle_ts != 0) { + _last_completed_candle = icdata.GetByKey(last_completed_candle_ts); + _last_completed_candle_type = GetCandleType(_last_completed_candle); + } else { + _last_completed_candle_type = INDI_RENKO_CANDLE_TYPE_NONE; + } + + if (last_incomplete_candle_ts != 0) { + // There is previous candle. Retrieving and updating it. + _candle = icdata.GetByKey(last_incomplete_candle_ts); + _candle.Update(entry.timestamp, _price); + + // Checking for close price difference. + if (RenkoConditionMet(_last_completed_candle_type, _candle, _price)) { + // Closing current candle. + _candle.is_complete = true; + } + + // Updating candle. + icdata.Add(_candle, last_incomplete_candle_ts); + + Print("Updated Candle: ", _candle.ToString()); + + if (_candle.is_complete) { + last_completed_candle_ts = last_incomplete_candle_ts; + last_incomplete_candle_ts = 0; + } + } else { + // There is no incomplete candle, creating one. + if (last_completed_candle_ts != 0) { + // Price of the last candle will be used to initialize open price for new, incomplete candle. + double _last_close_price = _last_completed_candle.close; + _candle = CandleOCTOHLC(_last_close_price, _last_close_price, _last_close_price, _last_close_price, + entry.timestamp, entry.timestamp); + // Current price will be added to newly created incomplete candle. + _candle.Update(entry.timestamp, _price); + } else { + // There was no completed candle. Creating new, incomplete candle from current price. + _candle = CandleOCTOHLC(_price, _price, _price, _price, entry.timestamp, entry.timestamp); + } + + _candle.is_complete = false; + + // Creating new candle. + icdata.Add(_candle, entry.timestamp); + + Print("Added candle: ", _candle.ToString(), " now there is ", icdata.Size(), " candles in the buffer."); + + last_incomplete_candle_ts = entry.timestamp; + } + + static int iteration = 0; + + ++iteration; + + Print("Iteration: ", iteration); + + if (iteration > 1793) { + // Print(icdata.ToJSON()); + } + + Print("Last Incomplete Time: ", TimeToString(last_incomplete_candle_ts, TIME_DATE | TIME_MINUTES | + TIME_SECONDS), " (", last_incomplete_candle_ts, ")"); Print("Last Incomplete Candle: ", + icdata.GetByKey(last_incomplete_candle_ts).ToString()); Print("Last Completed Time: ", + TimeToString(last_completed_candle_ts, TIME_DATE | TIME_MINUTES | TIME_SECONDS), " (", last_completed_candle_ts, + ")"); Print("Last Completed Candle: ", icdata.GetByKey(last_completed_candle_ts).ToString()); + + // Updating tick & bar indices. Bar time is time of the last completed candle. + // Print(last_completed_candle_ts); + counter.OnTick(last_completed_candle_ts); + + Print("---------"); + + last_entry_ts = entry.timestamp; + */ + }; + + /** + * Gets indicator's time-frame. + */ + ENUM_TIMEFRAMES GetTf() override { return tf; } + + /** + * Returns time of the bar for a given shift. + * + * Note: For Renko it returns last completed bar. + */ + datetime GetBarTime(int _rel_shift = 0) override { + if (_rel_shift != 0) { + Print("Error: IndicatorRenko doesn't yet support shift other than 0!"); + DebugBreak(); + return 0; + } + + return (datetime)last_completed_candle_ts; + } +}; + +#endif diff --git a/Indicator/IndicatorRenko.provider.h b/Indicator/IndicatorRenko.provider.h new file mode 100644 index 000000000..e987f12ba --- /dev/null +++ b/Indicator/IndicatorRenko.provider.h @@ -0,0 +1,80 @@ +//+------------------------------------------------------------------+ +//| 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 . + * + */ + +// Ignore processing of this file if already included. +#ifndef INDICATOR_RENKO_PROVIDER_H +#define INDICATOR_RENKO_PROVIDER_H + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +/** + * Candle grouping and regeneration for time-frame based candles. + */ +template +class ItemsHistoryRenkoCandleProvider : public ItemsHistoryCandleProvider { + // Pointer to IndicatorTick. Used to fetch data from IndicatorTick in the hierarchy. + IndicatorData* indi; + + // Current tick index. Effectively a number of ticks generated by attached + // IndicatorTick. + int tick_index; + + public: + /** + * Constructor. + */ + ItemsHistoryRenkoCandleProvider(IndicatorData* _indi_tick) : indi(_indi_tick), tick_index(0) {} + + /** + * Called when new tick was emitted from IndicatorTick-based source. + */ + virtual void OnTick(ItemsHistory, ItemsHistoryRenkoCandleProvider>* _history, long _time_ms, + float _ask, float _bid) { + ++tick_index; + + Print("IndicatorRenko is not yet implemented!"); + DebugBreak(); + } + + /** + * Returns current tick index. Effectively a number of ticks generated by + * attached IndicatorTick. + */ + int GetTickIndex() { return tick_index; } + + /** + * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we + * want previous or next items from selected starting point. + */ + void GetItems(ItemsHistory, ItemsHistoryRenkoCandleProvider>* _history, long _from_time_ms, + ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(CandleOCTOHLC, _out_arr)) { + // Method is called if there is a missing item (candle) in the history. We need to regenerate it. + + Print("IndicatorRenko is not yet implemented!"); + DebugBreak(); + } +}; + +#endif // INDICATOR_TF_PROVIDER_H diff --git a/Indicator/IndicatorRenko.struct.h b/Indicator/IndicatorRenko.struct.h new file mode 100644 index 000000000..d7e160798 --- /dev/null +++ b/Indicator/IndicatorRenko.struct.h @@ -0,0 +1,47 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2022, 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 IndicatorRenko's structs. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "Indicator.struct.h" + +/* Structure for IndicatorRenko class parameters. */ +struct IndicatorRenkoParams : IndicatorParams { + float ppc; // Pips per candle. + // Struct constructor. + IndicatorRenkoParams(float _ppc = 10) : ppc(_ppc) {} + // Getters. + float GetPipsPerCandle() { return ppc; } + // Setters. + void SetPipsPerCandle(float _ppc) { ppc = _ppc; } + // Copy constructor. + IndicatorRenkoParams(const IndicatorRenkoParams &_params) { THIS_REF = _params; } +}; diff --git a/Indicator/IndicatorTf.h b/Indicator/IndicatorTf.h index d0597b7ee..db9ab8980 100644 --- a/Indicator/IndicatorTf.h +++ b/Indicator/IndicatorTf.h @@ -30,16 +30,18 @@ #endif // Includes. -#include "../Chart.struct.tf.h" #include "IndicatorCandle.h" -#include "IndicatorTf.struct.h" +#include "IndicatorTf.provider.h" /** * Class to deal with candle indicators. */ template -class IndicatorTf : public IndicatorCandle { +class IndicatorTf : public IndicatorCandle> { protected: + // Time-frame used to create candles. + ENUM_TIMEFRAMES tf; + /* Protected methods */ /** @@ -47,7 +49,9 @@ class IndicatorTf : public IndicatorCandle { * * Called on constructor. */ - void Init() {} + void Init() { + history.SetItemProvider(new ItemsHistoryTfCandleProvider(iparams.GetSecsPerCandle(), THIS_PTR)); + } public: /* Special methods */ @@ -65,6 +69,7 @@ class IndicatorTf : public IndicatorCandle { */ IndicatorTf(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) { iparams.SetSecsPerCandle(ChartTf::TfToSeconds(_tf)); + tf = _tf; Init(); } @@ -73,16 +78,24 @@ class IndicatorTf : public IndicatorCandle { */ IndicatorTf(ENUM_TIMEFRAMES_INDEX _tfi = 0) { iparams.SetSecsPerCandle(ChartTf::TfToSeconds(ChartTf::IndexToTf(_tfi))); + tf = ChartTf::IndexToTf(_tfi); Init(); } /** * Class constructor with parameters. */ - IndicatorTf(TFP& _icparams, const IndicatorDataParams& _idparams) - : IndicatorCandle(_icparams, _idparams) { - Init(); - } + IndicatorTf(TFP& _icparams, const IndicatorDataParams& _idparams) { Init(); } + + /** + * Gets indicator's time-frame. + */ + ENUM_TIMEFRAMES GetTf() override { return tf; } + + /** + * Returns current tick index (incremented every OnTick()). + */ + int GetTickIndex() override { return history.GetItemProvider() PTR_DEREF GetTickIndex(); } }; -#endif +#endif // INDICATOR_TF_H diff --git a/Indicator/IndicatorTf.provider.h b/Indicator/IndicatorTf.provider.h new file mode 100644 index 000000000..5197aaa73 --- /dev/null +++ b/Indicator/IndicatorTf.provider.h @@ -0,0 +1,178 @@ +//+------------------------------------------------------------------+ +//| 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 . + * + */ + +// Ignore processing of this file if already included. +#ifndef INDICATOR_TF_PROVIDER_H +#define INDICATOR_TF_PROVIDER_H + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +/** + * Candle grouping and regeneration for time-frame based candles. + */ +template +class ItemsHistoryTfCandleProvider : public ItemsHistoryCandleProvider { + // Seconds per candle. + int spc; + + // Pointer to IndicatorTick. Used to fetch data from IndicatorTick in the hierarchy. + IndicatorData* indi; + + // Current tick index. Effectively a number of ticks generated by attached + // IndicatorTick. + int tick_index; + + public: + /** + * Constructor. + */ + ItemsHistoryTfCandleProvider(int _spc, IndicatorData* _indi_tf) : spc(_spc), indi(_indi_tf), tick_index(0) {} + + /** + * Called when new tick was emitted from IndicatorTick-based source. + */ + virtual void OnTick(ItemsHistory, ItemsHistoryTfCandleProvider>* _history, long _time_ms, + float _ask, float _bid) { + ++tick_index; + + // Print("IndicatorTf's history: New tick: ", TimeToString(_time_ms / 1000, TIME_DATE | TIME_MINUTES | + // TIME_SECONDS), + // ", ", _ask, ", ", _bid); + + // We know that tick's timestamp will be ahead of the last tick and thus + // inside or ahead of the last created candle. In order to retrieve last + // valid candle, we need to use ItemsHistory::GetItemByShift(0) to check if + // we have to update last or create/append new candle. + CandleOCTOHLC _candle; + + // Will regenerate candles up to the last added candle ever. We have to + // call it, because some of the previous actions may have removed some of + // the recent candles. Note that OnTick() advances its _time_ms in + // ascending order, so all we need to most recent history. + // + // Note that EnsureShiftExists() may return false when there never been any + // candle added. + _history PTR_DEREF EnsureShiftExists(0); + + if (_history PTR_DEREF TryGetItemByShift(0, _candle, false) && _candle.ContainsTimeMs(_time_ms)) { + // Time given fits in the last added candle's time-frame, updating the candle with given price. + _candle.Update(_time_ms, _bid); + + // Storing candle in the history. + _history PTR_DEREF Update(_candle, _history PTR_DEREF GetShiftIndex(0)); + } else { + CandleOCTOHLC _candle_tmp; + + // We don't want to regenerate history, because at the start there will bo no candle however. + if (_history PTR_DEREF TryGetItemByShift(0, _candle_tmp, false)) { + // Print("Completed candle: ", _candle_tmp.ToString()); + // Print("Real candle: ", iOpen(NULL, Period(), 1), " ", iHigh(NULL, Period(), 1), " ", + // iLow(NULL, Period(), 1), " ", ChartStatic::iClose(NULL, (ENUM_TIMEFRAMES)Period(), 1)); + // Print("--"); + } + + // Either there is no candle at shift 0 or given time doesn't fit in the #0 candle's time-frame. + _candle.Init(GetCandleTimeFromTimeMs(_time_ms, spc), spc, _time_ms, _bid); + + // Adding candle as the most recent item in the history. It will now become the candle at shift 0. + _history PTR_DEREF Append(_candle); + } + } + + /** + * Returns current tick index. Effectively a number of ticks generated by + * attached IndicatorTick. + */ + int GetTickIndex() { return tick_index; } + + /** + * Returns start time of the candle (the place it's on the chart) for the given tick's time in milliseconds. + */ + int GetCandleTimeFromTimeMs(long _time_ms, int _length_in_secs) { + return (int)((_time_ms - _time_ms % ((long)_length_in_secs * 1000)) / 1000); + } + + /** + * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we + * want previous or next items from selected starting point. + */ + void GetItems(ItemsHistory, ItemsHistoryTfCandleProvider>* _history, long _from_time_ms, + ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(CandleOCTOHLC, _out_arr)) { + // Method is called if there is a missing item (candle) in the history. We need to regenerate it. + if (_from_time_ms != 0) { + // In order to (re)generate candle, we need to fetch ticks within a fixed time-frame and then update that candle + // with all fetched ticks. For IndicatorTf, there is no difference between (re)generating backwards or forwards, + // as candles are time-framed. The only problem is that there may be missing candles in fetched time-frames. We + // just need to skip such time-frame and fetch ticks for next time-frame. In order to determine + + IndicatorData* _indi_tick = indi PTR_DEREF GetTick(); + + // Ticks to form a candle. + static ARRAY(TickTAB, _ticks); + + while (_num_items > 0) { + // Calculating time from which and to which we want to retrieve ticks to form a candle. + int _ticks_from_s = GetCandleTimeFromTimeMs(_from_time_ms, spc); + long _ticks_from_ms = (long)_ticks_from_s * 1000; + long _candle_length_ms = (long)spc * 1000; + long _ticks_to_ms = _ticks_from_ms + _candle_length_ms - 1; + + if (!_indi_tick PTR_DEREF FetchHistoryByTimeRange(_ticks_from_ms, _ticks_to_ms, _ticks)) { + // There is no more ticks in the history, giving up. + break; + } + + if (ArraySize(_ticks) > 0) { + // Forming a candle. + CandleOCTOHLC _candle; + _candle.Init(_ticks_from_s, spc); + for (int i = 0; i < ArraySize(_ticks); ++i) { + _candle.Update(_ticks[i].time_ms, _ticks[i].bid); + } + + // Adding candle to the output array. + ArrayPushObject(_out_arr, _candle); + --_num_items; + } + + if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { + _from_time_ms += _candle_length_ms; + } else { + _from_time_ms -= _candle_length_ms; + } + } + } else { + Print("Error: GetItems() for IndicatorTf can only work with given _from_time_ms!"); + DebugBreak(); + } + } + + /** + * Returns information about item provider. + */ + string ToString() override { return "IndicatorTf candle provider on " + indi PTR_DEREF GetFullName(); } +}; + +#endif // INDICATOR_TF_PROVIDER_H diff --git a/Indicator/IndicatorTf.struct.h b/Indicator/IndicatorTf.struct.h index d6e3e55b5..49b31ab0c 100644 --- a/Indicator/IndicatorTf.struct.h +++ b/Indicator/IndicatorTf.struct.h @@ -31,14 +31,14 @@ #endif // Includes. -#include "../Indicator.struct.h" +#include "Indicator.struct.h" /* Structure for IndicatorTf class parameters. */ struct IndicatorTfParams : IndicatorParams { ChartTf tf; unsigned int spc; // Seconds per candle. // Struct constructor. - IndicatorTfParams(unsigned int _spc = 60) : spc(_spc) {} + IndicatorTfParams(string _name = "", unsigned int _spc = 60) : IndicatorParams(_name), spc(_spc) {} // Getters. unsigned int GetSecsPerCandle() { return spc; } // Setters. diff --git a/Indicator/IndicatorTick.h b/Indicator/IndicatorTick.h index 3581a9840..a9a602440 100644 --- a/Indicator/IndicatorTick.h +++ b/Indicator/IndicatorTick.h @@ -1,6 +1,6 @@ //+------------------------------------------------------------------+ //| EA31337 framework | -//| Copyright 2016-2023, EA31337 Ltd | +//| Copyright 2016-2021, EA31337 Ltd | //| https://github.com/EA31337 | //+------------------------------------------------------------------+ @@ -31,8 +31,10 @@ // Includes. #include "../Buffer/BufferTick.h" -#include "../Indicator.mqh" -#include "../Indicator.struct.h" +#include "Indicator.h" +#include "Indicator.struct.h" +#include "IndicatorTick.provider.h" +#include "TickBarCounter.h" // Indicator modes. enum ENUM_INDI_TICK_MODE { @@ -44,13 +46,14 @@ enum ENUM_INDI_TICK_MODE { /** * Class to deal with tick indicators. */ -template +template class IndicatorTick : public Indicator { protected: - BufferTick itdata; + ItemsHistory, TCP> history; TS itparams; string symbol; SymbolInfoProp symbol_props; + TickBarCounter counter; protected: /* Protected methods */ @@ -66,10 +69,10 @@ class IndicatorTick : public Indicator { // We can only index via timestamp. flags |= INDI_FLAG_INDEXABLE_BY_TIMESTAMP; - itdata.AddFlags(DICT_FLAG_FILL_HOLES_UNSORTED); - itdata.SetOverflowListener(IndicatorTickOverflowListener, 10); // Ask and Bid price. Set(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_MAX_MODES), 2); + + history.SetItemProvider(new ItemsHistoryTickProvider(THIS_PTR)); } public: @@ -100,31 +103,31 @@ class IndicatorTick : public Indicator { unsigned int GetSuitableDataSourceTypes() override { return INDI_SUITABLE_DS_TYPE_EXPECT_NONE; } /** - * Returns time of the bar for a given shift. + * Returns possible data source modes. It is a bit mask of ENUM_IDATA_SOURCE_TYPE. */ - datetime GetBarTime(int _shift = 0) override { - if (_shift != 0) { - Print("Error: IndicatorTick::GetBarTime() does not yet support getting entries by shift other than 0!"); - DebugBreak(); - } + unsigned int GetPossibleDataModes() override { return IDATA_BUILTIN; } - return (datetime)itdata.GetMax(); - } + /** + * Returns time of the bar for a given shift. + */ + datetime GetBarTime(int _rel_shift = 0) override { return history.GetItemTimeByShift(_rel_shift); } /** * Gets ask price for a given date and time. Return current ask price if _dt wasn't passed or is 0. */ - virtual double GetAsk(datetime _dt = 0) { return GetEntry(_dt).GetValue(INDI_TICK_MODE_PRICE_ASK); } + virtual double GetAsk(int _shift = 0) { return GetEntryValue(INDI_TICK_MODE_PRICE_ASK, _shift).Get(); } /** * Gets bid price for a given date and time. Return current bid price if _dt wasn't passed or is 0. */ - virtual double GetBid(datetime _dt = 0) { return GetEntry(_dt).GetValue(INDI_TICK_MODE_PRICE_BID); } + virtual double GetBid(int _shift = 0) { return GetEntryValue(INDI_TICK_MODE_PRICE_BID, _shift).Get(); } /** * Returns value storage of given kind. */ IValueStorage* GetSpecificValueStorage(ENUM_INDI_VS_TYPE _type) override { + Print("IndicatorTick::GetSpecificValueStorage() is no longer available!"); + /* switch (_type) { case INDI_VS_TYPE_PRICE_ASK: return (IValueStorage*)itdata.GetAskValueStorage(); @@ -140,6 +143,8 @@ class IndicatorTick : public Indicator { // Trying in parent class. return Indicator::GetSpecificValueStorage(_type); } + */ + return nullptr; } /** @@ -159,31 +164,17 @@ class IndicatorTick : public Indicator { } /** - * Sends historic entries to listening indicators. May be overriden. + * Appends given entry into the history. */ - void EmitHistory() override { - for (DictStructIterator> iter(itdata.Begin()); iter.IsValid(); ++iter) { - IndicatorDataEntry _entry = TickToEntry(iter.Key(), iter.Value()); - EmitEntry(_entry, INDI_EMITTED_ENTRY_TYPE_TICK); - } - } - - /** - * Stores entry in the buffer for later rerieval. - */ - void StoreEntry(IndicatorDataEntry& _entry) override { itdata.Add(EntryToTick(_entry), _entry.timestamp); } + virtual void AppendEntry(IndicatorDataEntry& entry) override { + // Appending tick into the history. + history.GetItemProvider() PTR_DEREF OnTick(&history, entry.timestamp * 1000, (float)entry[0], (float)entry[1]); + }; /** - * @todo + * Returns points to ticks history. */ - IndicatorDataEntry TickToEntry(long _timestamp, TickAB& _tick) { - IndicatorDataEntry _entry(2); - _entry.timestamp = _timestamp; - _entry.values[INDI_TICK_MODE_PRICE_ASK] = _tick.ask; - _entry.values[INDI_TICK_MODE_PRICE_BID] = _tick.bid; - _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, _tick.ask != 0 && _tick.bid != 0); - return _entry; - } + ItemsHistory, TCP>* GetHistory() { return &history; } /** * @todo @@ -195,53 +186,6 @@ class IndicatorTick : public Indicator { return _tick; } - /** - * Returns the indicator's data entry. - * - * @see: IndicatorDataEntry. - * - * @return - * Returns IndicatorDataEntry struct filled with indicator values. - */ - IndicatorDataEntry GetEntry(long _dt = 0) override { - ResetLastError(); - long _timestamp; - - if ((long)_dt != 0) { - _timestamp = (long)_dt; - } else { - _timestamp = itdata.GetMax(); - } - - if (itdata.KeyExists(_timestamp)) { - TickAB _tick = itdata.GetByKey(_timestamp); - return TickToEntry(_timestamp, _tick); - } - int _max_modes = Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_MAX_MODES)); - - // No tick at given timestamp. Returning invalid entry. - IndicatorDataEntry _entry(_max_modes); - GetEntryAlter(_entry, (datetime)_entry.timestamp); - - for (int i = 0; i < _max_modes; ++i) { - _entry.values[i] = (double)0; - } - - _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, false); - return _entry; - } - - /** - * Alters indicator's struct value. - * - * This method allows user to modify the struct entry before it's added to cache. - * This method is called on GetEntry() right after values are set. - */ - virtual void GetEntryAlter(IndicatorDataEntry& _entry, datetime _time) { - ENUM_DATATYPE _dtype = Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_DTYPE)); - _entry.AddFlags(_entry.GetDataTypeFlags(_dtype)); - }; - /** * Returns the indicator's entry value for the given shift and mode. * @@ -250,17 +194,22 @@ class IndicatorTick : public Indicator { * @return * Returns DataParamEntry struct filled with a single value. */ - IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = 0) override { - if (_shift != 0) { - Print("Error: IndicatorTick does not yet support getting entries by shift other than 0!"); - DebugBreak(); - IndicatorDataEntryValue _default; - return _default; + IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) override { + TickTAB _tick; + + if (history.TryGetItemByShift(_abs_shift, _tick)) { + switch (_mode) { + case INDI_TICK_MODE_PRICE_ASK: + return _tick.ask; + case INDI_TICK_MODE_PRICE_BID: + return _tick.bid; + default: + Print("Invalid mode while trying to get entry from IndicatorTick!"); + DebugBreak(); + } } - int _ishift = _shift >= 0 ? _shift : itparams.GetShift(); - // @todo Support for shift. - return GetEntry((datetime)0)[_mode]; + return DBL_MAX; } /** @@ -293,55 +242,19 @@ class IndicatorTick : public Indicator { * Traverses source indicators' hierarchy and tries to find IndicatorTick object at the end. */ virtual IndicatorTick* GetTickIndicator() { return THIS_PTR; } - - /* Setters */ - - /** - * Sets a tick struct with price values. - * - * @see: MqlTick. - */ - void SetTick(MqlTick& _mql_tick, long _timestamp = 0) { - TickAB _tick(_mql_tick); - itdata.Add(_tick, _timestamp); - } - - /* Virtual methods */ - - /** - * Returns a tick struct with price values. - * - * @see: MqlTick. - * - * @return - * Returns MqlTick struct with prices of the symbol. - */ - virtual MqlTick GetTick(int _timestamp = 0) { - IndicatorDataEntry _entry = GetEntry((datetime)_timestamp); - MqlTick _tick; - _tick.time = (datetime)_entry.GetTime(); - _tick.bid = _entry[0]; - _tick.ask = _entry[1]; - return _tick; - } - - /* Callback methods */ - - /** - * Function should return true if resize can be made, or false to overwrite current slot. - */ - static bool IndicatorTickOverflowListener(ENUM_DICT_OVERFLOW_REASON _reason, int _size, int _num_conflicts) { - switch (_reason) { - case DICT_OVERFLOW_REASON_FULL: - // We allow resize if dictionary size is less than 86400 slots. - return _size < 86400; - case DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS: - default: - // When there is too many conflicts, we just reject doing resize, so first conflicting slot will be reused. - break; - } - return false; - } }; +/** + * Converts TickAB into IndicatorDataEntry. + */ +template +IndicatorDataEntry TickToEntry(long _timestamp, TickAB& _tick) { + IndicatorDataEntry _entry(2); + _entry.timestamp = _timestamp; + _entry.values[INDI_TICK_MODE_PRICE_ASK] = _tick.ask; + _entry.values[INDI_TICK_MODE_PRICE_BID] = _tick.bid; + _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, _tick.ask != 0 && _tick.bid != 0); + return _entry; +} + #endif diff --git a/Indicator/IndicatorTick.provider.h b/Indicator/IndicatorTick.provider.h new file mode 100644 index 000000000..16659088a --- /dev/null +++ b/Indicator/IndicatorTick.provider.h @@ -0,0 +1,74 @@ +//+------------------------------------------------------------------+ +//| 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 . + * + */ + +// Ignore processing of this file if already included. +#ifndef INDICATOR_TICK_PROVIDER_H +#define INDICATOR_TICK_PROVIDER_H + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Storage/ItemsHistory.h" + +/** + * Regenerates candles and updates exising candles from new ticks. Subclasses by IndicatorTf, IndicatorRenko. + */ +template +class ItemsHistoryTickProvider : public ItemsHistoryItemProvider> { + // Pointer to IndicatorTick. Used to fetch ask/bid prices. + IndicatorData* indi; + + public: + /** + * Constructor. + */ + ItemsHistoryTickProvider(IndicatorData* _indi_tick) : indi(_indi_tick) {} + + /** + * Called when new tick was emitted from IndicatorTick-based source. + */ + virtual void OnTick(ItemsHistory, ItemsHistoryTickProvider>* _history, long _time_ms, float _ask, + float _bid) { + TickTAB _tick(_time_ms, _ask, _bid); + _history PTR_DEREF Append(_tick); + } + + /** + * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we + * want previous or next items from selected starting point. + */ + void GetItems(ItemsHistory, ItemsHistoryTickProvider>* _history, long _from_time_ms, + ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(TickTAB, _out_arr)) { + // Method is called if there is a missing item (tick) in the history. We need to regenerate it. + indi PTR_DEREF FetchHistoryByStartTimeAndCount(_from_time_ms, _dir, _num_items, _out_arr); + } + + /** + * Returns information about item provider. + */ + string ToString() override { return "IndicatorTick tick provider on " + indi PTR_DEREF GetFullName(); } +}; + +#endif // INDICATOR_TICK_PROVIDER_H diff --git a/Indicator/IndicatorTickSource.h b/Indicator/IndicatorTickSource.h index adc3d17f4..eab3590d2 100644 --- a/Indicator/IndicatorTickSource.h +++ b/Indicator/IndicatorTickSource.h @@ -26,7 +26,7 @@ #endif // Includes. -#include "../Indicator.mqh" +#include "Indicator.h" /** * Indicator to be used with IndicatorTick as a data source. diff --git a/Indicator/README.md b/Indicator/README.md index add1d2685..83aa4e568 100644 --- a/Indicator/README.md +++ b/Indicator/README.md @@ -4,6 +4,20 @@ Indicator classes are intended for implementation of technical indicators. They can help with storing and accessing values and indicator parameters. +## Class diagram + +```mermaid +classDiagram + Object <|-- IndicatorBase + IndicatorBase <|-- Indicator + Indicator <|-- IndicatorCandle + Indicator <|-- IndicatorCandleSource + Indicator <|-- IndicatorTick + Indicator <|-- IndicatorTickSource + IndicatorCandle <|-- IndicatorRenko + IndicatorCandle <|-- IndicatorTf +``` + ## `IndicatorBase` An abstract class for all type of indicators (a base class). diff --git a/Indicator/TickBarCounter.h b/Indicator/TickBarCounter.h index 47d227631..45f0a102c 100644 --- a/Indicator/TickBarCounter.h +++ b/Indicator/TickBarCounter.h @@ -42,6 +42,13 @@ struct TickBarCounter { // Index of the current tick. int tick_index; + TickBarCounter() { + last_bar_time = (datetime)0; + bar_index = 0; + is_new_bar = false; + tick_index = 0; + } + /** * Increases current bar index (used in OnTick()). If there was no bar, the current bar will become 0. */ @@ -68,12 +75,11 @@ struct TickBarCounter { * Check if there is a new bar to parse. */ bool IsNewBarInternal(datetime _bar_time) { - bool _result = false; if (last_bar_time != _bar_time) { SetLastBarTime(_bar_time); - _result = true; + return true; } - return _result; + return false; } /* Setters */ @@ -96,7 +102,7 @@ struct TickBarCounter { /** * Updates tick & bar indices. */ - void OnTick(datetime _bar_time) { + void OnTick(datetime _bar_time = 0) { IncreaseTickIndex(); if (is_new_bar) { diff --git a/tests/IndicatorTest.mq4 b/Indicator/tests/Indicator.test.mq4 similarity index 97% rename from tests/IndicatorTest.mq4 rename to Indicator/tests/Indicator.test.mq4 index eb02d9786..d7d056e30 100644 --- a/tests/IndicatorTest.mq4 +++ b/Indicator/tests/Indicator.test.mq4 @@ -25,4 +25,4 @@ */ // Includes. -#include "IndicatorTest.mq5" +#include "Indicator.test.mq5" diff --git a/tests/IndicatorTest.mq5 b/Indicator/tests/Indicator.test.mq5 similarity index 97% rename from tests/IndicatorTest.mq5 rename to Indicator/tests/Indicator.test.mq5 index 4dc4ae546..56fa3ee98 100644 --- a/tests/IndicatorTest.mq5 +++ b/Indicator/tests/Indicator.test.mq5 @@ -25,8 +25,8 @@ */ // Includes. -#include "../Indicator.mqh" -#include "../Test.mqh" +#include "../Indicator.h" +#include "../../Test.mqh" /** * Implements OnInit(). diff --git a/tests/IndicatorBaseTest.mq4 b/Indicator/tests/IndicatorBase.test.mq4 similarity index 96% rename from tests/IndicatorBaseTest.mq4 rename to Indicator/tests/IndicatorBase.test.mq4 index fa5a9dd80..fbff42af3 100644 --- a/tests/IndicatorBaseTest.mq4 +++ b/Indicator/tests/IndicatorBase.test.mq4 @@ -25,4 +25,4 @@ */ // Includes. -#include "IndicatorBaseTest.mq5" +#include "IndicatorBase.test.mq5" diff --git a/tests/IndicatorBaseTest.mq5 b/Indicator/tests/IndicatorBase.test.mq5 similarity index 97% rename from tests/IndicatorBaseTest.mq5 rename to Indicator/tests/IndicatorBase.test.mq5 index 9c8877eb9..986873152 100644 --- a/tests/IndicatorBaseTest.mq5 +++ b/Indicator/tests/IndicatorBase.test.mq5 @@ -25,8 +25,8 @@ */ // Includes. +#include "../../Test.mqh" #include "../IndicatorBase.h" -#include "../Test.mqh" /** * Implements OnInit(). diff --git a/Indicator/tests/IndicatorCandle.test.mq5 b/Indicator/tests/IndicatorCandle.test.mq5 index 9611788d0..e56c7755f 100644 --- a/Indicator/tests/IndicatorCandle.test.mq5 +++ b/Indicator/tests/IndicatorCandle.test.mq5 @@ -25,13 +25,48 @@ */ // Includes. +#include "../../Platform.h" #include "../../Test.mqh" #include "../IndicatorCandle.h" +Ref indi_candle; + /** * Implements OnInit(). */ int OnInit() { - // @todo - return (INIT_SUCCEEDED); + Platform::Init(); + Platform::Add(indi_candle = Platform::FetchDefaultCandleIndicator()); + return _LastError == ERR_NO_ERROR ? INIT_SUCCEEDED : INIT_FAILED; +} + +void OnTick() { + Platform::Tick(); + if (Platform::IsNewHour() && indi_candle REF_DEREF GetBarIndex() > 0) { + // If a new hour occur, we check for a candle OHLCs, then we invalidate the + // candle and try to regenerate it by checking again the OHLCs. + BarOHLC _ohlc1 = indi_candle REF_DEREF GetOHLC(); + + // Now we invalidate current candle (candle will be removed from the IndicatorCandle's cache). + // @fixit @todo Fix candle invalidation. + // indi_candle REF_DEREF InvalidateCandle(indi_candle REF_DEREF GetBarIndex()); + + // Retrieving candle again. + BarOHLC _ohlc2 = indi_candle REF_DEREF GetOHLC(); + assertEqualOrExit( + _ohlc2.time, _ohlc1.time, + "Difference between consecutive OHLC values after invalidating and then regenerating the candle!"); + assertEqualOrExit( + _ohlc2.open, _ohlc1.open, + "Difference between consecutive OHLC values after invalidating and then regenerating the candle!"); + assertEqualOrExit( + _ohlc2.high, _ohlc1.high, + "Difference between consecutive OHLC values after invalidating and then regenerating the candle!"); + assertEqualOrExit( + _ohlc2.low, _ohlc1.low, + "Difference between consecutive OHLC values after invalidating and then regenerating the candle!"); + assertEqualOrExit( + _ohlc2.close, _ohlc1.close, + "Difference between consecutive OHLC values after invalidating and then regenerating the candle!"); + } } diff --git a/tests/IndicatorDataTest.mq4 b/Indicator/tests/IndicatorData.test.mq4 similarity index 96% rename from tests/IndicatorDataTest.mq4 rename to Indicator/tests/IndicatorData.test.mq4 index b249402d6..a91592e9f 100644 --- a/tests/IndicatorDataTest.mq4 +++ b/Indicator/tests/IndicatorData.test.mq4 @@ -25,4 +25,4 @@ */ // Includes. -#include "IndicatorDataTest.mq5" +#include "IndicatorData.test.mq5" diff --git a/tests/IndicatorDataTest.mq5 b/Indicator/tests/IndicatorData.test.mq5 similarity index 95% rename from tests/IndicatorDataTest.mq5 rename to Indicator/tests/IndicatorData.test.mq5 index 601dc9d4a..0bf82d51a 100644 --- a/tests/IndicatorDataTest.mq5 +++ b/Indicator/tests/IndicatorData.test.mq5 @@ -25,8 +25,8 @@ */ // Includes. -#include "../IndicatorData.mqh" -#include "../Test.mqh" +#include "../../Test.mqh" +#include "../IndicatorData.h" /** * Implements OnInit(). diff --git a/Indicator/tests/IndicatorRenko.test.mq4 b/Indicator/tests/IndicatorRenko.test.mq4 new file mode 100644 index 000000000..c1e3ff307 --- /dev/null +++ b/Indicator/tests/IndicatorRenko.test.mq4 @@ -0,0 +1,28 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2022, 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 + * Test functionality of IndicatorRenko class. + */ + +// Includes. +#include "IndicatorRenko.test.mq5" diff --git a/Indicator/tests/IndicatorRenko.test.mq5 b/Indicator/tests/IndicatorRenko.test.mq5 new file mode 100644 index 000000000..483749ff6 --- /dev/null +++ b/Indicator/tests/IndicatorRenko.test.mq5 @@ -0,0 +1,89 @@ +//+------------------------------------------------------------------+ +//| 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 + * Test functionality of IndicatorRenko class. + * + * Idea is to check if ticks from IndicatorTick will be properly grouped by given timespan/timeframe. + */ + +// Includes. +#include "../../Indicators/Indi_AMA.mqh" +#include "../../Indicators/Tick/Indi_TickMt.mqh" +#include "../../Platform.h" +#include "../../Test.mqh" +#include "../../Util.h" +#include "../IndicatorRenko.h" +#include "../IndicatorTick.h" +#include "classes/Indicators.h" + +#define __debug__x + +Ref indi_tick; +Ref indi_renko; + +/** + * Implements OnInit(). + */ +int OnInit() { + Platform::Init(); + // Platform ticks. + indi_tick = Platform::FetchDefaultTickIndicator(); + + // Renko with 10 pips limit. + Platform::Add(indi_renko = new IndicatorRenko(1)); + + double _pip_value = SymbolInfoStatic::GetPipValue(_Symbol); + Print("Pip Value: ", _pip_value); + + // Renko will be run over default tick indicator. + indi_renko.Ptr().SetDataSource(indi_tick.Ptr()); + + return (INIT_SUCCEEDED); +} + +/** + * Implements OnTick(). + */ +void OnTick() { + Platform::Tick(); + + if (indi_renko.Ptr().IsNewBar()) { + string c_o = DoubleToStr(indi_renko.Ptr().GetOpen(0), 5); + string c_h = DoubleToStr(indi_renko.Ptr().GetHigh(0), 5); + string c_l = DoubleToStr(indi_renko.Ptr().GetLow(0), 5); + string c_c = DoubleToStr(indi_renko.Ptr().GetClose(0), 5); + string time = TimeToString(indi_renko.Ptr().GetBarTime(0), TIME_DATE | TIME_MINUTES | TIME_SECONDS); + + Util::Print("Bar: " + IntegerToString(indi_renko.Ptr().GetBarTime(0)) + " (" + time + "), candle = " + c_o + ", " + + c_h + ", " + c_l + ", " + c_c); + } +} + +/** + * Implements OnDeinit(). + */ +void OnDeinit(const int reason) { + // Printing all grouped candles. + // Print(indi_renko_real.Ptr().GetName(), "'s all candles:"); + // Print(indi_renko_real.Ptr().CandlesToString()); +} diff --git a/Indicator/tests/IndicatorTf.test.mq5 b/Indicator/tests/IndicatorTf.test.mq5 index f3db2878e..dfba778b1 100644 --- a/Indicator/tests/IndicatorTf.test.mq5 +++ b/Indicator/tests/IndicatorTf.test.mq5 @@ -27,16 +27,19 @@ */ // Includes. +#include "../../DictBase.mqh" #include "../../Indicators/Indi_AMA.mqh" #include "../../Indicators/Tick/Indi_TickMt.mqh" +#include "../../Log.mqh" #include "../../Platform.h" +#include "../../SymbolInfo.mqh" #include "../../Test.mqh" #include "../../Util.h" #include "../IndicatorTf.h" -#include "../IndicatorTick.h" #include "classes/IndicatorTfDummy.h" #include "classes/Indicators.h" +// Global variables. Ref indi_tick; Ref indi_tf; Ref indi_tf_real; diff --git a/Indicator/tests/IndicatorTick.test.mq5 b/Indicator/tests/IndicatorTick.test.mq5 index 9cca139b8..31ff469c3 100644 --- a/Indicator/tests/IndicatorTick.test.mq5 +++ b/Indicator/tests/IndicatorTick.test.mq5 @@ -25,22 +25,27 @@ */ // Includes. +#include "../../Platform.h" #include "../../Test.mqh" +#include "../IndicatorTick.h" #include "classes/IndicatorTickDummy.h" /** * Implements OnInit(). */ int OnInit() { - IndicatorTickDummy _indi_tick(_Symbol); + Platform::Init(); + + Ref _indi_tick = new IndicatorTickDummy(_Symbol); + Platform::Add(_indi_tick.Ptr()); + long _time = 1; + for (double _price = 0.1; _price <= 2.0; _price += 0.1) { - MqlTick _tick; - _tick.time = (datetime)_time++; - _tick.ask = _price; - _tick.bid = _price; - _indi_tick.SetTick(_tick, _tick.time); + TickTAB _tick(_time++ * 1000, _price, _price); + _indi_tick REF_DEREF GetHistory() PTR_DEREF Append(_tick); } - // Print(_indi_tick.ToString()); + + // Print(_indi_tick REF_DEREF ToString()); return (INIT_SUCCEEDED); } diff --git a/Indicator/tests/classes/IndicatorTfDummy.h b/Indicator/tests/classes/IndicatorTfDummy.h index b13127b92..4b2a73c03 100644 --- a/Indicator/tests/classes/IndicatorTfDummy.h +++ b/Indicator/tests/classes/IndicatorTfDummy.h @@ -31,10 +31,11 @@ // Includes. #include "../../IndicatorTf.h" +#include "../../IndicatorTf.struct.h" // Params for dummy candle-based indicator. struct IndicatorTfDummyParams : IndicatorTfParams { - IndicatorTfDummyParams(unsigned int _spc = 60) : IndicatorTfParams(_spc) {} + IndicatorTfDummyParams(unsigned int _spc = 60) : IndicatorTfParams("IndicatorTf", _spc) {} }; /** @@ -48,13 +49,11 @@ class IndicatorTfDummy : public IndicatorTf { string GetName() override { return "IndicatorTfDummy(" + IntegerToString(iparams.spc) + ")"; } - void OnDataSourceEntry(IndicatorDataEntry& entry, - ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT) override { - IndicatorTf::OnDataSourceEntry(entry, type); - - if (type != INDI_EMITTED_ENTRY_TYPE_TICK) { - return; - } + void OnDataSourceEntry(IndicatorDataEntry& entry) override { + // When overriding OnDataSourceEntry() we have to remember to call parent + // method, because IndicatorCandle also need to invoke it in order to + // create/update matching candle. + IndicatorTf::OnDataSourceEntry(entry); #ifdef __debug_indicator__ Print(GetFullName(), " got new tick at ", entry.timestamp, diff --git a/Indicator/tests/classes/IndicatorTickDummy.h b/Indicator/tests/classes/IndicatorTickDummy.h index 209a6b056..ec9e43b06 100644 --- a/Indicator/tests/classes/IndicatorTickDummy.h +++ b/Indicator/tests/classes/IndicatorTickDummy.h @@ -30,7 +30,7 @@ #endif // Includes. -#include "../../../Tick.struct.h" +#include "../../../Tick/Tick.struct.h" #include "../../IndicatorTick.h" // Params for dummy tick-based indicator. @@ -39,7 +39,7 @@ struct IndicatorTickDummyParams : IndicatorParams { }; // Dummy tick-based indicator. -class IndicatorTickDummy : public IndicatorTick { +class IndicatorTickDummy : public IndicatorTick> { public: IndicatorTickDummy(string _symbol, int _shift = 0, string _name = "") : IndicatorTick(_symbol, INDI_TICK, _shift, _name) {} diff --git a/Indicator/tests/classes/Indicators.h b/Indicator/tests/classes/Indicators.h index ee19f59bc..d4839b3b3 100644 --- a/Indicator/tests/classes/Indicators.h +++ b/Indicator/tests/classes/Indicators.h @@ -30,7 +30,7 @@ #endif // Includes. -#include "../../../IndicatorData.mqh" +#include "../../../Indicator/IndicatorData.h" #include "../../../Refs.mqh" /** @@ -53,9 +53,9 @@ class Indicators { /** * Executes OnTick() on every added indicator. */ - void Tick() { + void Tick(int _global_tick_index) { for (int i = 0; i < ArraySize(_indis); ++i) { - _indis[i].Ptr().OnTick(); + _indis[i].Ptr().OnTick(_global_tick_index); } } diff --git a/IndicatorData.enum.h b/IndicatorData.enum.h deleted file mode 100644 index 10d745040..000000000 --- a/IndicatorData.enum.h +++ /dev/null @@ -1,54 +0,0 @@ -//+------------------------------------------------------------------+ -//| EA31337 framework | -//| Copyright 2016-2023, 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 IndicatorData's enums. - */ - -#ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once -#endif - -/* Defines type of source data for. Also used for Indicator::GetPossibleDataModes(). */ -enum ENUM_IDATA_SOURCE_TYPE { - IDATA_BUILTIN = 1 << 0, // Platform built-in - IDATA_CHART = 1 << 1, // Chart calculation - IDATA_ICUSTOM = 1 << 2, // iCustom: Custom indicator file - IDATA_ICUSTOM_LEGACY = 1 << 3, // iCustom: Custom, legacy, provided by MT indicator file - IDATA_INDICATOR = 1 << 4, // OnIndicator: Another indicator as a source of data - IDATA_ONCALCULATE = 1 << 5, // OnCalculate: Custom calculation function - IDATA_MATH = 1 << 6 // Math-based indicator -}; - -/* Defines range value data type for indicator storage. */ -enum ENUM_IDATA_VALUE_RANGE { - IDATA_RANGE_BINARY, // E.g. 0 or 1. - IDATA_RANGE_BITWISE, // Bitwise - IDATA_RANGE_MIXED, - IDATA_RANGE_PRICE, // Values represent price. - IDATA_RANGE_PRICE_DIFF, // Values represent price differences. - IDATA_RANGE_PRICE_ON_SIGNAL, // Values represent price on signal, otherwise zero. - IDATA_RANGE_RANGE, // E.g. 0 to 100. - IDATA_RANGE_UNKNOWN -}; diff --git a/Indicators/Bitwise/Indi_Candle.mqh b/Indicators/Bitwise/Indi_Candle.mqh index b1d62529b..098146b8f 100644 --- a/Indicators/Bitwise/Indi_Candle.mqh +++ b/Indicators/Bitwise/Indi_Candle.mqh @@ -23,9 +23,9 @@ // Includes. #include "../../Bar.struct.h" #include "../../BufferStruct.mqh" -#include "../../Indicator.mqh" +#include "../../Indicator/Indicator.h" #include "../../Pattern.struct.h" -#include "../../Serializer.mqh" +#include "../../Serializer/Serializer.h" #include "../Price/Indi_Price.mqh" #include "../Special/Indi_Math.mqh" @@ -91,15 +91,14 @@ class Indi_Candle : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); BarOHLC _ohlcs[1]; switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: // In this mode, price is fetched from IndicatorCandle. - _ohlcs[0] = GetCandle() PTR_DEREF GetOHLC(_ishift); + _ohlcs[0] = GetCandle() PTR_DEREF GetOHLC(ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: // In this mode, price is fetched from given indicator. Such indicator @@ -118,10 +117,10 @@ class Indi_Candle : public Indicator { break; } - _ohlcs[0].open = GetDataSource().GetValue(PRICE_OPEN, _ishift); - _ohlcs[0].high = GetDataSource().GetValue(PRICE_HIGH, _ishift); - _ohlcs[0].low = GetDataSource().GetValue(PRICE_LOW, _ishift); - _ohlcs[0].close = GetDataSource().GetValue(PRICE_CLOSE, _ishift); + _ohlcs[0].open = GetDataSource().GetValue(PRICE_OPEN, ToRelShift(_abs_shift)); + _ohlcs[0].high = GetDataSource().GetValue(PRICE_HIGH, ToRelShift(_abs_shift)); + _ohlcs[0].low = GetDataSource().GetValue(PRICE_LOW, ToRelShift(_abs_shift)); + _ohlcs[0].close = GetDataSource().GetValue(PRICE_CLOSE, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Bitwise/Indi_Pattern.mqh b/Indicators/Bitwise/Indi_Pattern.mqh index 8d5105831..cdcc5ae6a 100644 --- a/Indicators/Bitwise/Indi_Pattern.mqh +++ b/Indicators/Bitwise/Indi_Pattern.mqh @@ -23,9 +23,10 @@ // Includes. #include "../../Bar.struct.h" #include "../../BufferStruct.mqh" -#include "../../Indicator.mqh" +#include "../../Indicator/Indicator.define.h" +#include "../../Indicator/Indicator.h" #include "../../Pattern.struct.h" -#include "../../Serializer.mqh" +#include "../../Serializer/Serializer.h" #include "../Price/Indi_Price.mqh" #include "../Special/Indi_Math.mqh" @@ -83,17 +84,19 @@ class Indi_Pattern : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { int i; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); int _max_modes = Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_MAX_MODES)); + + INDI_REQUIRE_SHIFT_OR_RETURN(GetCandle(), ToRelShift(_abs_shift) + _max_modes, WRONG_VALUE); + BarOHLC _ohlcs[8]; switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: // In this mode, price is fetched from candle. for (i = 0; i < _max_modes; ++i) { - _ohlcs[i] = GetCandle() PTR_DEREF GetOHLC(_ishift + i); + _ohlcs[i] = GetCandle() PTR_DEREF GetOHLC(ToRelShift(_abs_shift) + i); if (!_ohlcs[i].IsValid()) { // Return empty entry on invalid candles. return WRONG_VALUE; @@ -118,10 +121,10 @@ class Indi_Pattern : public Indicator { } for (i = 0; i < _max_modes; ++i) { - _ohlcs[i].open = GetDataSource().GetValue(PRICE_OPEN, _ishift + i); - _ohlcs[i].high = GetDataSource().GetValue(PRICE_HIGH, _ishift + i); - _ohlcs[i].low = GetDataSource().GetValue(PRICE_LOW, _ishift + i); - _ohlcs[i].close = GetDataSource().GetValue(PRICE_CLOSE, _ishift + i); + _ohlcs[i].open = GetDataSource().GetValue(PRICE_OPEN, ToRelShift(_abs_shift) + i); + _ohlcs[i].high = GetDataSource().GetValue(PRICE_HIGH, ToRelShift(_abs_shift) + i); + _ohlcs[i].low = GetDataSource().GetValue(PRICE_LOW, ToRelShift(_abs_shift) + i); + _ohlcs[i].close = GetDataSource().GetValue(PRICE_CLOSE, ToRelShift(_abs_shift) + i); if (!_ohlcs[i].IsValid()) { // Return empty entry on invalid candles. return WRONG_VALUE; @@ -139,9 +142,9 @@ class Indi_Pattern : public Indicator { /** * Alters indicator's struct value. */ - void GetEntryAlter(IndicatorDataEntry& _entry, int _shift) override { + void GetEntryAlter(IndicatorDataEntry& _entry, int _rel_shift) override { _entry.SetFlag(INDI_ENTRY_FLAG_IS_BITWISE, true); - Indicator::GetEntryAlter(_entry, _shift); + Indicator::GetEntryAlter(_entry, _rel_shift); } /** @@ -150,5 +153,5 @@ class Indi_Pattern : public Indicator { * @return * Returns true if entry is valid (has valid values), otherwise false. */ - virtual bool IsValidEntry(IndicatorDataEntry& _entry) { return !_entry.HasValue(INT_MAX); } + virtual bool IsValidEntry(IndicatorDataEntry& _entry) { return !_entry.HasValue(WRONG_VALUE); } }; diff --git a/Indicators/Indi_AC.mqh b/Indicators/Indi_AC.mqh index f8e2be624..f0e75db14 100644 --- a/Indicators/Indi_AC.mqh +++ b/Indicators/Indi_AC.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -108,15 +108,15 @@ class Indi_AC : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) override { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) override { IndicatorDataEntryValue _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_AC::iAC(GetSymbol(), GetTf(), _ishift, THIS_PTR); + _value = Indi_AC::iAC(GetSymbol(), GetTf(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_AD.mqh b/Indicators/Indi_AD.mqh index aaa4193d5..8ea1a1ddb 100644 --- a/Indicators/Indi_AD.mqh +++ b/Indicators/Indi_AD.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -87,10 +87,7 @@ class Indi_AD : public Indicator { static double iAD(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0, IndicatorData *_obj = NULL) { #ifdef __MQL4__ - Print("We'll now retrieve value from ::iAD(", _symbol, ", ", EnumToString(_tf), ", ", _shift, ")..."); - double _value = ::iAD(_symbol, _tf, _shift); - Print("value = \"", _value, "\", LastError: ", _LastError); - return _value; + return ::iAD(_symbol, _tf, _shift); #else // __MQL5__ INDICATOR_BUILTIN_CALL_AND_RETURN(::iAD(_symbol, _tf, VOLUME_TICK), 0, _shift); #endif @@ -99,15 +96,15 @@ class Indi_AD : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_AD::iAD(GetSymbol(), GetTf(), _ishift, THIS_PTR); + _value = Indi_AD::iAD(GetSymbol(), GetTf(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ADX.mqh b/Indicators/Indi_ADX.mqh index d61f173ad..7d29eef1b 100644 --- a/Indicators/Indi_ADX.mqh +++ b/Indicators/Indi_ADX.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Price/Indi_Price.mqh" #ifndef __MQL4__ @@ -107,52 +107,25 @@ class Indi_ADX : public Indicator { // MODE_PLUSDI/PLUSDI_LINE, 2 - MODE_MINUSDI/MINUSDI_LINE int _shift = 0, IndicatorData *_obj = NULL) { #ifdef __MQL4__ - Print("We'll now retrieve value from ::iADX(", _symbol, ", ", EnumToString(_tf), ", ", _shift, ")..."); - double _value = ::iADX(_symbol, _tf, _period, _applied_price, _mode, _shift); - Print("value = \"", _value, "\", LastError: ", _LastError); - return _value; + return ::iADX(_symbol, _tf, _period, _applied_price, _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iADX(_symbol, _tf, _period)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iADX(_symbol, _tf, _period), _mode, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN_ADX, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN_ADX, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_ADX::iADX(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _mode, _ishift, THIS_PTR); + _value = Indi_ADX::iADX(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _mode, ToRelShift(_abs_shift), + THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ADXW.mqh b/Indicators/Indi_ADXW.mqh index 6b5856d57..1bd721f6f 100644 --- a/Indicators/Indi_ADXW.mqh +++ b/Indicators/Indi_ADXW.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.applied_price.h" #include "../Storage/ValueStorage.h" #include "../Storage/ValueStorage.spread.h" @@ -39,7 +39,7 @@ struct IndiADXWParams : IndiADXParams { IndiADXWParams(int _period = 14, ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL, int _shift = 0) : IndiADXParams(_period, _ap, _shift) { itype = itype == INDI_NONE || itype == INDI_ADX ? INDI_ADXW : itype; - if (custom_indi_name == "") { + if (custom_indi_name == "" || custom_indi_name == "Examples\\ADX") { SetCustomIndicatorName("Examples\\ADXW"); } }; @@ -122,7 +122,7 @@ class Indi_ADXW : public Indicator { /** * Calculates ADX Wilder on the array of values. */ - static double iADXWilderOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _period, int _mode, int _shift, + static double iADXWilderOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _period, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -142,15 +142,17 @@ class Indi_ADXW : public Indicator { // Returns value from the first calculation buffer. // Returns first value for as-series array or last value for non-as-series array. - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * On-indicator version of ADX Wilder. */ - static double iADXWilder(IndicatorData *_indi, int _period, int _mode = 0, int _shift = 0) { + static double iADXWilder(IndicatorData *_indi, int _period, int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _period); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey(_period)); - return iADXWilderOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _period, _mode, _shift, _cache); + return iADXWilderOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _period, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** @@ -162,6 +164,7 @@ class Indi_ADXW : public Indicator { ValueStorage &_pdb_buff, ValueStorage &_nd_buff, ValueStorage &_tr_buff, ValueStorage &_atr_buff, ValueStorage &_dx_buff, int _adxw_period) { int i; + // Checking for bars count. if (rates_total < _adxw_period) return (0); // Detect start position. @@ -265,22 +268,21 @@ class Indi_ADXW : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN_ADX, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN_ADX, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_ADXW::iADXWilder(GetSymbol(), GetTf(), GetPeriod(), _mode, _ishift, THIS_PTR); + _value = Indi_ADXW::iADXWilder(GetSymbol(), GetTf(), GetPeriod(), _mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: - _value = Indi_ADXW::iADXWilder(THIS_PTR, GetPeriod(), _mode, _ishift); + _value = Indi_ADXW::iADXWilder(THIS_PTR, GetPeriod(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = Indi_ADXW::iADXWilder(THIS_PTR, GetPeriod(), _mode, _ishift); + _value = Indi_ADXW::iADXWilder(THIS_PTR, GetPeriod(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_AMA.mqh b/Indicators/Indi_AMA.mqh index e5176d987..a34b4cca9 100644 --- a/Indicators/Indi_AMA.mqh +++ b/Indicators/Indi_AMA.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.h" #include "Price/Indi_Price.mqh" @@ -112,7 +112,7 @@ class Indi_AMA : public Indicator { * Calculates AMA on the array of values. */ static double iAMAOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _ama_period, int _fast_ema_period, - int _slow_ema_period, int _ama_shift, int _mode, int _shift, + int _slow_ema_period, int _ama_shift, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_price); @@ -127,7 +127,7 @@ class Indi_AMA : public Indicator { _cache.SetPrevCalculated(Indi_AMA::Calculate(INDICATOR_CALCULATE_GET_PARAMS_SHORT, _cache.GetBuffer(0), _ama_period, _fast_ema_period, _slow_ema_period, _ama_shift)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -135,6 +135,7 @@ class Indi_AMA : public Indicator { */ static double iAMAOnIndicator(IndicatorData *_indi, int _ama_period, int _fast_ema_period, int _slow_ema_period, int _ama_shift, ENUM_APPLIED_PRICE _ap, int _mode = 0, int _shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _ama_period); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT( _indi, _ap, Util::MakeKey(_ama_period, _fast_ema_period, _slow_ema_period, _ama_shift, (int)_ap)); return iAMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _ama_period, _fast_ema_period, _slow_ema_period, @@ -144,7 +145,7 @@ class Indi_AMA : public Indicator { /** * OnInit() method for AMA indicator. */ - static void CalculateInit(int InpPeriodAMA, int InpFastPeriodEMA, int InpSlowPeriodEMA, int InpShiftAMA, + static void CalculateInit(int InpPeriodAMA, int _period_fast_ema, int _period_slow_ema, int _ishift_ama, double &ExtFastSC, double &ExtSlowSC, int &ExtPeriodAMA, int &ExtSlowPeriodEMA, int &ExtFastPeriodEMA) { // Check for input values. @@ -155,20 +156,20 @@ class Indi_AMA : public Indicator { InpPeriodAMA, ExtPeriodAMA); } else ExtPeriodAMA = InpPeriodAMA; - if (InpSlowPeriodEMA <= 0) { + if (_period_slow_ema <= 0) { ExtSlowPeriodEMA = 30; PrintFormat( - "Input parameter InpSlowPeriodEMA has incorrect value (%d). Indicator will use value %d for calculations.", - InpSlowPeriodEMA, ExtSlowPeriodEMA); + "Input parameter _period_slow_ema has incorrect value (%d). Indicator will use value %d for calculations.", + _period_slow_ema, ExtSlowPeriodEMA); } else - ExtSlowPeriodEMA = InpSlowPeriodEMA; - if (InpFastPeriodEMA <= 0) { + ExtSlowPeriodEMA = _period_slow_ema; + if (_period_fast_ema <= 0) { ExtFastPeriodEMA = 2; PrintFormat( - "Input parameter InpFastPeriodEMA has incorrect value (%d). Indicator will use value %d for calculations.", - InpFastPeriodEMA, ExtFastPeriodEMA); + "Input parameter _period_fast_ema has incorrect value (%d). Indicator will use value %d for calculations.", + _period_fast_ema, ExtFastPeriodEMA); } else - ExtFastPeriodEMA = InpFastPeriodEMA; + ExtFastPeriodEMA = _period_fast_ema; // Calculate ExtFastSC & ExtSlowSC. ExtFastSC = 2.0 / (ExtFastPeriodEMA + 1.0); @@ -179,14 +180,14 @@ class Indi_AMA : public Indicator { * OnCalculate() method for AMA indicator. */ static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_SHORT, ValueStorage &ExtAMABuffer, int InpPeriodAMA, - int InpFastPeriodEMA, int InpSlowPeriodEMA, int InpShiftAMA) { + int _period_fast_ema, int _period_slow_ema, int _ishift_ama) { double ExtFastSC; double ExtSlowSC; int ExtPeriodAMA; int ExtSlowPeriodEMA; int ExtFastPeriodEMA; - CalculateInit(InpPeriodAMA, InpFastPeriodEMA, InpSlowPeriodEMA, InpShiftAMA, ExtFastSC, ExtSlowSC, ExtPeriodAMA, + CalculateInit(InpPeriodAMA, _period_fast_ema, _period_slow_ema, _ishift_ama, ExtFastSC, ExtSlowSC, ExtPeriodAMA, ExtSlowPeriodEMA, ExtFastPeriodEMA); int i; // Check for rates count. @@ -229,26 +230,25 @@ class Indi_AMA : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_AMA::iAMA(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetFastPeriod(), GetSlowPeriod(), - GetAMAShift(), GetAppliedPrice() /*]*/, _mode, _ishift, THIS_PTR); + GetAMAShift(), GetAppliedPrice() /*]*/, _mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetFastPeriod(), GetSlowPeriod(), GetAMAShift() /*]*/, _mode, _ishift); + GetFastPeriod(), GetSlowPeriod(), GetAMAShift() /*]*/, _mode, ToRelShift(_abs_shift)); break; case IDATA_ONCALCULATE: _value = Indi_AMA::iAMAOnIndicator(THIS_PTR, /*[*/ GetPeriod(), GetFastPeriod(), GetSlowPeriod(), GetAMAShift(), - GetAppliedPrice() /*]*/, _mode, _ishift); + GetAppliedPrice() /*]*/, _mode, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: _value = Indi_AMA::iAMAOnIndicator(THIS_PTR, /*[*/ GetPeriod(), GetFastPeriod(), GetSlowPeriod(), GetAMAShift(), - GetAppliedPrice() /*]*/, _mode, _ishift); + GetAppliedPrice() /*]*/, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_AO.mqh b/Indicators/Indi_AO.mqh index 24e05a3a3..a7554e38d 100644 --- a/Indicators/Indi_AO.mqh +++ b/Indicators/Indi_AO.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -100,46 +100,22 @@ class Indi_AO : public Indicator { // Note: In MQL4 _mode is not supported. return ::iAO(_symbol, _tf, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iAO(_symbol, _tf)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iAO(_symbol, _tf), _mode, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_AO::iAO(GetSymbol(), GetTf(), _ishift, _mode, THIS_PTR); + _value = Indi_AO::iAO(GetSymbol(), GetTf(), ToRelShift(_abs_shift), _mode, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ASI.mqh b/Indicators/Indi_ASI.mqh index 3137ae317..27744184f 100644 --- a/Indicators/Indi_ASI.mqh +++ b/Indicators/Indi_ASI.mqh @@ -22,9 +22,13 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" +// Defines. +// 2 bars was originally specified by Accumulative Swing Index algorithm. +#define INDI_ASI_MIN_BARS 2 + // Structs. struct IndiASIParams : IndicatorParams { unsigned int period; @@ -38,7 +42,7 @@ struct IndiASIParams : IndicatorParams { }; /** - * Implements the Bill Williams' Accelerator/Decelerator oscillator. + * Implements the Accumulative Swing Index indicator. */ class Indi_ASI : public Indicator { protected: @@ -100,15 +104,17 @@ class Indi_ASI : public Indicator { /** * OnCalculate-based version of ASI as there is no built-in one. */ - static double iASI(IndicatorData *_indi, double _mpc, int _mode = 0, int _shift = 0) { + static double iASI(IndicatorData *_indi, double _mpc, int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, INDI_ASI_MIN_BARS); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey(_mpc)); - return iASIOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mpc, _mode, _shift, _cache); + return iASIOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mpc, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _cache); } /** * Calculates ASI on the array of values. */ - static double iASIOnArray(INDICATOR_CALCULATE_PARAMS_LONG, double _mpc, int _mode, int _shift, + static double iASIOnArray(INDICATOR_CALCULATE_PARAMS_LONG, double _mpc, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -123,7 +129,7 @@ class Indi_ASI : public Indicator { _cache.SetPrevCalculated(Indi_ASI::Calculate(INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _cache.GetBuffer(1), _cache.GetBuffer(2), _mpc)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -153,7 +159,7 @@ class Indi_ASI : public Indicator { CalculateInit(InpT, ExtTpoints, ExtT); - if (rates_total < 2) return (0); + if (rates_total < INDI_ASI_MIN_BARS) return (0); // Start calculation. int pos = prev_calculated - 1; // Correct position, when it's first iteration. @@ -206,20 +212,19 @@ class Indi_ASI : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetMaximumPriceChanging() /*]*/, 0, _ishift); + /*[*/ GetMaximumPriceChanging() /*]*/, 0, ToRelShift(_abs_shift)); break; case IDATA_ONCALCULATE: - _value = Indi_ASI::iASI(THIS_PTR, GetMaximumPriceChanging(), _mode, _ishift); + _value = Indi_ASI::iASI(THIS_PTR, GetMaximumPriceChanging(), _mode, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = Indi_ASI::iASI(THIS_PTR, GetMaximumPriceChanging(), _mode, _ishift); + _value = Indi_ASI::iASI(THIS_PTR, GetMaximumPriceChanging(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ATR.mqh b/Indicators/Indi_ATR.mqh index fc3b68387..028562379 100644 --- a/Indicators/Indi_ATR.mqh +++ b/Indicators/Indi_ATR.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -83,46 +83,22 @@ class Indi_ATR : public Indicator { #ifdef __MQL4__ return ::iATR(_symbol, _tf, _period, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iATR(_symbol, _tf, _period)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iATR(_symbol, _tf, _period), 0, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_ATR::iATR(GetSymbol(), GetTf(), GetPeriod(), _ishift, THIS_PTR); + _value = Indi_ATR::iATR(GetSymbol(), GetTf(), GetPeriod(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Alligator.mqh b/Indicators/Indi_Alligator.mqh index ff999a0af..6eaa59636 100644 --- a/Indicators/Indi_Alligator.mqh +++ b/Indicators/Indi_Alligator.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -143,60 +143,37 @@ class Indi_Alligator : public Indicator { return ::iAlligator(_symbol, _tf, _jaw_period, _jaw_shift, _teeth_period, _teeth_shift, _lips_period, _lips_shift, _ma_method, _applied_price, _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iAlligator(_symbol, _tf, _jaw_period, _jaw_shift, _teeth_period, _teeth_shift, _lips_period, - _lips_shift, _ma_method, _applied_price)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iAlligator(_symbol, _tf, _jaw_period, _jaw_shift, _teeth_period, _teeth_shift, + _lips_period, _lips_shift, _ma_method, _applied_price), + _mode, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); #ifdef __MQL4__ if (_mode == 0) { // In MQL4 mode 0 should be treated as mode 1 as Alligator buffers starts from index 1. - return GetEntryValue((ENUM_ALLIGATOR_LINE)1, _ishift); + return GetEntryValue((ENUM_ALLIGATOR_LINE)1, ToRelShift(_abs_shift)); } #endif switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_Alligator::iAlligator(_Symbol, GetTf(), GetJawPeriod(), GetJawShift(), GetTeethPeriod(), - GetTeethShift(), GetLipsPeriod(), GetLipsShift(), GetMAMethod(), - GetAppliedPrice(), (ENUM_ALLIGATOR_LINE)_mode, _ishift, THIS_PTR); + _value = + Indi_Alligator::iAlligator(GetSymbol(), GetTf(), GetJawPeriod(), GetJawShift(), GetTeethPeriod(), + GetTeethShift(), GetLipsPeriod(), GetLipsShift(), GetMAMethod(), + GetAppliedPrice(), (ENUM_ALLIGATOR_LINE)_mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetJawPeriod(), GetJawShift(), GetTeethPeriod(), GetTeethShift(), GetLipsPeriod(), GetLipsShift(), GetMAMethod(), GetAppliedPrice() /*]*/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_AppliedPrice.mqh b/Indicators/Indi_AppliedPrice.mqh index ed319b117..ce0d688f6 100644 --- a/Indicators/Indi_AppliedPrice.mqh +++ b/Indicators/Indi_AppliedPrice.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "OHLC/Indi_OHLC.mqh" // Structs. @@ -87,13 +87,13 @@ class Indi_AppliedPrice : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_INDICATOR: if (HasDataSource()) { - _value = Indi_AppliedPrice::iAppliedPriceOnIndicator(GetDataSource(), GetAppliedPrice(), _ishift); + _value = + Indi_AppliedPrice::iAppliedPriceOnIndicator(GetDataSource(), GetAppliedPrice(), ToRelShift(_abs_shift)); } break; default: diff --git a/Indicators/Indi_BWMFI.mqh b/Indicators/Indi_BWMFI.mqh index 6a06f7dc9..3c491465d 100644 --- a/Indicators/Indi_BWMFI.mqh +++ b/Indicators/Indi_BWMFI.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -93,7 +93,6 @@ class Indi_BWMFI : public Indicator { */ unsigned int GetSuitableDataSourceTypes() override { return INDI_SUITABLE_DS_TYPE_EXPECT_NONE; } - public: /** * Returns possible data source modes. It is a bit mask of ENUM_IDATA_SOURCE_TYPE. */ @@ -111,48 +110,23 @@ class Indi_BWMFI : public Indicator { #ifdef __MQL4__ return ::iBWMFI(_symbol, _tf, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iBWMFI(_symbol, _tf, VOLUME_TICK)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iBWMFI(_symbol, _tf, VOLUME_TICK), _mode, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = BWMFI_BUFFER, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = BWMFI_BUFFER, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_BWMFI::iBWMFI(GetSymbol(), GetTf(), _ishift, (ENUM_BWMFI_BUFFER)_mode, THIS_PTR); + _value = Indi_BWMFI::iBWMFI(GetSymbol(), GetTf(), ToRelShift(_abs_shift), (ENUM_BWMFI_BUFFER)_mode, THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ VOLUME_TICK /*]*/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); @@ -167,9 +141,11 @@ class Indi_BWMFI : public Indicator { void GetEntryAlter(IndicatorDataEntry &_entry, int _shift) override { Indicator::GetEntryAlter(_entry, _shift); #ifdef __MQL4__ + Print(GetVolume(_shift), ", ", GetVolume(_shift + 1), " | ", GetValue(BWMFI_BUFFER, _shift), " > ", + GetValue(BWMFI_BUFFER, _shift + 1)); // @see: https://en.wikipedia.org/wiki/Market_facilitation_index - bool _vol_up = GetVolume(_shift) > GetVolume(_shift); - bool _val_up = GetValue(BWMFI_BUFFER, _shift) > GetValue(BWMFI_BUFFER, _shift); + bool _vol_up = GetVolume(_shift) > GetVolume(_shift + 1); + bool _val_up = GetValue(BWMFI_BUFFER, _shift) > GetValue(BWMFI_BUFFER, _shift + 1); double _histcolor = EMPTY_VALUE; switch (_vol_up) { case true: @@ -207,8 +183,8 @@ class Indi_BWMFI : public Indicator { * @return * Returns true if entry is valid (has valid values), otherwise false. */ - virtual bool IsValidEntry(IndicatorDataEntry &_entry) { - return _entry[(int)BWMFI_BUFFER] > 0 && _entry[(int)BWMFI_HISTCOLOR] >= 0 && !_entry.HasValue(DBL_MAX) && - !_entry.HasValue(EMPTY_VALUE); + bool IsValidEntry(IndicatorDataEntry &_entry) override { + return _entry.GetValue((int)BWMFI_BUFFER) > 0 && _entry.GetValue((int)BWMFI_HISTCOLOR) >= 0 && + !_entry.HasValue(DBL_MAX); } }; diff --git a/Indicators/Indi_BWZT.mqh b/Indicators/Indi_BWZT.mqh index e6d9c6c80..e5c9afff9 100644 --- a/Indicators/Indi_BWZT.mqh +++ b/Indicators/Indi_BWZT.mqh @@ -20,6 +20,10 @@ * */ +// Defines. +// 38 bars (DATA_LIMIT) was originally specified by Indicators/Examples/BW-ZoneTrade.mq5 +#define INDI_BWZT_DATA_LIMIT 100 + // Includes. #include "../BufferStruct.mqh" #include "../Indicator/IndicatorTf.h" @@ -118,7 +122,7 @@ class Indi_BWZT : public Indicator { /** * OnCalculate-based version of BWZT as there is no built-in one. */ - static double iBWZT(IndicatorData *_indi, int _mode = 0, int _shift = 0) { + static double iBWZT(IndicatorData *_indi, int _mode = 0, int _rel_shift = 0) { INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, ""); // Will return Indi_AC with the same candles source as _indi's. @@ -127,13 +131,14 @@ class Indi_BWZT : public Indicator { // Will return Indi_AO with the same candles source as _indi's. Indi_AO *_indi_ao = Indi_AO::GetCached(_indi); - return iBWZTOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _shift, _cache, _indi_ac, _indi_ao); + return iBWZTOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + INDI_BWZT_DATA_LIMIT, _cache, _indi_ac, _indi_ao); } /** * Calculates BWZT on the array of values. */ - static double iBWZTOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _shift, + static double iBWZTOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _abs_shift, int _data_limit, IndicatorCalculateCache *_cache, Indi_AC *_indi_ac, Indi_AO *_indi_ao, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -149,22 +154,24 @@ class Indi_BWZT : public Indicator { _cache.SetPrevCalculated(Indi_BWZT::Calculate( INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _cache.GetBuffer(1), _cache.GetBuffer(2), _cache.GetBuffer(3), _cache.GetBuffer(4), - _cache.GetBuffer(5), _cache.GetBuffer(6), 38, _indi_ac, _indi_ao)); + _cache.GetBuffer(5), _cache.GetBuffer(6), _data_limit, _indi_ac, _indi_ao)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * On-indicator version of BWZT. */ - static double iBWZTOnIndicator(IndicatorData *_indi, string _symbol, ENUM_TIMEFRAMES _tf, int _mode, int _shift, - IndicatorData *_obj) { + static double iBWZTOnIndicator(IndicatorData *_indi, string _symbol, ENUM_TIMEFRAMES _tf, int _mode, int _rel_shift, + int _data_limit, IndicatorData *_obj) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _data_limit); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey("Indi_BWZT_ON_" + _indi.GetFullName())); Indi_AC *_indi_ac = _obj.GetDataSource(INDI_AC); Indi_AO *_indi_ao = _obj.GetDataSource(INDI_AO); - return iBWZTOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _shift, _cache, _indi_ac, _indi_ao); + return iBWZTOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _data_limit, _cache, _indi_ac, _indi_ao); } /** @@ -251,19 +258,19 @@ class Indi_BWZT : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = Indi_BWZT::iBWZT(THIS_PTR, _mode, _ishift); + _value = Indi_BWZT::iBWZT(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = Indi_BWZT::iBWZT(THIS_PTR, _mode, _ishift); + _value = Indi_BWZT::iBWZT(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Bands.mqh b/Indicators/Indi_Bands.mqh index f9a414031..9300cb9c5 100644 --- a/Indicators/Indi_Bands.mqh +++ b/Indicators/Indi_Bands.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Indi_CCI.mqh" #include "Indi_Envelopes.mqh" #include "Indi_MA.mqh" @@ -38,11 +38,6 @@ double iBands(string _symbol, int _tf, int _period, double _deviation, int _band return Indi_Bands::iBands(_symbol, (ENUM_TIMEFRAMES)_tf, _period, _deviation, _bands_shift, (ENUM_APPLIED_PRICE)_ap, (ENUM_BANDS_LINE)_mode, _shift); } -double iBandsOnArray(double &_arr[], int _total, int _period, double _deviation, int _bands_shift, int _mode, - int _shift) { - ResetLastError(); - return Indi_Bands::iBandsOnArray(_arr, _total, _period, _deviation, _bands_shift, _mode, _shift); -} #endif // Indicator line identifiers used in Bands. @@ -136,126 +131,118 @@ class Indi_Bands : public Indicator { #ifdef __MQL4__ return ::iBands(_symbol, _tf, _period, _deviation, _bands_shift, _applied_price, _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iBands(_symbol, _tf, _period, _bands_shift, _deviation, _applied_price)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iBands(_symbol, _tf, _period, _bands_shift, _deviation, _applied_price), _mode, + _shift); #endif } /** * Calculates Bands on another indicator. + * + * Note that "_bands_shift" is used only for drawing. */ - static double iBandsOnIndicator(IndicatorData *_target, IndicatorData *_source, string _symbol, ENUM_TIMEFRAMES _tf, - unsigned int _period, double _deviation, int _bands_shift, ENUM_APPLIED_PRICE _ap, + static double iBandsOnIndicator(IndicatorData *_indi, unsigned int _period, double _deviation, int _bands_shift, + ENUM_APPLIED_PRICE _ap, ENUM_BANDS_LINE _mode, // (MT4/MT5): 0 - MODE_MAIN/BASE_LINE, 1 - // MODE_UPPER/UPPER_BAND, 2 - MODE_LOWER/LOWER_BAND - int _shift, IndicatorData *_indi_source = NULL) { - double _indi_value_buffer[]; - double _std_dev; - double _line_value; - - ValueStorage *_indi_applied_price = _source PTR_DEREF GetSpecificAppliedPriceValueStorage(_ap, _target); - - // Period can't be higher than number of available bars. - _period = MathMin(_period, ArraySize(_indi_applied_price)); - - ArrayCopy(_indi_value_buffer, _indi_applied_price, 0, _bands_shift + _shift, _period); - - // Base band. Calculating MA from "_period" number of values or less. - _line_value = Indi_MA::SimpleMA(0, _period, _indi_value_buffer); + int _rel_shift) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _period); + INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT(_indi, _ap, + Util::MakeKey(_period, _deviation, _bands_shift, (int)_ap)); + return iBandsOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _period, _deviation, _bands_shift, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); + } - // Standard deviation. - _std_dev = Indi_StdDev::iStdDevOnArray(_indi_value_buffer, _line_value, _period); + static double iBandsOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _period, double _deviation, int _bands_shift, + int _mode, int _abs_shift, IndicatorCalculateCache *_cache, + bool _recalculate = false) { + _cache.SetPriceBuffer(_price); - double _result = EMPTY_VALUE; + if (!_cache.HasBuffers()) { + _cache.AddBuffer>(4); + } - switch (_mode) { - case BAND_BASE: - // Already calculated. - _result = _line_value; - break; - case BAND_UPPER: - _result = _line_value + /* band deviations */ _deviation * _std_dev; - break; - case BAND_LOWER: - _result = _line_value - /* band deviations */ _deviation * _std_dev; - break; + if (_recalculate) { + _cache.ResetPrevCalculated(); } - return _result; - } + _cache.SetPrevCalculated(Indi_Bands::Calculate(INDICATOR_CALCULATE_GET_PARAMS_SHORT, _cache.GetBuffer(0), + _cache.GetBuffer(1), _cache.GetBuffer(2), + _cache.GetBuffer(3), _period, _bands_shift, _deviation)); - static double iBandsOnArray(double &array[], int total, int period, double deviation, int bands_shift, int mode, - int shift) { -#ifdef __MQL4__ - return ::iBandsOnArray(array, total, period, deviation, bands_shift, mode, shift); -#else // __MQL5__ - static Ref price_feeder = new Indi_PriceFeeder(); - price_feeder REF_DEREF SetPrices(array); - price_feeder REF_DEREF SetDataSourceAppliedPrice(INDI_VS_TYPE_INDEX_0); - // First parameter is a pointer to target indicator. It is used to override applied price, so we configure it on the - // price feeder itself and pass it as both, target and source indicator. - return iBandsOnIndicator(price_feeder.Ptr(), price_feeder.Ptr(), NULL, NULL, period, deviation, bands_shift, - (ENUM_APPLIED_PRICE)0 /* unused */, (ENUM_BANDS_LINE)mode, shift); -#endif + return _cache.GetTailValue(_mode, _abs_shift); } - static double iBandsOnArray2(double &array[], int total, int period, double deviation, int bands_shift, int mode, - int shift) { -#ifdef __MQL5__ - // Calculates bollinger bands indicator from array data - int size = ArraySize(array); - if (size < period) return false; - if (period <= 0) return false; - - double ma = Indi_MA::iMAOnArray(array, total, period, 0, MODE_SMA, 0); - - double sum = 0.0, val; - int i; - - for (i = 0; i < period; i++) { - val = array[size - i - 1] - ma; - sum += val * val; + /** + * OnCalculate() method for Bands indicator. + */ + static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_SHORT, ValueStorage &ExtMLBuffer, + ValueStorage &ExtTLBuffer, ValueStorage &ExtBLBuffer, + ValueStorage &ExtStdDevBuffer, int InpBandsPeriod, int InpBandsShift, + double InpBandsDeviations) { + int ExtBandsPeriod, ExtBandsShift; + double ExtBandsDeviations; + int ExtPlotBegin = 0; + + if (InpBandsPeriod < 2) { + ExtBandsPeriod = 20; + PrintFormat("Incorrect value for input variable InpBandsPeriod=%d. Indicator will use value=%d for calculations.", + InpBandsPeriod, ExtBandsPeriod); + } else + ExtBandsPeriod = InpBandsPeriod; + if (InpBandsShift < 0) { + ExtBandsShift = 0; + PrintFormat("Incorrect value for input variable InpBandsShift=%d. Indicator will use value=%d for calculations.", + InpBandsShift, ExtBandsShift); + } else + ExtBandsShift = InpBandsShift; + if (InpBandsDeviations == 0.0) { + ExtBandsDeviations = 2.0; + PrintFormat( + "Incorrect value for input variable InpBandsDeviations=%f. Indicator will use value=%f for calculations.", + InpBandsDeviations, ExtBandsDeviations); + } else + ExtBandsDeviations = InpBandsDeviations; + + if (rates_total < ExtPlotBegin) return (0); + //--- indexes draw begin settings, when we've recieved previous begin + if (ExtPlotBegin != ExtBandsPeriod + begin) { + ExtPlotBegin = ExtBandsPeriod + begin; + PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, ExtPlotBegin); + PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, ExtPlotBegin); + PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, ExtPlotBegin); } - - double dev = deviation * MathSqrt(sum / period); - - switch (mode) { - case BAND_BASE: - return ma; - case BAND_UPPER: - return ma + dev; - case BAND_LOWER: - return ma - dev; + //--- starting calculation + int pos; + if (prev_calculated > 1) + pos = prev_calculated - 1; + else + pos = 0; + //--- main cycle + for (int i = pos; i < rates_total && !IsStopped(); i++) { + //--- middle line + ExtMLBuffer[i] = Indi_MA::SimpleMA(i, ExtBandsPeriod, price); + //--- calculate and write down StdDev + ExtStdDevBuffer[i] = StdDev_Func(i, price, ExtMLBuffer, ExtBandsPeriod); + //--- upper line + ExtTLBuffer[i] = ExtMLBuffer[i] + ExtBandsDeviations * ExtStdDevBuffer[i].Get(); + //--- lower line + ExtBLBuffer[i] = ExtMLBuffer[i] - ExtBandsDeviations * ExtStdDevBuffer[i].Get(); } + //--- OnCalculate done. Return new prev_calculated. + return (rates_total); + } - return DBL_MIN; -#else - return ::iBandsOnArray(array, total, period, deviation, bands_shift, mode, shift); -#endif + static double StdDev_Func(const int position, ValueStorage &price, ValueStorage &ma_price, + const int period) { + double std_dev = 0.0; + //--- calcualte StdDev + if (position >= period) { + for (int i = 0; i < period; i++) std_dev += MathPow(price[position - i] - ma_price[position], 2.0); + std_dev = MathSqrt(std_dev / period); + } + //--- return calculated value + return (std_dev); } /** @@ -274,28 +261,22 @@ class Indi_Bands : public Indicator { * Note that in MQL5 Applied Price must be passed as the last parameter * (before mode and shift). */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = BAND_BASE, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = BAND_BASE, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_Bands::iBands(GetSymbol(), GetTf(), GetPeriod(), GetDeviation(), GetBandsShift(), - GetAppliedPrice(), (ENUM_BANDS_LINE)_mode, _ishift, THIS_PTR); + GetAppliedPrice(), (ENUM_BANDS_LINE)_mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: - _value = - Indi_Bands::iBandsOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), GetDeviation(), - GetBandsShift(), GetAppliedPrice(), (ENUM_BANDS_LINE)_mode, _ishift); + case IDATA_INDICATOR: + // Calculating bands value from specified indicator. + _value = Indi_Bands::iBandsOnIndicator(THIS_PTR, GetPeriod(), GetDeviation(), GetBandsShift(), + GetAppliedPrice(), (ENUM_BANDS_LINE)_mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), - GetBandsShift(), GetDeviation(), GetAppliedPrice() /* ] */, _mode, _ishift); - break; - case IDATA_INDICATOR: - // Calculating bands value from specified indicator. - _value = Indi_Bands::iBandsOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), - GetDeviation(), GetBandsShift(), GetAppliedPrice(), - (ENUM_BANDS_LINE)_mode, _ishift, THIS_PTR); + GetBandsShift(), GetDeviation(), GetAppliedPrice() /* ] */, _mode, ToRelShift(_abs_shift)); break; } return _value; diff --git a/Indicators/Indi_BearsPower.mqh b/Indicators/Indi_BearsPower.mqh index 4cb9cd79d..602686d7a 100644 --- a/Indicators/Indi_BearsPower.mqh +++ b/Indicators/Indi_BearsPower.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -84,47 +84,23 @@ class Indi_BearsPower : public Indicator { #ifdef __MQL4__ return ::iBearsPower(_symbol, _tf, _period, _applied_price, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iBearsPower(_symbol, _tf, _period)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iBearsPower(_symbol, _tf, _period), 0, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = _value = iBearsPower(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _ishift, THIS_PTR); + _value = _value = + iBearsPower(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_BullsPower.mqh b/Indicators/Indi_BullsPower.mqh index 3b599acd2..d560bfb1f 100644 --- a/Indicators/Indi_BullsPower.mqh +++ b/Indicators/Indi_BullsPower.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -84,47 +84,22 @@ class Indi_BullsPower : public Indicator { #ifdef __MQL4__ return ::iBullsPower(_symbol, _tf, _period, _applied_price, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iBullsPower(_symbol, _tf, _period)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iBullsPower(_symbol, _tf, _period), 0, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = iBullsPower(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _ishift, THIS_PTR); + _value = iBullsPower(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /**/ GetPeriod() /**/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_CCI.mqh b/Indicators/Indi_CCI.mqh index ed6aee388..c7c659883 100644 --- a/Indicators/Indi_CCI.mqh +++ b/Indicators/Indi_CCI.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Indi_MA.mqh" #include "Indi_PriceFeeder.mqh" #include "Price/Indi_Price.mqh" @@ -32,9 +32,9 @@ double iCCI(string _symbol, int _tf, int _period, int _ap, int _shift) { ResetLastError(); return Indi_CCI::iCCI(_symbol, (ENUM_TIMEFRAMES)_tf, _period, (ENUM_APPLIED_PRICE)_ap, _shift); } -double iCCIOnArray(double &_arr[], int _total, int _period, int _shift) { +double iCCIOnArray(double &_arr[], int _total, int _period, int _abs_shift) { ResetLastError(); - return Indi_CCI::iCCIOnArray(_arr, _total, _period, _shift); + return Indi_CCI::iCCIOnArray(_arr, _total, _period, _abs_shift); } #endif @@ -100,6 +100,8 @@ class Indi_CCI : public Indicator { static double iCCIOnIndicator(IndicatorData *_indi, string _symbol, ENUM_TIMEFRAMES _tf, unsigned int _period, int _mode, int _shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _period); + _indi.ValidateDataSourceMode(_mode); double _indi_value_buffer[]; @@ -161,30 +163,29 @@ class Indi_CCI : public Indicator { * Note that in MQL5 Applied Price must be passed as the last parameter * (before mode and shift). */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: // @fixit Somehow shift isn't used neither in MT4 nor MT5. - _value = Indi_CCI::iCCI(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _ishift /* + iparams.shift*/, - THIS_PTR); + _value = Indi_CCI::iCCI(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), + ToRelShift(_abs_shift) /* + iparams.shift*/, THIS_PTR); break; case IDATA_ONCALCULATE: // @fixit Somehow shift isn't used neither in MT4 nor MT5. - _value = - Indi_CCI::iCCIOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), _ishift /* + iparams.shift*/); + _value = Indi_CCI::iCCIOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), + ToRelShift(_abs_shift) /* + iparams.shift*/); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), - GetAppliedPrice() /* ] */, 0, _ishift); + GetAppliedPrice() /* ] */, 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: ValidateSelectedDataSource(); // @fixit Somehow shift isn't used neither in MT4 nor MT5. - _value = - Indi_CCI::iCCIOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), _ishift /* + iparams.shift*/); + _value = Indi_CCI::iCCIOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), + ToRelShift(_abs_shift) /* + iparams.shift*/); break; } return _value; diff --git a/Indicators/Indi_CHO.mqh b/Indicators/Indi_CHO.mqh index 267fe5288..3d87d5e6c 100644 --- a/Indicators/Indi_CHO.mqh +++ b/Indicators/Indi_CHO.mqh @@ -20,9 +20,13 @@ * */ +// Defines. +// 2 bars was originally specified by Indicators/Examples/CHO.mq5 +#define INDI_CHO_MIN_BARS 2 + // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" #include "../Util.h" #include "Indi_MA.mqh" @@ -93,6 +97,7 @@ class Indi_CHO : public Indicator { DebugBreak(); return 0; } + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_obj, INDI_CHO_MIN_BARS); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG( _obj, Util::MakeKey(_fast_ma_period, _slow_ma_period, (int)_ma_method, (int)_av)); return iChaikinOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _fast_ma_period, _slow_ma_period, _ma_method, _av, @@ -104,7 +109,7 @@ class Indi_CHO : public Indicator { * Calculates Chaikin Oscillator on the array of values. */ static double iChaikinOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _fast_ma_period, int _slow_ma_period, - ENUM_MA_METHOD _ma_method, ENUM_APPLIED_VOLUME _av, int _mode, int _shift, + ENUM_MA_METHOD _ma_method, ENUM_APPLIED_VOLUME _av, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -120,7 +125,7 @@ class Indi_CHO : public Indicator { INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _cache.GetBuffer(1), _cache.GetBuffer(2), _cache.GetBuffer(3), _fast_ma_period, _slow_ma_period, _ma_method, _av)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -128,6 +133,7 @@ class Indi_CHO : public Indicator { */ static double iChaikinOnIndicator(IndicatorData *_indi, int _fast_ma_period, int _slow_ma_period, ENUM_MA_METHOD _ma_method, ENUM_APPLIED_VOLUME _av, int _mode = 0, int _shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, INDI_CHO_MIN_BARS); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG( _indi, Util::MakeKey(_fast_ma_period, _slow_ma_period, (int)_ma_method, (int)_av)); return iChaikinOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _fast_ma_period, _slow_ma_period, _ma_method, _av, @@ -144,7 +150,7 @@ class Indi_CHO : public Indicator { if (rates_total < InpSlowMA) return (0); // Preliminary calculations. int i, start; - if (prev_calculated < 2) + if (prev_calculated < INDI_CHO_MIN_BARS) start = 0; else start = prev_calculated - 2; @@ -198,22 +204,21 @@ class Indi_CHO : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: _value = Indi_CHO::iChaikin(GetSymbol(), GetTf(), /*[*/ GetSlowMA(), GetFastMA(), GetSmoothMethod(), - GetInputVolume() /*]*/, _mode, _ishift, THIS_PTR); + GetInputVolume() /*]*/, _mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetFastMA(), - GetSlowMA(), GetSmoothMethod(), GetInputVolume() /*]*/, 0, _ishift); + GetSlowMA(), GetSmoothMethod(), GetInputVolume() /*]*/, 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: _value = Indi_CHO::iChaikinOnIndicator(GetDataSource(), /*[*/ GetFastMA(), GetSlowMA(), GetSmoothMethod(), - GetInputVolume() /*]*/, _mode, _ishift); + GetInputVolume() /*]*/, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_CHV.mqh b/Indicators/Indi_CHV.mqh index 2e6c56531..cfc61a34f 100644 --- a/Indicators/Indi_CHV.mqh +++ b/Indicators/Indi_CHV.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" #include "../Util.h" #include "Indi_MA.mqh" @@ -95,6 +95,7 @@ class Indi_CHV : public Indicator { */ double iCHV(IndicatorData *_indi, int _smooth_period, int _chv_period, ENUM_CHV_SMOOTH_METHOD _smooth_method, int _mode = 0, int _shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _chv_period + _smooth_period - 2); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey(_smooth_period, _chv_period, _smooth_method)); return iCHVOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _smooth_period, _chv_period, _smooth_method, _mode, @@ -105,7 +106,7 @@ class Indi_CHV : public Indicator { * Calculates Chaikin Volatility on the array of values. */ static double iCHVOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _smooth_period, int _chv_period, - ENUM_CHV_SMOOTH_METHOD _smooth_method, int _mode, int _shift, + ENUM_CHV_SMOOTH_METHOD _smooth_method, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -123,7 +124,7 @@ class Indi_CHV : public Indicator { // Returns value from the first calculation buffer. // Returns first value for as-series array or last value for non-as-series array. - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -189,20 +190,19 @@ class Indi_CHV : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = iCHV(THIS_PTR, GetSmoothPeriod(), GetCHVPeriod(), GetSmoothMethod(), _mode, _ishift); + _value = iCHV(THIS_PTR, GetSmoothPeriod(), GetCHVPeriod(), GetSmoothMethod(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetSmoothPeriod(), - GetCHVPeriod(), GetSmoothMethod() /*]*/, _mode, _ishift); + GetCHVPeriod(), GetSmoothMethod() /*]*/, _mode, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iCHV(THIS_PTR, GetSmoothPeriod(), GetCHVPeriod(), GetSmoothMethod(), _mode, _ishift); + _value = iCHV(THIS_PTR, GetSmoothPeriod(), GetCHVPeriod(), GetSmoothMethod(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ColorBars.mqh b/Indicators/Indi_ColorBars.mqh index d7a1755e7..527aeb3c1 100644 --- a/Indicators/Indi_ColorBars.mqh +++ b/Indicators/Indi_ColorBars.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" // Structs. @@ -67,15 +67,16 @@ class Indi_ColorBars : public Indicator { /** * OnCalculate-based version of Color Bars as there is no built-in one. */ - static double iColorBars(IndicatorData *_indi, int _mode = 0, int _shift = 0) { + static double iColorBars(IndicatorData *_indi, int _mode = 0, int _rel_shift = 0) { INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, ""); - return iColorBarsOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _shift, _cache); + return iColorBarsOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _cache); } /** * Calculates Color Bars on the array of values. */ - static double iColorBarsOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _shift, + static double iColorBarsOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -91,7 +92,7 @@ class Indi_ColorBars : public Indicator { _cache.GetBuffer(1), _cache.GetBuffer(2), _cache.GetBuffer(3), _cache.GetBuffer(4))); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -125,19 +126,19 @@ class Indi_ColorBars : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = Indi_ColorBars::iColorBars(THIS_PTR, _mode, _ishift); + _value = Indi_ColorBars::iColorBars(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = Indi_ColorBars::iColorBars(THIS_PTR, _mode, _ishift); + _value = Indi_ColorBars::iColorBars(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ColorCandlesDaily.mqh b/Indicators/Indi_ColorCandlesDaily.mqh index c156e0a98..7803c32cb 100644 --- a/Indicators/Indi_ColorCandlesDaily.mqh +++ b/Indicators/Indi_ColorCandlesDaily.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" // Structs. @@ -80,15 +80,16 @@ class Indi_ColorCandlesDaily : public Indicator { /** * OnCalculate-based version of Color Candles Daily as there is no built-in one. */ - static double iCCD(IndicatorData *_indi, int _mode = 0, int _shift = 0) { + static double iCCD(IndicatorData *_indi, int _mode = 0, int _rel_shift = 0) { INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, ""); - return iCCDOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _shift, _cache); + return iCCDOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _cache); } /** * Calculates Color Candles Daily on the array of values. */ - static double iCCDOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _shift, + static double iCCDOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -104,7 +105,7 @@ class Indi_ColorCandlesDaily : public Indicator { INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _cache.GetBuffer(1), _cache.GetBuffer(2), _cache.GetBuffer(3), _cache.GetBuffer(4))); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -135,19 +136,19 @@ class Indi_ColorCandlesDaily : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = Indi_ColorCandlesDaily::iCCD(THIS_PTR, _mode, _ishift); + _value = Indi_ColorCandlesDaily::iCCD(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = Indi_ColorCandlesDaily::iCCD(THIS_PTR, _mode, _ishift); + _value = Indi_ColorCandlesDaily::iCCD(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ColorLine.mqh b/Indicators/Indi_ColorLine.mqh index cf3fa6b4a..60b8b06c2 100644 --- a/Indicators/Indi_ColorLine.mqh +++ b/Indicators/Indi_ColorLine.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" #include "Indi_MA.mqh" @@ -82,18 +82,19 @@ class Indi_ColorLine : public Indicator { /** * OnCalculate-based version of Color Line as there is no built-in one. */ - static double iColorLine(IndicatorData *_indi, int _mode = 0, int _shift = 0) { + static double iColorLine(IndicatorData *_indi, int _mode = 0, int _rel_shift = 0) { INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, ""); // Will return Indi_MA with the same candles source as _indi's. // @fixit There should be Candle attached to MA! Indi_MA *_indi_ma = Indi_MA::GetCached(_indi, 10, 0, MODE_EMA, PRICE_CLOSE); - return iColorLineOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _shift, _cache, _indi_ma); + return iColorLineOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _cache, _indi_ma); } /** * Calculates Color Line on the array of values. */ - static double iColorLineOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _shift, + static double iColorLineOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, IndicatorData *_indi_ma, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -109,16 +110,17 @@ class Indi_ColorLine : public Indicator { _cache.SetPrevCalculated(Indi_ColorLine::Calculate(INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _cache.GetBuffer(1), _indi_ma)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * On-indicator version of Color Line. */ - static double iColorLineOnIndicator(IndicatorData *_indi, int _mode, int _shift, IndicatorData *_obj) { + static double iColorLineOnIndicator(IndicatorData *_indi, int _mode, int _rel_shift, IndicatorData *_obj) { INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, ""); Indi_MA *_indi_ma = _obj.GetDataSource(INDI_MA); - return iColorLineOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _shift, _cache, _indi_ma); + return iColorLineOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _cache, _indi_ma); } /** @@ -218,19 +220,19 @@ class Indi_ColorLine : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = iColorLine(THIS_PTR, _mode, _ishift); + _value = iColorLine(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iColorLine(THIS_PTR, _mode, _ishift); + _value = iColorLine(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_CustomMovingAverage.mqh b/Indicators/Indi_CustomMovingAverage.mqh index 1e997dc45..bc6a8aab6 100644 --- a/Indicators/Indi_CustomMovingAverage.mqh +++ b/Indicators/Indi_CustomMovingAverage.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" // Structs. struct IndiCustomMovingAverageParams : IndicatorParams { @@ -79,13 +79,12 @@ class Indi_CustomMovingAverage : public Indicator /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetSmoothPeriod(), - GetSmoothShift(), GetSmoothMethod() /*]*/, 0, _ishift); + GetSmoothShift(), GetSmoothMethod() /*]*/, 0, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_DEMA.mqh b/Indicators/Indi_DEMA.mqh index 1229b059c..f7f94a417 100644 --- a/Indicators/Indi_DEMA.mqh +++ b/Indicators/Indi_DEMA.mqh @@ -34,7 +34,7 @@ // Includes. #include "../Dict.mqh" #include "../DictObject.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Refs.mqh" #include "../Storage/Objects.h" #include "../Storage/ValueStorage.h" @@ -88,7 +88,11 @@ class Indi_DEMA : public Indicator { * Returns possible data source modes. It is a bit mask of ENUM_IDATA_SOURCE_TYPE. */ unsigned int GetPossibleDataModes() override { +#ifdef __MQL5__ return IDATA_BUILTIN | IDATA_ONCALCULATE | IDATA_ICUSTOM | IDATA_INDICATOR; +#else + return IDATA_ONCALCULATE | IDATA_ICUSTOM | IDATA_INDICATOR; +#endif } /** @@ -100,31 +104,7 @@ class Indi_DEMA : public Indicator { static double iDEMA(string _symbol, ENUM_TIMEFRAMES _tf, unsigned int _period, unsigned int _ma_shift, ENUM_APPLIED_PRICE _applied_price, int _shift = 0, int _mode = 0, IndicatorData *_obj = NULL) { #ifdef __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iDEMA(_symbol, _tf, _period, _ma_shift, _applied_price)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iDEMA(_symbol, _tf, _period, _ma_shift, _applied_price), _mode, _shift); #else if (_obj == nullptr) { Print( @@ -169,9 +149,11 @@ class Indi_DEMA : public Indicator { * On-indicator version of DEMA. */ static double iDEMAOnIndicator(IndicatorData *_indi, int _period, int _ma_shift, ENUM_APPLIED_PRICE _ap, - int _mode = 0, int _shift = 0) { + int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, 2 * _period - 1); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT(_indi, _ap, Util::MakeKey(_period, _ma_shift)); - return iDEMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _period, _ma_shift, _mode, _shift, _cache); + return iDEMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _period, _ma_shift, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_SHORT, ValueStorage &DemaBuffer, @@ -200,29 +182,28 @@ class Indi_DEMA : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: // We're getting DEMA from Price indicator. _value = Indi_DEMA::iDEMA(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetMAShift(), GetAppliedPrice() /*]*/, - _ishift, _mode, THIS_PTR); + ToRelShift(_abs_shift), _mode, THIS_PTR); break; case IDATA_ONCALCULATE: _value = Indi_DEMA::iDEMAOnIndicator(GetDataSource(), /*[*/ GetPeriod(), GetMAShift(), GetAppliedPrice() /*]*/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /*[*/ GetPeriod(), GetMAShift(), - GetAppliedPrice() /*]*/, _mode, _ishift); + GetAppliedPrice() /*]*/, _mode, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: // Calculating DEMA value from specified indicator. _value = Indi_DEMA::iDEMAOnIndicator(GetDataSource(), /*[*/ GetPeriod(), GetMAShift(), GetAppliedPrice() /*]*/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; } return _value; diff --git a/Indicators/Indi_DeMarker.mqh b/Indicators/Indi_DeMarker.mqh index d78e6bc65..3a1f9e638 100644 --- a/Indicators/Indi_DeMarker.mqh +++ b/Indicators/Indi_DeMarker.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -81,47 +81,22 @@ class Indi_DeMarker : public Indicator { #ifdef __MQL4__ return ::iDeMarker(_symbol, _tf, _period, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iDeMarker(_symbol, _tf, _period)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iDeMarker(_symbol, _tf, _period), 0, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = _value = Indi_DeMarker::iDeMarker(GetSymbol(), GetTf(), GetPeriod(), _ishift, THIS_PTR); + _value = _value = Indi_DeMarker::iDeMarker(GetSymbol(), GetTf(), GetPeriod(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Demo.mqh b/Indicators/Indi_Demo.mqh index 46d351fa9..b1c7bbd61 100644 --- a/Indicators/Indi_Demo.mqh +++ b/Indicators/Indi_Demo.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Price/Indi_Price.mqh" /** @@ -79,11 +79,10 @@ class Indi_Demo : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); - double _value = Indi_Demo::iDemo(THIS_PTR, _ishift); + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { + double _value = Indi_Demo::iDemo(THIS_PTR, ToRelShift(_abs_shift)); if (idparams.is_draw) { - draw.DrawLineTo(GetName(), GetCandle() PTR_DEREF GetBarTime(_ishift), _value); + draw.DrawLineTo(GetName(), GetCandle() PTR_DEREF GetBarTime(ToRelShift(_abs_shift)), _value); } return _value; } diff --git a/Indicators/Indi_DetrendedPrice.mqh b/Indicators/Indi_DetrendedPrice.mqh index 70030acbc..2cdc29e1c 100644 --- a/Indicators/Indi_DetrendedPrice.mqh +++ b/Indicators/Indi_DetrendedPrice.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Indi_MA.mqh" // Structs. @@ -82,16 +82,18 @@ class Indi_DetrendedPrice : public Indicator { /** * Built-in version of DPO. */ - static double iDPO(IndicatorData *_indi, int _period, ENUM_APPLIED_PRICE _ap, int _mode = 0, int _shift = 0) { + static double iDPO(IndicatorData *_indi, int _period, ENUM_APPLIED_PRICE _ap, int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _period + _rel_shift); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT(_indi, _ap, Util::MakeKey(_indi.GetId())); - return iDPOOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _period, _ap, _mode, _shift, _cache); + return iDPOOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _period, _ap, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * Calculates DPO on the array of values. */ static double iDPOOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _period, ENUM_APPLIED_PRICE _ap, int _mode, - int _shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { + int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_price); if (!_cache.HasBuffers()) { @@ -105,7 +107,7 @@ class Indi_DetrendedPrice : public Indicator { _cache.SetPrevCalculated(Indi_DetrendedPrice::Calculate( INDICATOR_CALCULATE_GET_PARAMS_SHORT, _cache.GetBuffer(0), _cache.GetBuffer(1), _period)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -135,20 +137,19 @@ class Indi_DetrendedPrice : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = iDPO(THIS_PTR, GetPeriod(), GetAppliedPrice(), _mode, _ishift); + _value = iDPO(THIS_PTR, GetPeriod(), GetAppliedPrice(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iDPO(THIS_PTR, GetPeriod(), GetAppliedPrice(), _mode, _ishift); + _value = iDPO(THIS_PTR, GetPeriod(), GetAppliedPrice(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Drawer.mqh b/Indicators/Indi_Drawer.mqh index 2ef66b6f0..d12dc6368 100644 --- a/Indicators/Indi_Drawer.mqh +++ b/Indicators/Indi_Drawer.mqh @@ -22,7 +22,7 @@ // Includes. #include "../DictStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Redis.mqh" #include "../Task/TaskAction.h" #include "Indi_Drawer.struct.h" @@ -111,8 +111,11 @@ class Indi_Drawer : public Indicator { return false; } - virtual void OnTick() { - Indicator::OnTick(); + /** + * Called when new tick is retrieved from attached data source. + */ + void OnTick(int _global_tick_index) override { + Indicator::OnTick(_global_tick_index); /* @fixme TaskActionEntry action(INDI_ACTION_SET_VALUE); @@ -191,15 +194,15 @@ class Indi_Drawer : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_Drawer::iDrawer(GetSymbol(), GetTf(), _ishift, THIS_PTR); + _value = Indi_Drawer::iDrawer(GetSymbol(), GetTf(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_INDICATOR: - _value = Indi_Drawer::iDrawerOnIndicator(GetDataSource(), THIS_PTR, GetSymbol(), GetTf(), _ishift); + _value = + Indi_Drawer::iDrawerOnIndicator(GetDataSource(), THIS_PTR, GetSymbol(), GetTf(), ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Drawer.struct.h b/Indicators/Indi_Drawer.struct.h index 473209d9f..2014e8894 100644 --- a/Indicators/Indi_Drawer.struct.h +++ b/Indicators/Indi_Drawer.struct.h @@ -26,8 +26,8 @@ */ // Includes. -#include "../Indicator.struct.h" -#include "../SerializerNode.enum.h" +#include "../Indicator/Indicator.struct.h" +#include "../Serializer/SerializerNode.enum.h" // Structs. diff --git a/Indicators/Indi_Envelopes.mqh b/Indicators/Indi_Envelopes.mqh index f21fc1af0..8cc78a95b 100644 --- a/Indicators/Indi_Envelopes.mqh +++ b/Indicators/Indi_Envelopes.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/Singleton.h" #include "Indi_MA.mqh" #include "Indi_PriceFeeder.mqh" @@ -137,32 +137,9 @@ class Indi_Envelopes : public Indicator { _mode = 1; break; } - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iEnvelopes(_symbol, _tf, _ma_period, _ma_shift, _ma_method, _ap, _deviation)) == - INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + + INDICATOR_BUILTIN_CALL_AND_RETURN(::iEnvelopes(_symbol, _tf, _ma_period, _ma_shift, _ma_method, _ap, _deviation), + _mode, _shift); #endif } @@ -173,6 +150,8 @@ class Indi_Envelopes : public Indicator { int _mode, // (MT4 _mode): 0 - MODE_MAIN, 1 - MODE_UPPER, 2 - MODE_LOWER; (MT5 // _mode): 0 - UPPER_LINE, 1 - LOWER_LINE int _shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_source, _shift + _ma_shift + _ma_period); + return iEnvelopesOnArray(_source.GetSpecificAppliedPriceValueStorage(_ap, _target), 0, _ma_period, _ma_method, _ma_shift, _deviation, _mode, _shift, _target PTR_DEREF GetCache()); } @@ -225,28 +204,28 @@ class Indi_Envelopes : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_Envelopes::iEnvelopes(GetSymbol(), GetTf(), GetMAPeriod(), GetMAMethod(), GetMAShift(), - GetAppliedPrice(), GetDeviation(), _mode, _ishift, THIS_PTR); + GetAppliedPrice(), GetDeviation(), _mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: // @todo Is cache needed here? _value = Indi_Envelopes::iEnvelopesOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), GetMAPeriod(), GetMAMethod(), GetAppliedPrice(), GetMAShift(), GetDeviation(), - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /**/ GetMAPeriod(), - GetMAMethod(), GetMAShift(), GetAppliedPrice(), GetDeviation() /**/, _mode, _ishift); + _value = + iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /**/ GetMAPeriod(), + GetMAMethod(), GetMAShift(), GetAppliedPrice(), GetDeviation() /**/, _mode, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: _value = Indi_Envelopes::iEnvelopesOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), GetMAPeriod(), GetMAMethod(), GetAppliedPrice(), GetMAShift(), GetDeviation(), - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Force.mqh b/Indicators/Indi_Force.mqh index ad0caebda..5a6b02fa7 100644 --- a/Indicators/Indi_Force.mqh +++ b/Indicators/Indi_Force.mqh @@ -32,7 +32,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -98,48 +98,23 @@ class Indi_Force : public Indicator { #ifdef __MQL4__ return ::iForce(_symbol, _tf, _period, _ma_method, _applied_price, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iForce(_symbol, _tf, _period, _ma_method, VOLUME_TICK)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iForce(_symbol, _tf, _period, _ma_method, VOLUME_TICK), 0, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = - Indi_Force::iForce(GetSymbol(), GetTf(), GetPeriod(), GetMAMethod(), GetAppliedPrice(), _ishift, THIS_PTR); + _value = Indi_Force::iForce(GetSymbol(), GetTf(), GetPeriod(), GetMAMethod(), GetAppliedPrice(), + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetMAMethod(), GetAppliedPrice(), VOLUME_TICK /*]*/, 0, _ishift); + GetMAMethod(), GetAppliedPrice(), VOLUME_TICK /*]*/, 0, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_FractalAdaptiveMA.mqh b/Indicators/Indi_FractalAdaptiveMA.mqh index 51c2320f2..90457a785 100644 --- a/Indicators/Indi_FractalAdaptiveMA.mqh +++ b/Indicators/Indi_FractalAdaptiveMA.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" // Structs. @@ -91,9 +91,10 @@ class Indi_FrAMA : public Indicator { * Built-in version of FrAMA. */ static double iFrAMA(string _symbol, ENUM_TIMEFRAMES _tf, int _ma_period, int _ma_shift, ENUM_APPLIED_PRICE _ap, - int _mode = 0, int _shift = 0, IndicatorData *_obj = NULL) { + int _mode = 0, int _rel_shift = 0, IndicatorData *_obj = NULL) { #ifdef __MQL5__ - INDICATOR_BUILTIN_CALL_AND_RETURN(::iFrAMA(_symbol, _tf, _ma_period, _ma_shift, _ap), _mode, _shift); + INDICATOR_BUILTIN_CALL_AND_RETURN(::iFrAMA(_symbol, _tf, _ma_period, _ma_shift, _ap), _mode, + _obj PTR_DEREF ToAbsShift(_rel_shift)); #else if (_obj == nullptr) { Print( @@ -102,8 +103,10 @@ class Indi_FrAMA : public Indicator { DebugBreak(); return 0; } + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_obj, 2 * _ma_period); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_obj, Util::MakeKey(_ma_period, _ma_shift, (int)_ap)); - return iFrAMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _ma_period, _ma_shift, _ap, _mode, _shift, _cache); + return iFrAMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _ma_period, _ma_shift, _ap, _mode, + _obj PTR_DEREF ToAbsShift(_rel_shift), _cache); #endif } @@ -111,7 +114,7 @@ class Indi_FrAMA : public Indicator { * Calculates FrAMA on the array of values. */ static double iFrAMAOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _ma_period, int _ma_shift, ENUM_APPLIED_PRICE _ap, - int _mode, int _shift, IndicatorCalculateCache *_cache, + int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -126,20 +129,22 @@ class Indi_FrAMA : public Indicator { _cache.SetPrevCalculated(Indi_FrAMA::Calculate(INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _ma_period, _ma_shift, _ap)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * On-indicator version of FrAMA. */ static double iFrAMAOnIndicator(IndicatorData *_indi, int _ma_period, int _ma_shift, ENUM_APPLIED_PRICE _ap, - int _mode = 0, int _shift = 0) { + int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, 2 * _ma_period); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey(_ma_period, _ma_shift, (int)_ap)); - return iFrAMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _ma_period, _ma_shift, _ap, _mode, _shift, _cache); + return iFrAMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _ma_period, _ma_shift, _ap, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_LONG, ValueStorage &FrAmaBuffer, int InpPeriodFrAMA, - int InpShift, ENUM_APPLIED_PRICE InpAppliedPrice) { + int _ishift, ENUM_APPLIED_PRICE _applied_price) { if (rates_total < 2 * InpPeriodFrAMA) return (0); int start, i; @@ -147,7 +152,7 @@ class Indi_FrAMA : public Indicator { if (prev_calculated == 0) { start = 2 * InpPeriodFrAMA - 1; for (i = 0; i <= start; i++) - FrAmaBuffer[i] = AppliedPriceValueStorage::GetApplied(open, high, low, close, i, InpAppliedPrice); + FrAmaBuffer[i] = AppliedPriceValueStorage::GetApplied(open, high, low, close, i, _applied_price); } else start = prev_calculated - 1; @@ -165,7 +170,7 @@ class Indi_FrAMA : public Indicator { double n3 = (hi3 - lo3) / (2 * InpPeriodFrAMA); double d = (MathLog(n1 + n2) - MathLog(n3)) / math_log_2; double alfa = MathExp(-4.6 * (d - 1.0)); - double _iprice = AppliedPriceValueStorage::GetApplied(open, high, low, close, i, InpAppliedPrice); + double _iprice = AppliedPriceValueStorage::GetApplied(open, high, low, close, i, _applied_price); FrAmaBuffer[i] = alfa * _iprice + (1 - alfa) * FrAmaBuffer[i - 1].Get(); } @@ -177,23 +182,24 @@ class Indi_FrAMA : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = - iFrAMA(GetSymbol(), GetTf(), GetPeriod(), GetFRAMAShift(), GetAppliedPrice(), _mode, _ishift, THIS_PTR); + _value = iFrAMA(GetSymbol(), GetTf(), GetPeriod(), GetFRAMAShift(), GetAppliedPrice(), _mode, + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: - _value = iFrAMAOnIndicator(GetDataSource(), GetPeriod(), GetFRAMAShift(), GetAppliedPrice(), _mode, _ishift); + _value = iFrAMAOnIndicator(GetDataSource(), GetPeriod(), GetFRAMAShift(), GetAppliedPrice(), _mode, + ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetFRAMAShift() /*]*/, 0, _ishift); + GetFRAMAShift() /*]*/, 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iFrAMAOnIndicator(GetDataSource(), GetPeriod(), GetFRAMAShift(), GetAppliedPrice(), _mode, _ishift); + _value = iFrAMAOnIndicator(GetDataSource(), GetPeriod(), GetFRAMAShift(), GetAppliedPrice(), _mode, + ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Fractals.mqh b/Indicators/Indi_Fractals.mqh index fa08a645a..636d36fb5 100644 --- a/Indicators/Indi_Fractals.mqh +++ b/Indicators/Indi_Fractals.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -97,46 +97,23 @@ class Indi_Fractals : public Indicator { #ifdef __MQL4__ return ::iFractals(_symbol, _tf, _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iFractals(_symbol, _tf)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iFractals(_symbol, _tf), _mode, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = _value = Indi_Fractals::iFractals(GetSymbol(), GetTf(), (ENUM_LO_UP_LINE)_mode, _ishift, THIS_PTR); + _value = + Indi_Fractals::iFractals(GetSymbol(), GetTf(), (ENUM_LO_UP_LINE)_mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); @@ -159,7 +136,7 @@ class Indi_Fractals : public Indicator { * Checks if indicator entry values are valid. */ virtual bool IsValidEntry(IndicatorDataEntry &_entry) { - double _wrong_value = (double)NULL; + double _wrong_value = DBL_MAX; #ifdef __MQL4__ // In MT4, the empty value for iFractals is 0, not EMPTY_VALUE=DBL_MAX as in MT5. // So the wrong value is the opposite. diff --git a/Indicators/Indi_Gator.mqh b/Indicators/Indi_Gator.mqh index d573da0c9..92bda24ea 100644 --- a/Indicators/Indi_Gator.mqh +++ b/Indicators/Indi_Gator.mqh @@ -28,7 +28,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -166,46 +166,22 @@ class Indi_Gator : public Indicator { return ::iGator(_symbol, _tf, _jaw_period, _jaw_shift, _teeth_period, _teeth_shift, _lips_period, _lips_shift, _ma_method, _applied_price, _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iGator(_symbol, _tf, _jaw_period, _jaw_shift, _teeth_period, _teeth_shift, _lips_period, - _lips_shift, _ma_method, _applied_price)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iGator(_symbol, _tf, _jaw_period, _jaw_shift, _teeth_period, _teeth_shift, + _lips_period, _lips_shift, _ma_method, _applied_price), + _mode, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_Gator::iGator(GetSymbol(), GetTf(), GetJawPeriod(), GetJawShift(), GetTeethPeriod(), GetTeethShift(), GetLipsPeriod(), GetLipsShift(), GetMAMethod(), GetAppliedPrice(), - (ENUM_GATOR_HISTOGRAM)_mode, _ishift, THIS_PTR); + (ENUM_GATOR_HISTOGRAM)_mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /**/ @@ -213,7 +189,7 @@ class Indi_Gator : public Indicator { GetLipsShift(), GetMAMethod(), GetAppliedPrice() /**/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_HeikenAshi.mqh b/Indicators/Indi_HeikenAshi.mqh index 84e6a8969..3c89e0fc6 100644 --- a/Indicators/Indi_HeikenAshi.mqh +++ b/Indicators/Indi_HeikenAshi.mqh @@ -28,7 +28,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" // Enums. @@ -143,46 +143,23 @@ class Indi_HeikenAshi : public Indicator { } return ::iCustom(_symbol, _tf, "Heiken Ashi", _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iCustom(_symbol, _tf, "Examples\\Heiken_Ashi")) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iCustom(_symbol, _tf, "Examples\\Heiken_Ashi"), _mode, _shift); #endif } /** * OnCalculate-based version of Color Heiken Ashi as there is no built-in one. */ - static double iHeikenAshi(IndicatorData *_indi, int _mode = 0, int _shift = 0) { + static double iHeikenAshi(IndicatorData *_indi, int _mode = 0, int _rel_shift = 0) { INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, ""); - return iHeikenAshiOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _shift, _cache); + return iHeikenAshiOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _cache); } /** * Calculates Heiken Ashi on the array of values. */ - static double iHeikenAshiOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _shift, + static double iHeikenAshiOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -198,7 +175,7 @@ class Indi_HeikenAshi : public Indicator { INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _cache.GetBuffer(1), _cache.GetBuffer(2), _cache.GetBuffer(3), _cache.GetBuffer(4))); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -239,9 +216,8 @@ class Indi_HeikenAshi : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = HA_OPEN, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = HA_OPEN, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: @@ -262,17 +238,18 @@ class Indi_HeikenAshi : public Indicator { break; } #endif - _value = Indi_HeikenAshi::iHeikenAshi(THIS_PTR, _mode, _ishift); + _value = Indi_HeikenAshi::iHeikenAshi(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM_LEGACY: _value = Indi_HeikenAshi::iCustomLegacyHeikenAshi(GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, - _ishift, THIS_PTR); + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_INDICATOR: - _value = Indi_HeikenAshi::iHeikenAshi(THIS_PTR, _mode, _ishift); + _value = Indi_HeikenAshi::iHeikenAshi(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Ichimoku.mqh b/Indicators/Indi_Ichimoku.mqh index 8924096f8..d54eea444 100644 --- a/Indicators/Indi_Ichimoku.mqh +++ b/Indicators/Indi_Ichimoku.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -136,48 +136,24 @@ class Indi_Ichimoku : public Indicator { #ifdef __MQL4__ return ::iIchimoku(_symbol, _tf, _tenkan_sen, _kijun_sen, _senkou_span_b, _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iIchimoku(_symbol, _tf, _tenkan_sen, _kijun_sen, _senkou_span_b)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iIchimoku(_symbol, _tf, _tenkan_sen, _kijun_sen, _senkou_span_b), _mode, + _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_Ichimoku::iIchimoku(GetSymbol(), GetTf(), GetTenkanSen(), GetKijunSen(), GetSenkouSpanB(), _mode, - _ishift, THIS_PTR); + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetTenkanSen(), - GetKijunSen(), GetSenkouSpanB() /*]*/, _mode, _ishift); + GetKijunSen(), GetSenkouSpanB() /*]*/, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Killzones.mqh b/Indicators/Indi_Killzones.mqh index d8c43a299..3f9dbe40a 100644 --- a/Indicators/Indi_Killzones.mqh +++ b/Indicators/Indi_Killzones.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Market.struct.h" // Defines enumerations. @@ -131,10 +131,9 @@ class Indi_Killzones : public Indicator { /** * Returns the indicator's value. */ - IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { float _value = FLT_MAX; int _index = (int)_mode / 2; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: // Builtin mode not supported. @@ -144,8 +143,8 @@ class Indi_Killzones : public Indicator { ikt.Set(::TimeGMT()); if (ikt.CheckHours(_index)) { // Pass values to check for new highs or lows. - ikt.Update(_mode % 2 == 0 ? (float)GetCandle() PTR_DEREF GetHigh(_ishift) - : (float)GetCandle() PTR_DEREF GetLow(_ishift), + ikt.Update(_mode % 2 == 0 ? (float)GetCandle() PTR_DEREF GetHigh(ToRelShift(_abs_shift)) + : (float)GetCandle() PTR_DEREF GetLow(ToRelShift(_abs_shift)), _index); } // Set a final value. diff --git a/Indicators/Indi_MA.mqh b/Indicators/Indi_MA.mqh index 25cc36000..1af92cd2e 100644 --- a/Indicators/Indi_MA.mqh +++ b/Indicators/Indi_MA.mqh @@ -27,7 +27,7 @@ // Includes. #include "../Dict.mqh" #include "../DictObject.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Refs.mqh" #include "../Storage/Singleton.h" #include "../Storage/ValueStorage.h" @@ -40,10 +40,10 @@ double iMA(string _symbol, int _tf, int _ma_period, int _ma_shift, int _ma_metho return Indi_MA::iMA(_symbol, (ENUM_TIMEFRAMES)_tf, _ma_period, _ma_shift, (ENUM_MA_METHOD)_ma_method, (ENUM_APPLIED_PRICE)_ap, _shift); } -double iMAOnArray(double &_arr[], int _total, int _period, int _ma_shift, int _ma_method, int _shift, +double iMAOnArray(double &_arr[], int _total, int _period, int _ma_shift, int _ma_method, int _abs_shift, IndicatorCalculateCache *_cache = NULL) { ResetLastError(); - return Indi_MA::iMAOnArray(_arr, _total, _period, _ma_shift, _ma_method, _shift, _cache); + return Indi_MA::iMAOnArray(_arr, _total, _period, _ma_shift, _ma_method, _abs_shift, _cache); } #endif @@ -54,8 +54,13 @@ struct IndiMAParams : IndicatorParams { ENUM_MA_METHOD ma_method; ENUM_APPLIED_PRICE applied_array; // Struct constructors. - IndiMAParams(unsigned int _period = 13, int _ma_shift = 10, ENUM_MA_METHOD _ma_method = MODE_SMA, - ENUM_APPLIED_PRICE _ap = PRICE_OPEN, int _shift = 0) + /** + * Regarding _ma_shift and _shift: + * @see https://www.mql5.com/en/forum/146006#comment_3685589 + * "Always use MA shift 0 (ignore it) and use the regular shift, unless you are placing it on the chart for visual". + */ + IndiMAParams(unsigned int _period = 13, int _ma_shift = 0, ENUM_MA_METHOD _ma_method = MODE_SMA, + ENUM_APPLIED_PRICE _ap = PRICE_OPEN, int _shift = 10) : period(_period), ma_shift(_ma_shift), ma_method(_ma_method), applied_array(_ap), IndicatorParams(INDI_MA) { shift = _shift; SetCustomIndicatorName("Examples\\Moving Average"); @@ -117,31 +122,8 @@ class Indi_MA : public Indicator { #ifdef __MQL4__ return ::iMA(_symbol, _tf, _ma_period, _ma_shift, _ma_method, _applied_price, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iMA(_symbol, _tf, _ma_period, _ma_shift, _ma_method, _applied_price)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iMA(_symbol, _tf, _ma_period, _ma_shift, _ma_method, _applied_price), 0, + _shift); #endif } @@ -152,6 +134,7 @@ class Indi_MA : public Indicator { unsigned int ma_period, unsigned int ma_shift, ENUM_MA_METHOD ma_method, // (MT4/MT5): MODE_SMA, MODE_EMA, MODE_SMMA, MODE_LWMA ENUM_APPLIED_PRICE _ap, int shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_source, int(ma_period + ma_shift + shift)); ValueStorage *_data = (ValueStorage *)_source.GetSpecificAppliedPriceValueStorage(_ap, _target); return iMAOnArray(_data, 0, ma_period, ma_shift, ma_method, shift, _target PTR_DEREF GetCache()); } @@ -284,23 +267,23 @@ class Indi_MA : public Indicator { * Calculates Simple Moving Average (SMA). The same as in "Example Moving Average" indicator. */ static void CalculateSimpleMA(int rates_total, int prev_calculated, int begin, ValueStorage &price, - ValueStorage &ExtLineBuffer, int _ma_period) { + ValueStorage &_line_buff, int _ma_period) { int i, start; // First calculation or number of bars was changed. if (prev_calculated == 0) { start = _ma_period + begin; // Set empty value for first start bars. - for (i = 0; i < start - 1; i++) ExtLineBuffer[i] = 0.0; + for (i = 0; i < start - 1; i++) _line_buff[i] = 0.0; // Calculate first visible value. double first_value = 0; for (i = begin; i < start; i++) first_value += price[i].Get(); first_value /= _ma_period; - ExtLineBuffer[start - 1] = first_value; + _line_buff[start - 1] = first_value; } else start = prev_calculated - 1; // Main loop. for (i = start; i < rates_total && !IsStopped(); i++) { - ExtLineBuffer[i] = ExtLineBuffer[i - 1] + (price[i] - price[i - _ma_period]) / _ma_period; + _line_buff[i] = _line_buff[i - 1] + (price[i] - price[i - _ma_period]) / _ma_period; } } @@ -308,21 +291,21 @@ class Indi_MA : public Indicator { * Calculates Exponential Moving Average (EMA). The same as in "Example Moving Average" indicator. */ static void CalculateEMA(int rates_total, int prev_calculated, int begin, ValueStorage &price, - ValueStorage &ExtLineBuffer, int _ma_period) { + ValueStorage &_line_buff, int _ma_period) { int i, limit; double SmoothFactor = 2.0 / (1.0 + _ma_period); // First calculation or number of bars was changed. if (prev_calculated == 0) { limit = _ma_period + begin; - ExtLineBuffer[begin] = price[begin]; + _line_buff[begin] = price[begin]; for (i = begin + 1; i < limit; i++) { - ExtLineBuffer[i] = price[i] * SmoothFactor + ExtLineBuffer[i - 1] * (1.0 - SmoothFactor); + _line_buff[i] = price[i] * SmoothFactor + _line_buff[i - 1] * (1.0 - SmoothFactor); } } else limit = prev_calculated - 1; // Main loop. for (i = limit; i < rates_total && !IsStopped(); i++) { - ExtLineBuffer[i] = price[i] * SmoothFactor + ExtLineBuffer[i - 1] * (1.0 - SmoothFactor); + _line_buff[i] = price[i] * SmoothFactor + _line_buff[i - 1] * (1.0 - SmoothFactor); } } @@ -330,7 +313,7 @@ class Indi_MA : public Indicator { * Calculates Linearly Weighted Moving Average (LWMA). The same as in "Example Moving Average" indicator. */ static void CalculateLWMA(int rates_total, int prev_calculated, int begin, ValueStorage &price, - ValueStorage &ExtLineBuffer, int _ma_period) { + ValueStorage &_line_buff, int _ma_period) { int i, limit; static int weightsum; double sum; @@ -339,7 +322,7 @@ class Indi_MA : public Indicator { weightsum = 0; limit = _ma_period + begin; // Set empty value for first limit bars. - for (i = 0; i < limit; i++) ExtLineBuffer[i] = 0.0; + for (i = 0; i < limit; i++) _line_buff[i] = 0.0; // Calculate first visible value. double firstValue = 0; for (i = begin; i < limit; i++) { @@ -348,14 +331,14 @@ class Indi_MA : public Indicator { firstValue += k * price[i].Get(); } firstValue /= (double)weightsum; - ExtLineBuffer[limit - 1] = firstValue; + _line_buff[limit - 1] = firstValue; } else limit = prev_calculated - 1; // Main loop. for (i = limit; i < rates_total && !IsStopped(); i++) { sum = 0; for (int j = 0; j < _ma_period; j++) sum += (_ma_period - j) * price[i - j].Get(); - ExtLineBuffer[i] = sum / weightsum; + _line_buff[i] = sum / weightsum; } //--- } @@ -364,23 +347,23 @@ class Indi_MA : public Indicator { * Calculates Smoothed Moving Average (SMMA). The same as in "Example Moving Average" indicator. */ static void CalculateSmoothedMA(int rates_total, int prev_calculated, int begin, ValueStorage &price, - ValueStorage &ExtLineBuffer, int _ma_period) { + ValueStorage &_line_buff, int _ma_period) { int i, limit; // First calculation or number of bars was changed. if (prev_calculated == 0) { limit = _ma_period + begin; // Set empty value for first limit bars. - for (i = 0; i < limit - 1; i++) ExtLineBuffer[i] = 0.0; + for (i = 0; i < limit - 1; i++) _line_buff[i] = 0.0; // Calculate first visible value. double firstValue = 0; for (i = begin; i < limit; i++) firstValue += price[i].Get(); firstValue /= _ma_period; - ExtLineBuffer[limit - 1] = firstValue; + _line_buff[limit - 1] = firstValue; } else limit = prev_calculated - 1; // Main loop. for (i = limit; i < rates_total && !IsStopped(); i++) - ExtLineBuffer[i] = (ExtLineBuffer[i - 1] * (_ma_period - 1) + price[i].Get()) / _ma_period; + _line_buff[i] = (_line_buff[i - 1] * (_ma_period - 1) + price[i].Get()) / _ma_period; //--- } @@ -650,26 +633,25 @@ class Indi_MA : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_MA::iMA(_Symbol, GetTf(), GetPeriod(), GetMAShift(), GetMAMethod(), GetAppliedPrice(), - _ishift, THIS_PTR); + _value = Indi_MA::iMA(GetSymbol(), GetTf(), GetPeriod(), GetMAShift(), GetMAMethod(), GetAppliedPrice(), + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: - _value = Indi_MA::iMAOnIndicator(THIS_PTR, GetDataSource(), _Symbol, GetTf(), GetPeriod(), GetMAShift(), - GetMAMethod(), GetAppliedPrice(), _ishift); + _value = Indi_MA::iMAOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), GetMAShift(), + GetMAMethod(), GetAppliedPrice(), ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), - GetMAShift(), GetMAMethod(), GetAppliedPrice() /* ] */, 0, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), + GetMAShift(), GetMAMethod(), GetAppliedPrice() /* ] */, 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: // Calculating MA value from specified indicator. - _value = Indi_MA::iMAOnIndicator(THIS_PTR, GetDataSource(), _Symbol, GetTf(), GetPeriod(), GetMAShift(), - GetMAMethod(), GetAppliedPrice(), _ishift); + _value = Indi_MA::iMAOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), GetMAShift(), + GetMAMethod(), GetAppliedPrice(), ToRelShift(_abs_shift)); break; } diff --git a/Indicators/Indi_MACD.mqh b/Indicators/Indi_MACD.mqh index 338517687..c7977ddd0 100644 --- a/Indicators/Indi_MACD.mqh +++ b/Indicators/Indi_MACD.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -97,50 +97,25 @@ class Indi_MACD : public Indicator { #ifdef __MQL4__ return ::iMACD(_symbol, _tf, _ema_fast_period, _ema_slow_period, _signal_period, _applied_price, _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iMACD(_symbol, _tf, _ema_fast_period, _ema_slow_period, _signal_period, _applied_price)) == - INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN( + ::iMACD(_symbol, _tf, _ema_fast_period, _ema_slow_period, _signal_period, _applied_price), _mode, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_MACD::iMACD(GetSymbol(), GetTf(), GetEmaFastPeriod(), GetEmaSlowPeriod(), GetSignalPeriod(), - GetAppliedPrice(), (ENUM_SIGNAL_LINE)_mode, _ishift, THIS_PTR); + GetAppliedPrice(), (ENUM_SIGNAL_LINE)_mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetEmaFastPeriod(), - GetEmaSlowPeriod(), GetSignalPeriod(), GetAppliedPrice() /*]*/, _mode, _ishift); + GetEmaSlowPeriod(), GetSignalPeriod(), GetAppliedPrice() /*]*/, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_MFI.mqh b/Indicators/Indi_MFI.mqh index 73f20e646..01aa9fc05 100644 --- a/Indicators/Indi_MFI.mqh +++ b/Indicators/Indi_MFI.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -92,51 +92,27 @@ class Indi_MFI : public Indicator { #ifdef __MQL4__ return ::iMFI(_symbol, _tf, _period, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iMFI(_symbol, _tf, _period, VOLUME_TICK)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iMFI(_symbol, _tf, _period, VOLUME_TICK), 0, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: #ifdef __MQL4__ - _value = Indi_MFI::iMFI(GetSymbol(), GetTf(), GetPeriod(), _ishift); + _value = Indi_MFI::iMFI(GetSymbol(), GetTf(), GetPeriod(), ToRelShift(_abs_shift)); #else // __MQL5__ - _value = Indi_MFI::iMFI(GetSymbol(), GetTf(), GetPeriod(), GetAppliedVolume(), _ishift, THIS_PTR); + _value = + Indi_MFI::iMFI(GetSymbol(), GetTf(), GetPeriod(), GetAppliedVolume(), ToRelShift(_abs_shift), THIS_PTR); #endif break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - VOLUME_TICK /*]*/, 0, _ishift); + VOLUME_TICK /*]*/, 0, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_MassIndex.mqh b/Indicators/Indi_MassIndex.mqh index c1c85d1f8..3a82a39b1 100644 --- a/Indicators/Indi_MassIndex.mqh +++ b/Indicators/Indi_MassIndex.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" #include "Indi_MA.mqh" @@ -87,17 +87,18 @@ class Indi_MassIndex : public Indicator { * OnCalculate-based version of Mass Index as there is no built-in one. */ static double iMI(IndicatorData *_indi, int _period, int _second_period, int _sum_period, int _mode = 0, - int _shift = 0) { + int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _sum_period + _period + _second_period - 3); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey(_period, _second_period, _sum_period)); - return iMIOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _period, _second_period, _sum_period, _mode, _shift, - _cache); + return iMIOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _period, _second_period, _sum_period, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * Calculates Mass Index on the array of values. */ static double iMIOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _period, int _second_period, int _sum_period, int _mode, - int _shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { + int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); if (!_cache.HasBuffers()) { @@ -112,7 +113,7 @@ class Indi_MassIndex : public Indicator { INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _cache.GetBuffer(1), _cache.GetBuffer(2), _cache.GetBuffer(3), _period, _second_period, _sum_period)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -176,20 +177,21 @@ class Indi_MassIndex : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = Indi_MassIndex::iMI(THIS_PTR, GetPeriod(), GetSecondPeriod(), GetSumPeriod(), _mode, _ishift); + _value = Indi_MassIndex::iMI(THIS_PTR, GetPeriod(), GetSecondPeriod(), GetSumPeriod(), _mode, + ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetSecondPeriod(), GetSumPeriod() /*]*/, _mode, _ishift); + GetSecondPeriod(), GetSumPeriod() /*]*/, _mode, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = Indi_MassIndex::iMI(THIS_PTR, GetPeriod(), GetSecondPeriod(), GetSumPeriod(), _mode, _ishift); + _value = Indi_MassIndex::iMI(THIS_PTR, GetPeriod(), GetSecondPeriod(), GetSumPeriod(), _mode, + ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Momentum.mqh b/Indicators/Indi_Momentum.mqh index fc62b5e7c..6d6d54496 100644 --- a/Indicators/Indi_Momentum.mqh +++ b/Indicators/Indi_Momentum.mqh @@ -30,7 +30,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Indi_PriceFeeder.mqh" #ifndef __MQL4__ @@ -98,36 +98,14 @@ class Indi_Momentum : public Indicator { #ifdef __MQL4__ return ::iMomentum(_symbol, _tf, _period, _ap, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iMomentum(_symbol, _tf, _period, _ap)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iMomentum(_symbol, _tf, _period, _ap), 0, _shift); #endif } static double iMomentumOnIndicator(IndicatorData *_indi, string _symbol, ENUM_TIMEFRAMES _tf, unsigned int _period, int _mode, int _shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _period); + double _indi_value_buffer[]; IndicatorDataEntry _entry(_indi.GetModeCount()); @@ -136,6 +114,8 @@ class Indi_Momentum : public Indicator { ArrayResize(_indi_value_buffer, _period); for (int i = 0; i < (int)_period; i++) { + // Print(": Getting data from ", _indi PTR_DEREF GetFullName(), " from shift ", i); + // Getting value from single, selected buffer. _indi_value_buffer[i] = _indi[i].GetValue(_mode); } @@ -158,35 +138,34 @@ class Indi_Momentum : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: // @fixit Somehow shift isn't used neither in MT4 nor MT5. - _value = Indi_Momentum::iMomentum(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), iparams.shift + _ishift, - THIS_PTR); + _value = Indi_Momentum::iMomentum(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), + iparams.shift + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: // @fixit Somehow shift isn't used neither in MT4 nor MT5. _value = Indi_Momentum::iMomentumOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), - iparams.shift + _shift); + iparams.shift + ToRelShift(_abs_shift)); if (idparams.is_draw) { - draw.DrawLineTo(StringFormat("%s", GetName()), GetBarTime(iparams.shift + _shift), _value, 1); + draw.DrawLineTo(StringFormat("%s", GetName()), GetBarTime(iparams.shift + ToRelShift(_abs_shift)), _value, 1); } break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: ValidateSelectedDataSource(); // @fixit Somehow shift isn't used neither in MT4 nor MT5. _value = Indi_Momentum::iMomentumOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), - iparams.shift + _shift); + iparams.shift + ToRelShift(_abs_shift)); if (idparams.is_draw) { - draw.DrawLineTo(StringFormat("%s", GetName()), GetBarTime(iparams.shift + _shift), _value, 1); + draw.DrawLineTo(StringFormat("%s", GetName()), GetBarTime(iparams.shift + ToRelShift(_abs_shift)), _value, 1); } break; } diff --git a/Indicators/Indi_OBV.mqh b/Indicators/Indi_OBV.mqh index 48f259884..2d82a0001 100644 --- a/Indicators/Indi_OBV.mqh +++ b/Indicators/Indi_OBV.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -107,51 +107,26 @@ class Indi_OBV : public Indicator { #ifdef __MQL4__ return ::iOBV(_symbol, _tf, _applied, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iOBV(_symbol, _tf, _applied)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iOBV(_symbol, _tf, _applied), 0, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: #ifdef __MQL4__ - _value = Indi_OBV::iOBV(GetSymbol(), GetTf(), GetAppliedPrice(), _ishift); + _value = Indi_OBV::iOBV(GetSymbol(), GetTf(), GetAppliedPrice(), ToRelShift(_abs_shift), THIS_PTR); #else // __MQL5__ - _value = Indi_OBV::iOBV(GetSymbol(), GetTf(), GetAppliedVolume(), _ishift, THIS_PTR); + _value = Indi_OBV::iOBV(GetSymbol(), GetTf(), GetAppliedVolume(), ToRelShift(_abs_shift), THIS_PTR); #endif break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ VOLUME_TICK /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_OsMA.mqh b/Indicators/Indi_OsMA.mqh index b88ec377b..4c6e79dcb 100644 --- a/Indicators/Indi_OsMA.mqh +++ b/Indicators/Indi_OsMA.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -91,50 +91,25 @@ class Indi_OsMA : public Indicator { #ifdef __MQL4__ return ::iOsMA(_symbol, _tf, _ema_fast_period, _ema_slow_period, _signal_period, _applied_price, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iOsMA(_symbol, _tf, _ema_fast_period, _ema_slow_period, _signal_period, _applied_price)) == - INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN( + ::iOsMA(_symbol, _tf, _ema_fast_period, _ema_slow_period, _signal_period, _applied_price), 0, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_OsMA::iOsMA(GetSymbol(), GetTf(), GetEmaFastPeriod(), GetEmaSlowPeriod(), GetSignalPeriod(), - GetAppliedPrice(), _ishift, THIS_PTR); + GetAppliedPrice(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetEmaFastPeriod(), - GetEmaSlowPeriod(), GetSignalPeriod(), GetAppliedPrice() /*]*/, 0, _ishift); + GetEmaSlowPeriod(), GetSignalPeriod(), GetAppliedPrice() /*]*/, 0, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Pivot.mqh b/Indicators/Indi_Pivot.mqh index f01264b9c..5a767296d 100644 --- a/Indicators/Indi_Pivot.mqh +++ b/Indicators/Indi_Pivot.mqh @@ -1,6 +1,6 @@ //+------------------------------------------------------------------+ //| EA31337 framework | -//| Copyright 2016-2023, EA31337 Ltd | +//| Copyright 2016-2021, EA31337 Ltd | //| https://github.com/EA31337 | //+------------------------------------------------------------------+ @@ -22,8 +22,8 @@ // Includes. #include "../Bar.struct.h" -#include "../Indicator.struct.h" -#include "../Serializer.mqh" +#include "../Indicator/Indicator.struct.h" +#include "../Serializer/Serializer.h" #include "Special/Indi_Math.mqh" // Structs. @@ -58,14 +58,14 @@ class Indi_Pivot : public Indicator { */ Indi_Pivot(IndiPivotParams& _p, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_INDICATOR, IndicatorData* _indi_src = NULL, int _indi_src_mode = 0) - : Indicator(_p, IndicatorDataParams::GetInstance(9, TYPE_FLOAT, _idstype, IDATA_RANGE_MIXED, _indi_src_mode), + : Indicator(_p, IndicatorDataParams::GetInstance(9, TYPE_DOUBLE, _idstype, IDATA_RANGE_MIXED, _indi_src_mode), _indi_src) { Init(); }; Indi_Pivot(int _shift = 0, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_INDICATOR, IndicatorData* _indi_src = NULL, int _indi_src_mode = 0) : Indicator(IndiPivotParams(), - IndicatorDataParams::GetInstance(9, TYPE_FLOAT, _idstype, IDATA_RANGE_MIXED, _indi_src_mode), + IndicatorDataParams::GetInstance(9, TYPE_DOUBLE, _idstype, IDATA_RANGE_MIXED, _indi_src_mode), _indi_src) { Init(); } @@ -99,14 +99,13 @@ class Indi_Pivot : public Indicator { * @return * Returns IndicatorDataEntry struct filled with indicator values. */ - virtual IndicatorDataEntry GetEntry(int _shift = 0) { - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); - long _bar_time = GetCandle() PTR_DEREF GetBarTime(_ishift); + virtual IndicatorDataEntry GetEntry(int _rel_shift = 0) { + long _bar_time = GetCandle() PTR_DEREF GetBarTime(_rel_shift); IndicatorDataEntry _entry = idata.GetByKey(_bar_time); if (_bar_time > 0 && !_entry.IsValid() && !_entry.CheckFlag(INDI_ENTRY_FLAG_INSUFFICIENT_DATA)) { ResetLastError(); - BarOHLC _ohlc = GetOHLC(_ishift); - _entry.timestamp = GetCandle() PTR_DEREF GetBarTime(_ishift); + BarOHLC _ohlc = GetOHLC(_rel_shift); + _entry.timestamp = GetCandle() PTR_DEREF GetBarTime(_rel_shift); if (_ohlc.IsValid()) { _entry.Resize(Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_MAX_MODES))); _ohlc.GetPivots(GetMethod(), _entry.values[0].value.vdbl, _entry.values[1].value.vdbl, @@ -117,7 +116,7 @@ class Indi_Pivot : public Indicator { _entry.values[i].SetDataType(TYPE_DOUBLE); } } - GetEntryAlter(_entry, _shift); + GetEntryAlter(_entry, _rel_shift); _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, IsValidEntry(_entry)); if (_entry.IsValid()) { idata.Add(_entry, _bar_time); @@ -137,34 +136,8 @@ class Indi_Pivot : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); - return GetEntry(_ishift)[_mode]; - } - - /** - * Checks if indicator entry values are valid. - */ - virtual bool IsValidEntry(IndicatorDataEntry& _entry) { - bool _is_valid = Indicator::IsValidEntry(_entry); - switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { - case IDATA_BUILTIN: - break; - case IDATA_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 (!HasDataSource()) { - SetUserError(ERR_INVALID_PARAMETER); - _is_valid &= false; - } - break; - default: - SetUserError(ERR_INVALID_PARAMETER); - _is_valid &= false; - break; - } - return _is_valid; + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { + return GetEntry(ToRelShift(_abs_shift))[_mode]; } /* Getters */ diff --git a/Indicators/Indi_PriceChannel.mqh b/Indicators/Indi_PriceChannel.mqh index c6d066a11..4e365d2d9 100644 --- a/Indicators/Indi_PriceChannel.mqh +++ b/Indicators/Indi_PriceChannel.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Indi_ZigZag.mqh" // Structs. @@ -79,15 +79,17 @@ class Indi_PriceChannel : public Indicator { /** * OnCalculate-based version of Price Channel indicator as there is no built-in one. */ - static double iPriceChannel(IndicatorData *_indi, int _period, int _mode = 0, int _shift = 0) { + static double iPriceChannel(IndicatorData *_indi, int _period, int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _period); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey(_period)); - return iPriceChannelOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _period, _mode, _shift, _cache); + return iPriceChannelOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _period, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * Calculates Price Channel on the array of values. */ - static double iPriceChannelOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _period, int _mode, int _shift, + static double iPriceChannelOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _period, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -103,7 +105,7 @@ class Indi_PriceChannel : public Indicator { _cache.GetBuffer(0), _cache.GetBuffer(1), _cache.GetBuffer(2), _period)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -126,20 +128,19 @@ class Indi_PriceChannel : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = iPriceChannel(THIS_PTR, GetPeriod(), _mode, _ishift); + _value = iPriceChannel(THIS_PTR, GetPeriod(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iPriceChannel(THIS_PTR, GetPeriod(), _mode, _ishift); + _value = iPriceChannel(THIS_PTR, GetPeriod(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_PriceFeeder.mqh b/Indicators/Indi_PriceFeeder.mqh index 122dd8ff3..bdfe6dea4 100644 --- a/Indicators/Indi_PriceFeeder.mqh +++ b/Indicators/Indi_PriceFeeder.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" // Structs. struct IndiPriceFeederParams : IndicatorParams { @@ -88,18 +88,20 @@ class Indi_PriceFeeder : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { int data_size = ArraySize(iparams.price_data); - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); - if (_ishift >= data_size || _ishift < 0) return DBL_MIN; + if (_abs_shift >= data_size || _abs_shift < 0) return DBL_MIN; - double _value = iparams.price_data[data_size - _ishift - 1]; + double _value = iparams.price_data[data_size - _abs_shift - 1]; return _value; } - void OnTick() { - Indicator::OnTick(); + /** + * Called when new tick is retrieved from attached data source. + */ + void OnTick(int _global_tick_index) override { + Indicator::OnTick(_global_tick_index); if (idparams.is_draw) { int _max_modes = Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_MAX_MODES)); diff --git a/Indicators/Indi_PriceVolumeTrend.mqh b/Indicators/Indi_PriceVolumeTrend.mqh index ec56c2e53..bb101d2bb 100644 --- a/Indicators/Indi_PriceVolumeTrend.mqh +++ b/Indicators/Indi_PriceVolumeTrend.mqh @@ -20,9 +20,13 @@ * */ +// Defines. +// 100 bars was originally specified by Indicators/Examples/PVT.mq5 +#define INDI_PVT_MIN_BARS 2 + // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" // Structs. @@ -70,15 +74,17 @@ class Indi_PriceVolumeTrend : public Indicator { /** * OnCalculate-based version of Price Volume Trend as there is no built-in one. */ - static double iPVT(IndicatorData *_indi, ENUM_APPLIED_VOLUME _av, int _mode = 0, int _shift = 0) { + static double iPVT(IndicatorData *_indi, ENUM_APPLIED_VOLUME _av, int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, INDI_PVT_MIN_BARS); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey((int)_av)); - return iPVTOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _av, _mode, _shift, _cache); + return iPVTOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _av, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _cache); } /** * Calculates Price Volume Trend on the array of values. */ - static double iPVTOnArray(INDICATOR_CALCULATE_PARAMS_LONG, ENUM_APPLIED_VOLUME _av, int _mode, int _shift, + static double iPVTOnArray(INDICATOR_CALCULATE_PARAMS_LONG, ENUM_APPLIED_VOLUME _av, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -93,15 +99,16 @@ class Indi_PriceVolumeTrend : public Indicator { _cache.SetPrevCalculated( Indi_PriceVolumeTrend::Calculate(INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _av)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * On-indicator version of Price Volume Trend. */ - static double iPVTOnIndicator(IndicatorData *_indi, ENUM_APPLIED_VOLUME _av, int _mode = 0, int _shift = 0) { + static double iPVTOnIndicator(IndicatorData *_indi, ENUM_APPLIED_VOLUME _av, int _mode = 0, int _rel_shift = 0) { INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey((int)_av)); - return iPVTOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _av, _mode, _shift, _cache); + return iPVTOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _av, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _cache); } /** @@ -109,7 +116,7 @@ class Indi_PriceVolumeTrend : public Indicator { */ static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_LONG, ValueStorage &ExtPVTBuffer, ENUM_APPLIED_VOLUME InpVolumeType) { - if (rates_total < 2) return (0); + if (rates_total < INDI_PVT_MIN_BARS) return (0); int pos = prev_calculated - 1; // Correct position, when it's first iteration. if (pos < 0) { @@ -140,20 +147,19 @@ class Indi_PriceVolumeTrend : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = iPVT(THIS_PTR, GetAppliedVolume(), _mode, _ishift); + _value = iPVT(THIS_PTR, GetAppliedVolume(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetAppliedVolume() /*]*/, 0, _ishift); + /*[*/ GetAppliedVolume() /*]*/, 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iPVT(THIS_PTR, GetAppliedVolume(), _mode, _ishift); + _value = iPVT(THIS_PTR, GetAppliedVolume(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_RS.mqh b/Indicators/Indi_RS.mqh index b19c0daea..3b7c513c4 100644 --- a/Indicators/Indi_RS.mqh +++ b/Indicators/Indi_RS.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "OHLC/Indi_OHLC.mqh" #include "Special/Indi_Math.mqh" @@ -104,8 +104,7 @@ class Indi_RS : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_MATH: // Updating Maths' data sources to be the same as RS data source. @@ -119,9 +118,4 @@ class Indi_RS : public Indicator { } return EMPTY_VALUE; } - - /** - * Checks if indicator entry values are valid. - */ - virtual bool IsValidEntry(IndicatorDataEntry &_entry) { return true; } }; diff --git a/Indicators/Indi_RSI.mqh b/Indicators/Indi_RSI.mqh index 0826d327a..088467c26 100644 --- a/Indicators/Indi_RSI.mqh +++ b/Indicators/Indi_RSI.mqh @@ -22,7 +22,7 @@ // Includes. #include "../DictStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Indi_Bands.mqh" #include "Indi_CCI.mqh" #include "Indi_Envelopes.mqh" @@ -37,9 +37,9 @@ double iRSI(string _symbol, int _tf, int _period, int _ap, int _shift) { ResetLastError(); return Indi_RSI::iRSI(_symbol, (ENUM_TIMEFRAMES)_tf, _period, (ENUM_APPLIED_PRICE)_ap, _shift); } -double iRSIOnArray(double &_arr[], int _total, int _period, int _shift) { +double iRSIOnArray(double &_arr[], int _total, int _period, int _abs_shift) { ResetLastError(); - return Indi_RSI::iRSIOnArray(_arr, _total, _period, _shift); + return Indi_RSI::iRSIOnArray(_arr, _total, _period, _abs_shift); } #endif @@ -173,6 +173,8 @@ class Indi_RSI : public Indicator { static double iRSIOnIndicator(Indi_RSI *_target, IndicatorData *_source, string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _period = 14, ENUM_APPLIED_PRICE _ap = PRICE_CLOSE, int _shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_target, _period + _shift + 1); // +1 because of _bar_time_prev. + long _bar_time_curr = _source PTR_DEREF GetBarTime(_shift); long _bar_time_prev = _source PTR_DEREF GetBarTime(_shift + 1); if (fmin(_bar_time_curr, _bar_time_prev) < 0) { @@ -307,25 +309,24 @@ class Indi_RSI : public Indicator { * Note that in MQL5 Applied Price must be passed as the last parameter * (before mode and shift). */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; double _res[]; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = - Indi_RSI::iRSI(GetSymbol(), GetTf(), iparams.GetPeriod(), iparams.GetAppliedPrice(), _ishift, THIS_PTR); + _value = Indi_RSI::iRSI(GetSymbol(), GetTf(), iparams.GetPeriod(), iparams.GetAppliedPrice(), + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: // @todo Modify iRSIOnIndicator() to operate on single IndicatorData pointer. break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /* [ */ iparams.GetPeriod(), - iparams.GetAppliedPrice() /* ] */, 0, _ishift); + iparams.GetAppliedPrice() /* ] */, 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: _value = Indi_RSI::iRSIOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), iparams.GetPeriod(), - iparams.GetAppliedPrice(), _ishift); + iparams.GetAppliedPrice(), ToRelShift(_abs_shift)); break; } return _value; diff --git a/Indicators/Indi_RVI.mqh b/Indicators/Indi_RVI.mqh index 1d9ee3f2e..f857a4694 100644 --- a/Indicators/Indi_RVI.mqh +++ b/Indicators/Indi_RVI.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -87,47 +87,23 @@ class Indi_RVI : public Indicator { #ifdef __MQL4__ return ::iRVI(_symbol, _tf, _period, _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iRVI(_symbol, _tf, _period)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iRVI(_symbol, _tf, _period), _mode, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_RVI::iRVI(GetSymbol(), GetTf(), GetPeriod(), (ENUM_SIGNAL_LINE)_mode, _ishift, THIS_PTR); + _value = Indi_RVI::iRVI(GetSymbol(), GetTf(), GetPeriod(), (ENUM_SIGNAL_LINE)_mode, ToRelShift(_abs_shift), + THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - _mode, _ishift); + _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_RateOfChange.mqh b/Indicators/Indi_RateOfChange.mqh index 67296bea3..dda47c51c 100644 --- a/Indicators/Indi_RateOfChange.mqh +++ b/Indicators/Indi_RateOfChange.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" // Structs. struct IndiRateOfChangeParams : IndicatorParams { @@ -69,15 +69,16 @@ class Indi_RateOfChange : public Indicator { /** * Checks whether given data source satisfies our requirements. */ - double iROC(IndicatorData *_indi, int _period, ENUM_APPLIED_PRICE _ap, int _mode = 0, int _shift = 0) { + double iROC(IndicatorData *_indi, int _period, ENUM_APPLIED_PRICE _ap, int _mode = 0, int _rel_shift = 0) { INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT(_indi, _ap, Util::MakeKey(_period, (int)_ap)); - return iROCOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _period, _mode, _shift, _cache); + return iROCOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _period, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * Calculates Rate of Change on the array of values. */ - static double iROCOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _period, int _mode, int _shift, + static double iROCOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _period, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_price); @@ -92,16 +93,18 @@ class Indi_RateOfChange : public Indicator { _cache.SetPrevCalculated( Indi_RateOfChange::Calculate(INDICATOR_CALCULATE_GET_PARAMS_SHORT, _cache.GetBuffer(0), _period)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * On-indicator version of Rate of Change. */ static double iROCOnIndicator(IndicatorData *_indi, int _period, ENUM_APPLIED_PRICE _ap, int _mode = 0, - int _shift = 0) { + int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _period); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT(_indi, _ap, Util::MakeKey(_period, (int)_ap)); - return iROCOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _period, _mode, _shift, _cache); + return iROCOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _period, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** @@ -126,20 +129,19 @@ class Indi_RateOfChange : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = iROC(THIS_PTR, GetPeriod(), GetAppliedPrice(), _mode, _ishift); + _value = iROC(THIS_PTR, GetPeriod(), GetAppliedPrice(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iROC(THIS_PTR, GetPeriod(), GetAppliedPrice(), _mode, _ishift); + _value = iROC(THIS_PTR, GetPeriod(), GetAppliedPrice(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_SAR.mqh b/Indicators/Indi_SAR.mqh index 39c6e3b9e..ecb2581c9 100644 --- a/Indicators/Indi_SAR.mqh +++ b/Indicators/Indi_SAR.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -83,47 +83,22 @@ class Indi_SAR : public Indicator { #ifdef __MQL4__ return ::iSAR(_symbol, _tf, _step, _max, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iSAR(_symbol, _tf, _step, _max)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iSAR(_symbol, _tf, _step, _max), 0, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_SAR::iSAR(GetSymbol(), GetTf(), GetStep(), GetMax(), _ishift, THIS_PTR); + _value = Indi_SAR::iSAR(GetSymbol(), GetTf(), GetStep(), GetMax(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetStep(), - GetMax() /*]*/, _mode, _ishift); + GetMax() /*]*/, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_StdDev.mqh b/Indicators/Indi_StdDev.mqh index 5c3334bf7..089449b0e 100644 --- a/Indicators/Indi_StdDev.mqh +++ b/Indicators/Indi_StdDev.mqh @@ -28,7 +28,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ObjectsCache.h" #include "Indi_MA.mqh" #include "Indi_PriceFeeder.mqh" @@ -40,9 +40,9 @@ double iStdDev(string _symbol, int _tf, int _ma_period, int _ma_shift, int _ma_m return Indi_StdDev::iStdDev(_symbol, (ENUM_TIMEFRAMES)_tf, _ma_period, _ma_shift, (ENUM_MA_METHOD)_ma_method, (ENUM_APPLIED_PRICE)_ap, _shift); } -double iStdDevOnArray(double &_arr[], int _total, int _ma_period, int _ma_shift, int _ma_method, int _shift) { +double iStdDevOnArray(double &_arr[], int _total, int _ma_period, int _ma_shift, int _ma_method, int _abs_shift) { ResetLastError(); - return Indi_StdDev::iStdDevOnArray(_arr, _total, _ma_period, _ma_shift, (ENUM_MA_METHOD)_ma_method, _shift); + return Indi_StdDev::iStdDevOnArray(_arr, _total, _ma_period, _ma_shift, (ENUM_MA_METHOD)_ma_method, _abs_shift); } #endif @@ -107,31 +107,8 @@ class Indi_StdDev : public Indicator { #ifdef __MQL4__ return ::iStdDev(_symbol, _tf, _ma_period, _ma_shift, _ma_method, _applied_price, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iStdDev(_symbol, _tf, _ma_period, _ma_shift, _ma_method, _applied_price)) == INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - if (GetLastError() > 0) { - return EMPTY_VALUE; - } else if (_bars_calc <= 2) { - SetUserError(ERR_USER_INVALID_BUFF_NUM); - return EMPTY_VALUE; - } - } - if (CopyBuffer(_handle, 0, _shift, 1, _res) < 0) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN(::iStdDev(_symbol, _tf, _ma_period, _ma_shift, _ma_method, _applied_price), 0, + _shift); #endif } @@ -141,6 +118,8 @@ class Indi_StdDev : public Indicator { static double iStdDevOnIndicator(IndicatorData *_target, IndicatorData *_source, string _symbol, ENUM_TIMEFRAMES _tf, int _ma_period, int _ma_shift, ENUM_APPLIED_PRICE _ap, int _shift = 0, Indi_StdDev *_obj = NULL) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_source, _ma_period + _ma_shift + _shift) + double _indi_value_buffer[]; double _std_dev; int i; @@ -247,25 +226,24 @@ class Indi_StdDev : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_StdDev::iStdDev(GetSymbol(), GetTf(), GetMAPeriod(), GetMAShift(), GetMAMethod(), - GetAppliedPrice(), _ishift, THIS_PTR); + GetAppliedPrice(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: _value = Indi_StdDev::iStdDevOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), GetMAPeriod(), - GetMAShift(), GetAppliedPrice(), _ishift, THIS_PTR); + GetMAShift(), GetAppliedPrice(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetMAPeriod(), - GetMAShift(), GetMAMethod() /*]*/, 0, _ishift); + GetMAShift(), GetMAMethod() /*]*/, 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: _value = Indi_StdDev::iStdDevOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), GetMAPeriod(), - GetMAShift(), GetAppliedPrice(), _ishift, THIS_PTR); + GetMAShift(), GetAppliedPrice(), ToRelShift(_abs_shift), THIS_PTR); break; } return _value; diff --git a/Indicators/Indi_Stochastic.mqh b/Indicators/Indi_Stochastic.mqh index 025e94022..d2cb81fe4 100644 --- a/Indicators/Indi_Stochastic.mqh +++ b/Indicators/Indi_Stochastic.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -102,49 +102,24 @@ class Indi_Stochastic : public Indicator { #ifdef __MQL4__ return ::iStochastic(_symbol, _tf, _kperiod, _dperiod, _slowing, _ma_method, _price_field, _mode, _shift); #else // __MQL5__ - int _handle = Object::IsValid(_obj) ? _obj.Get(IndicatorState::INDICATOR_STATE_PROP_HANDLE) : NULL; - double _res[]; - if (_handle == NULL || _handle == INVALID_HANDLE) { - if ((_handle = ::iStochastic(_symbol, _tf, _kperiod, _dperiod, _slowing, _ma_method, _price_field)) == - INVALID_HANDLE) { - SetUserError(ERR_USER_INVALID_HANDLE); - return EMPTY_VALUE; - } else if (Object::IsValid(_obj)) { - _obj.SetHandle(_handle); - } - } - 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); - 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) { - return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; - } - return _res[0]; + INDICATOR_BUILTIN_CALL_AND_RETURN( + ::iStochastic(_symbol, _tf, _kperiod, _dperiod, _slowing, _ma_method, _price_field), _mode, _shift); #endif } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_Stochastic::iStochastic(GetSymbol(), GetTf(), GetKPeriod(), GetDPeriod(), GetSlowing(), - GetMAMethod(), GetPriceField(), _mode, _ishift, THIS_PTR); + GetMAMethod(), GetPriceField(), _mode, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetKPeriod(), - GetDPeriod(), GetSlowing() /*]*/, _mode, _ishift); + GetDPeriod(), GetSlowing() /*]*/, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_TEMA.mqh b/Indicators/Indi_TEMA.mqh index 0d24d68ed..55c550fa3 100644 --- a/Indicators/Indi_TEMA.mqh +++ b/Indicators/Indi_TEMA.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Indi_MA.mqh" // Structs. @@ -94,7 +94,7 @@ class Indi_TEMA : public Indicator { /** * Calculates iTEMA on the array of values. */ - static double iTEMAOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _ma_period, int _ma_shift, int _mode, int _shift, + static double iTEMAOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _ma_period, int _ma_shift, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_price); @@ -110,26 +110,28 @@ class Indi_TEMA : public Indicator { _cache.GetBuffer(1), _cache.GetBuffer(2), _cache.GetBuffer(3), _ma_period, _ma_shift)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * On-indicator version of TEMA. */ static double iTEMAOnIndicator(IndicatorData *_indi, int _ma_period, int _ma_shift, ENUM_APPLIED_PRICE _ap, - int _mode = 0, int _shift = 0) { + int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, 3 * _ma_period - 3); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT(_indi, _ap, Util::MakeKey(_ma_period, _ma_shift, (int)_ap)); - return iTEMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _ma_period, _ma_shift, _mode, _shift, _cache); + return iTEMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _ma_period, _ma_shift, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * OnCalculate() method for TEMA indicator. * - * Note that InpShift is used for drawing only and thus is unused. + * Note that _ishift is used for drawing only and thus is unused. */ static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_SHORT, ValueStorage &TemaBuffer, ValueStorage &Ema, ValueStorage &EmaOfEma, ValueStorage &EmaOfEmaOfEma, - int InpPeriodEMA, int InpShift) { + int InpPeriodEMA, int _ishift) { if (rates_total < 3 * InpPeriodEMA - 3) return (0); //--- int start; @@ -154,22 +156,23 @@ class Indi_TEMA : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_TEMA::iTEMA(GetSymbol(), GetTf(), GetPeriod(), GetTEMAShift(), GetAppliedPrice(), 0, _ishift, - THIS_PTR); + _value = Indi_TEMA::iTEMA(GetSymbol(), GetTf(), GetPeriod(), GetTEMAShift(), GetAppliedPrice(), 0, + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: - _value = iTEMAOnIndicator(GetDataSource(), GetPeriod(), GetTEMAShift(), GetAppliedPrice(), _mode, _ishift); + _value = iTEMAOnIndicator(GetDataSource(), GetPeriod(), GetTEMAShift(), GetAppliedPrice(), _mode, + ToRelShift(_abs_shift)); case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetTEMAShift() /*]*/, 0, _ishift); + GetTEMAShift() /*]*/, 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iTEMAOnIndicator(GetDataSource(), GetPeriod(), GetTEMAShift(), GetAppliedPrice(), _mode, _ishift); + _value = iTEMAOnIndicator(GetDataSource(), GetPeriod(), GetTEMAShift(), GetAppliedPrice(), _mode, + ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_TRIX.mqh b/Indicators/Indi_TRIX.mqh index 851afe9c1..f877fafa8 100644 --- a/Indicators/Indi_TRIX.mqh +++ b/Indicators/Indi_TRIX.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "Indi_MA.mqh" // Structs. @@ -92,7 +92,7 @@ class Indi_TRIX : public Indicator { /** * Calculates TriX on the array of values. */ - static double iTriXOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _ma_period, int _mode, int _shift, + static double iTriXOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _ma_period, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_price); @@ -108,16 +108,18 @@ class Indi_TRIX : public Indicator { _cache.GetBuffer(1), _cache.GetBuffer(2), _cache.GetBuffer(3), _ma_period)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * On-indicator version of TriX. */ static double iTriXOnIndicator(IndicatorData *_indi, int _ma_period, ENUM_APPLIED_PRICE _ap, int _mode = 0, - int _shift = 0, IndicatorData *_obj = NULL) { + int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, 3 * _ma_period - 3); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT(_indi, _ap, Util::MakeKey(_ma_period, (int)_ap)); - return iTriXOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _ma_period, _mode, _shift, _cache); + return iTriXOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _ma_period, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** @@ -154,23 +156,24 @@ class Indi_TRIX : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_TRIX::iTriX(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetAppliedPrice() /*]*/, _mode, _ishift, - THIS_PTR); + _value = Indi_TRIX::iTriX(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetAppliedPrice() /*]*/, _mode, + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: - _value = Indi_TRIX::iTriXOnIndicator(GetDataSource(), GetPeriod(), GetAppliedPrice(), _mode, _ishift); + _value = + Indi_TRIX::iTriXOnIndicator(GetDataSource(), GetPeriod(), GetAppliedPrice(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = Indi_TRIX::iTriXOnIndicator(GetDataSource(), GetPeriod(), GetAppliedPrice(), _mode, _ishift); + _value = + Indi_TRIX::iTriXOnIndicator(GetDataSource(), GetPeriod(), GetAppliedPrice(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_UltimateOscillator.mqh b/Indicators/Indi_UltimateOscillator.mqh index 112319753..69160578e 100644 --- a/Indicators/Indi_UltimateOscillator.mqh +++ b/Indicators/Indi_UltimateOscillator.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" #include "Indi_ATR.mqh" #include "Indi_MA.mqh" @@ -100,6 +100,9 @@ class Indi_UltimateOscillator : public Indicator { */ static double iUO(IndicatorData *_indi, int _fast_period, int _middle_period, int _slow_period, int _fast_k, int _middle_k, int _slow_k, int _mode = 0, int _shift = 0) { + int _min_bars_required = MathMax(MathMax(_fast_period, _middle_period), _slow_period); + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _min_bars_required); + INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG( _indi, Util::MakeKey(_fast_period, _middle_period, _slow_period, _fast_k, _middle_k, _slow_k)); @@ -116,7 +119,7 @@ class Indi_UltimateOscillator : public Indicator { * Calculates Ultimate Oscillator on the array of values. */ static double iUOOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _fast_period, int _middle_period, int _slow_period, - int _fast_k, int _middle_k, int _slow_k, int _mode, int _shift, + int _fast_k, int _middle_k, int _slow_k, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, IndicatorData *_indi_atr_fast, IndicatorData *_indi_atr_middle, IndicatorData *_indi_atr_slow, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -134,7 +137,7 @@ class Indi_UltimateOscillator : public Indicator { _cache.GetBuffer(2), _cache.GetBuffer(3), _cache.GetBuffer(4), _fast_period, _middle_period, _slow_period, _fast_k, _middle_k, _slow_k, _indi_atr_fast, _indi_atr_middle, _indi_atr_slow)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -167,8 +170,6 @@ class Indi_UltimateOscillator : public Indicator { if (ExtMaxPeriod < InpMiddlePeriod) ExtMaxPeriod = InpMiddlePeriod; if (ExtMaxPeriod < InpFastPeriod) ExtMaxPeriod = InpFastPeriod; - int min_bars_required = MathMax(MathMax(InpFastPeriod, InpMiddlePeriod), InpSlowPeriod); - if (rates_total < ExtMaxPeriod) return (0); // Not all data may be calculated. int calculated = BarsCalculated(ExtFastATRhandle); @@ -217,7 +218,7 @@ class Indi_UltimateOscillator : public Indicator { ExtBPBuffer[0] = 0.0; ExtUOBuffer[0] = 0.0; // Set value for first InpSlowPeriod bars. - for (i = 1; i <= InpSlowPeriod; i++) { + for (i = 1; i < InpSlowPeriod; i++) { ExtUOBuffer[i] = 0.0; true_low = MathMin(low[i].Get(), close[i - 1].Get()); ExtBPBuffer[i] = close[i] - true_low; @@ -248,25 +249,24 @@ class Indi_UltimateOscillator : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: _value = Indi_UltimateOscillator::iUO(THIS_PTR, GetFastPeriod(), GetMiddlePeriod(), GetSlowPeriod(), GetFastK(), - GetMiddleK(), GetSlowK(), _mode, _ishift); + GetMiddleK(), GetSlowK(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetFastPeriod(), GetMiddlePeriod(), GetSlowPeriod(), GetFastK(), GetMiddleK(), GetSlowK() /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: _value = Indi_UltimateOscillator::iUO(THIS_PTR, GetFastPeriod(), GetMiddlePeriod(), GetSlowPeriod(), GetFastK(), - GetMiddleK(), GetSlowK(), _mode, _ishift); + GetMiddleK(), GetSlowK(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_VIDYA.mqh b/Indicators/Indi_VIDYA.mqh index 40d0b8cde..ebcc3b304 100644 --- a/Indicators/Indi_VIDYA.mqh +++ b/Indicators/Indi_VIDYA.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" // Structs. struct IndiVIDYAParams : IndicatorParams { @@ -98,7 +98,7 @@ class Indi_VIDYA : public Indicator { * Calculates iVIDyA on the array of values. */ static double iVIDyAOnArray(INDICATOR_CALCULATE_PARAMS_SHORT, int _cmo_period, int _ema_period, int _ma_shift, - int _mode, int _shift, IndicatorCalculateCache *_cache, + int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_price); @@ -113,28 +113,29 @@ class Indi_VIDYA : public Indicator { _cache.SetPrevCalculated(Indi_VIDYA::Calculate(INDICATOR_CALCULATE_GET_PARAMS_SHORT, _cache.GetBuffer(0), _cmo_period, _ema_period, _ma_shift)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * On-indicator version of VIDya indicator. */ static double iVIDyAOnIndicator(IndicatorData *_indi, string _symbol, ENUM_TIMEFRAMES _tf, int _cmo_period, - int _ema_period, int _ma_shift, ENUM_APPLIED_PRICE _ap, int _mode = 0, int _shift = 0, - IndicatorData *_obj = NULL) { + int _ema_period, int _ma_shift, ENUM_APPLIED_PRICE _ap, int _mode = 0, + int _rel_shift = 0, IndicatorData *_obj = NULL) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _ema_period + _cmo_period - 1); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT(_indi, _ap, Util::MakeKey(_cmo_period, _ema_period, _ma_shift, (int)_ap)); - return iVIDyAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _cmo_period, _ema_period, _ma_shift, _mode, _shift, - _cache); + return iVIDyAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _cmo_period, _ema_period, _ma_shift, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * OnCalculate() method for VIDyA indicator. * - * Note that InpShift is used for drawing only and thus is unused. + * Note that _ishift is used for drawing only and thus is unused. */ static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_SHORT, ValueStorage &VIDYA_Buffer, int InpPeriodCMO, - int InpPeriodEMA, int InpShift) { + int InpPeriodEMA, int _ishift) { double ExtF = 2.0 / (1.0 + InpPeriodEMA); if (rates_total < InpPeriodEMA + InpPeriodCMO - 1) return (0); @@ -177,30 +178,29 @@ class Indi_VIDYA : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: _value = Indi_VIDYA::iVIDyA(GetSymbol(), GetTf(), /*[*/ GetCMOPeriod(), GetMAPeriod(), GetVIDYAShift(), - GetAppliedPrice() /*]*/, 0, _ishift, THIS_PTR); + GetAppliedPrice() /*]*/, 0, ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ONCALCULATE: - _value = - Indi_VIDYA::iVIDyAOnIndicator(GetDataSource(), GetSymbol(), GetTf(), /*[*/ GetCMOPeriod(), GetMAPeriod(), - GetVIDYAShift(), GetAppliedPrice() /*]*/, _mode, _ishift, THIS_PTR); + _value = Indi_VIDYA::iVIDyAOnIndicator(GetDataSource(), GetSymbol(), GetTf(), /*[*/ GetCMOPeriod(), + GetMAPeriod(), GetVIDYAShift(), GetAppliedPrice() /*]*/, _mode, + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetCMOPeriod(), GetMAPeriod(), GetVIDYAShift() /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = - Indi_VIDYA::iVIDyAOnIndicator(GetDataSource(), GetSymbol(), GetTf(), /*[*/ GetCMOPeriod(), GetMAPeriod(), - GetVIDYAShift(), GetAppliedPrice() /*]*/, _mode, _ishift, THIS_PTR); + _value = Indi_VIDYA::iVIDyAOnIndicator(GetDataSource(), GetSymbol(), GetTf(), /*[*/ GetCMOPeriod(), + GetMAPeriod(), GetVIDYAShift(), GetAppliedPrice() /*]*/, _mode, + ToRelShift(_abs_shift), THIS_PTR); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_VROC.mqh b/Indicators/Indi_VROC.mqh index 947090b0c..df21ac3a3 100644 --- a/Indicators/Indi_VROC.mqh +++ b/Indicators/Indi_VROC.mqh @@ -22,7 +22,7 @@ // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" // Structs. @@ -82,16 +82,18 @@ class Indi_VROC : public Indicator { /** * OnCalculate-based version of VROC as there is no built-in one. */ - static double iVROC(IndicatorData *_indi, int _period, ENUM_APPLIED_VOLUME _av, int _mode = 0, int _shift = 0) { + static double iVROC(IndicatorData *_indi, int _period, ENUM_APPLIED_VOLUME _av, int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, _period); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey(_period, (int)_av)); - return iVROCOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _period, _av, _mode, _shift, _cache); + return iVROCOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _period, _av, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * Calculates VROC on the array of values. */ static double iVROCOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _period, ENUM_APPLIED_VOLUME _av, int _mode, - int _shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { + int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); if (!_cache.HasBuffers()) { @@ -105,7 +107,7 @@ class Indi_VROC : public Indicator { _cache.SetPrevCalculated( Indi_VROC::Calculate(INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _period, _av)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -153,20 +155,19 @@ class Indi_VROC : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = iVROC(THIS_PTR, GetPeriod(), GetAppliedVolume(), _mode, _ishift); + _value = iVROC(THIS_PTR, GetPeriod(), GetAppliedVolume(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetPeriod(), GetAppliedVolume() /*]*/, _mode, _ishift); + /*[*/ GetPeriod(), GetAppliedVolume() /*]*/, _mode, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iVROC(THIS_PTR, GetPeriod(), GetAppliedVolume(), _mode, _ishift); + _value = iVROC(THIS_PTR, GetPeriod(), GetAppliedVolume(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Volumes.mqh b/Indicators/Indi_Volumes.mqh index f68aeddc9..32dd51fa1 100644 --- a/Indicators/Indi_Volumes.mqh +++ b/Indicators/Indi_Volumes.mqh @@ -20,9 +20,13 @@ * */ +// Defines. +// 2 bars was originally specified by Indicators/Examples/Volumes.mq5 +#define INDI_VOLUMES_MIN_BARS 2 + // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" // Structs. @@ -81,15 +85,17 @@ class Indi_Volumes : public Indicator { /** * OnCalculate-based version of Volumes as there is no built-in one. */ - static double iVolumes(IndicatorData *_indi, ENUM_APPLIED_VOLUME _av, int _mode = 0, int _shift = 0) { + static double iVolumes(IndicatorData *_indi, ENUM_APPLIED_VOLUME _av, int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, INDI_VOLUMES_MIN_BARS); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey((int)_av)); - return iVolumesOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _av, _mode, _shift, _cache); + return iVolumesOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _av, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * Calculates AMVolumes on the array of values. */ - static double iVolumesOnArray(INDICATOR_CALCULATE_PARAMS_LONG, ENUM_APPLIED_VOLUME _av, int _mode, int _shift, + static double iVolumesOnArray(INDICATOR_CALCULATE_PARAMS_LONG, ENUM_APPLIED_VOLUME _av, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -104,7 +110,7 @@ class Indi_Volumes : public Indicator { _cache.SetPrevCalculated(Indi_Volumes::Calculate(INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0), _cache.GetBuffer(1), _av)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -112,7 +118,7 @@ class Indi_Volumes : public Indicator { */ static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_LONG, ValueStorage &ExtVolumesBuffer, ValueStorage &ExtColorsBuffer, ENUM_APPLIED_VOLUME InpVolumeType) { - if (rates_total < 2) return (0); + if (rates_total < INDI_VOLUMES_MIN_BARS) return (0); // Starting work. int pos = prev_calculated - 1; @@ -147,20 +153,19 @@ class Indi_Volumes : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = Indi_Volumes::iVolumes(THIS_PTR, GetAppliedVolume(), _mode, _ishift); + _value = Indi_Volumes::iVolumes(THIS_PTR, GetAppliedVolume(), _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetAppliedVolume() /*]*/, _mode, _ishift); + /*[*/ GetAppliedVolume() /*]*/, _mode, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = Indi_Volumes::iVolumes(THIS_PTR, GetAppliedVolume(), _mode, _ishift); + _value = Indi_Volumes::iVolumes(THIS_PTR, GetAppliedVolume(), _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_WPR.mqh b/Indicators/Indi_WPR.mqh index afa9bfafd..ff759aa64 100644 --- a/Indicators/Indi_WPR.mqh +++ b/Indicators/Indi_WPR.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #ifndef __MQL4__ // Defines global functions (for MQL4 backward compability). @@ -117,16 +117,15 @@ class Indi_WPR : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: - _value = Indi_WPR::iWPR(GetSymbol(), GetTf(), GetPeriod(), _ishift, THIS_PTR); + _value = Indi_WPR::iWPR(GetSymbol(), GetTf(), GetPeriod(), ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - 0, _ishift); + 0, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_WilliamsAD.mqh b/Indicators/Indi_WilliamsAD.mqh index 96792d428..af3893d88 100644 --- a/Indicators/Indi_WilliamsAD.mqh +++ b/Indicators/Indi_WilliamsAD.mqh @@ -20,9 +20,13 @@ * */ +// Defines. +// 2 bars was originally specified by Indicators/Examples/W_AD.mq5 +#define INDI_WAD_MIN_BARS 100 + // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" // Structs. @@ -79,15 +83,17 @@ class Indi_WilliamsAD : public Indicator { /** * OnCalculate-based version of Williams' AD as there is no built-in one. */ - static double iWAD(IndicatorData *_indi, int _mode = 0, int _shift = 0) { + static double iWAD(IndicatorData *_indi, int _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, INDI_WAD_MIN_BARS); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, ""); - return iWADOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _shift, _cache); + return iWADOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _mode, _indi PTR_DEREF ToAbsShift(_rel_shift), + _cache); } /** * Calculates William's AD on the array of values. */ - static double iWADOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _shift, + static double iWADOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -102,14 +108,14 @@ class Indi_WilliamsAD : public Indicator { _cache.SetPrevCalculated( Indi_WilliamsAD::Calculate(INDICATOR_CALCULATE_GET_PARAMS_LONG, _cache.GetBuffer(0))); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** * OnCalculate() method for Williams' AD indicator. */ static int Calculate(INDICATOR_CALCULATE_METHOD_PARAMS_LONG, ValueStorage &ExtWADBuffer) { - if (rates_total < 2) return (0); + if (rates_total < INDI_WAD_MIN_BARS) return (0); int pos = prev_calculated - 1; if (pos < 1) { pos = 1; @@ -149,19 +155,19 @@ class Indi_WilliamsAD : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = iWAD(THIS_PTR, _mode, _ishift); + _value = iWAD(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), 0, _ishift); + _value = + iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), 0, ToRelShift(_abs_shift)); break; case IDATA_INDICATOR: - _value = iWAD(THIS_PTR, _mode, _ishift); + _value = iWAD(THIS_PTR, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ZigZag.mqh b/Indicators/Indi_ZigZag.mqh index fbf82f5b5..ba563219b 100644 --- a/Indicators/Indi_ZigZag.mqh +++ b/Indicators/Indi_ZigZag.mqh @@ -20,8 +20,12 @@ * */ +// Defines. +// 100 bars was originally specified by Indicators/Examples/ZigZag.mq5 +#define INDI_ZIGZAG_MIN_BARS 100 + // Includes. -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" // Defines. @@ -146,17 +150,18 @@ class Indi_ZigZag : public Indicator { * Returns value for ZigZag indicator. */ static double iZigZag(IndicatorData *_indi, int _depth, int _deviation, int _backstep, ENUM_ZIGZAG_LINE _mode = 0, - int _shift = 0) { + int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, INDI_ZIGZAG_MIN_BARS); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey(_depth, _deviation, _backstep)); - return iZigZagOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _depth, _deviation, _backstep, _mode, _shift, - _cache); + return iZigZagOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _depth, _deviation, _backstep, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * Calculates ZigZag on the array of values. */ static double iZigZagOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _depth, int _deviation, int _backstep, int _mode, - int _shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { + int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); if (!_cache.HasBuffers()) { @@ -171,7 +176,7 @@ class Indi_ZigZag : public Indicator { _cache.GetBuffer(1), _cache.GetBuffer(2), _depth, _deviation, _backstep)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -182,7 +187,7 @@ class Indi_ZigZag : public Indicator { int InpDeviation, int InpBackstep) { int ExtRecalc = 3; - if (rates_total < 100) return (0); + if (rates_total < INDI_ZIGZAG_MIN_BARS) return (0); //--- int i = 0; int start = 0, extreme_counter = 0, extreme_search = Extremum; @@ -373,21 +378,22 @@ class Indi_ZigZag : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_BUILTIN: case IDATA_ONCALCULATE: - _value = iZigZag(THIS_PTR, GetDepth(), GetDeviation(), GetBackstep(), (ENUM_ZIGZAG_LINE)_mode, _ishift); + _value = iZigZag(THIS_PTR, GetDepth(), GetDeviation(), GetBackstep(), (ENUM_ZIGZAG_LINE)_mode, + ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: - _value = - Indi_ZigZag::iCustomZigZag(GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetDepth(), - GetDeviation(), GetBackstep() /*]*/, (ENUM_ZIGZAG_LINE)_mode, _ishift, THIS_PTR); + _value = Indi_ZigZag::iCustomZigZag(GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetDepth(), + GetDeviation(), GetBackstep() /*]*/, (ENUM_ZIGZAG_LINE)_mode, + ToRelShift(_abs_shift), THIS_PTR); break; case IDATA_INDICATOR: - _value = iZigZag(THIS_PTR, GetDepth(), GetDeviation(), GetBackstep(), (ENUM_ZIGZAG_LINE)_mode, _ishift); + _value = iZigZag(THIS_PTR, GetDepth(), GetDeviation(), GetBackstep(), (ENUM_ZIGZAG_LINE)_mode, + ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ZigZagColor.mqh b/Indicators/Indi_ZigZagColor.mqh index 6cd13b0e5..f764a2522 100644 --- a/Indicators/Indi_ZigZagColor.mqh +++ b/Indicators/Indi_ZigZagColor.mqh @@ -20,9 +20,13 @@ * */ +// Defines. +// 100 bars was originally specified by Indicators/Examples/ZigzagColor.mq5 +#define INDI_ZIGZAG_COLOR_MIN_BARS 100 + // Includes. #include "../BufferStruct.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Storage/ValueStorage.all.h" #include "Indi_ZigZag.mqh" @@ -93,17 +97,18 @@ class Indi_ZigZagColor : public Indicator { * Returns value for ZigZag Color indicator. */ static double iZigZagColor(IndicatorData *_indi, int _depth, int _deviation, int _backstep, - ENUM_ZIGZAG_LINE _mode = 0, int _shift = 0) { + ENUM_ZIGZAG_LINE _mode = 0, int _rel_shift = 0) { + INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_indi, INDI_ZIGZAG_COLOR_MIN_BARS); INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_indi, Util::MakeKey(_depth, _deviation, _backstep)); - return iZigZagColorOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _depth, _deviation, _backstep, _mode, _shift, - _cache); + return iZigZagColorOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, _depth, _deviation, _backstep, _mode, + _indi PTR_DEREF ToAbsShift(_rel_shift), _cache); } /** * Calculates ZigZag Color on the array of values. */ static double iZigZagColorOnArray(INDICATOR_CALCULATE_PARAMS_LONG, int _depth, int _deviation, int _backstep, - int _mode, int _shift, IndicatorCalculateCache *_cache, + int _mode, int _abs_shift, IndicatorCalculateCache *_cache, bool _recalculate = false) { _cache.SetPriceBuffer(_open, _high, _low, _close); @@ -120,7 +125,7 @@ class Indi_ZigZagColor : public Indicator { _cache.GetBuffer(2), _cache.GetBuffer(3), _cache.GetBuffer(4), _depth, _deviation, _backstep)); - return _cache.GetTailValue(_mode, _shift); + return _cache.GetTailValue(_mode, _abs_shift); } /** @@ -132,7 +137,7 @@ class Indi_ZigZagColor : public Indicator { int InpDeviation, int InpBackstep) { int ExtRecalc = 3; - if (rates_total < 100) return 0; + if (rates_total < INDI_ZIGZAG_COLOR_MIN_BARS) return 0; int i, start = 0; int extreme_counter = 0, extreme_search = Extremum; int shift, back = 0, last_high_pos = 0, last_low_pos = 0; @@ -295,17 +300,16 @@ class Indi_ZigZagColor : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_ONCALCULATE: _value = Indi_ZigZagColor::iZigZagColor(THIS_PTR, GetDepth(), GetDeviation(), GetBackstep(), - (ENUM_ZIGZAG_LINE)_mode, _ishift); + (ENUM_ZIGZAG_LINE)_mode, ToRelShift(_abs_shift)); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetDepth(), GetDeviation(), GetBackstep() /*]*/, _mode, _ishift); + /*[*/ GetDepth(), GetDeviation(), GetBackstep() /*]*/, _mode, ToRelShift(_abs_shift)); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/OHLC/Indi_OHLC.mqh b/Indicators/OHLC/Indi_OHLC.mqh index 5a013fc73..bf119cb41 100644 --- a/Indicators/OHLC/Indi_OHLC.mqh +++ b/Indicators/OHLC/Indi_OHLC.mqh @@ -22,7 +22,7 @@ // Includes. #include "../../BufferStruct.mqh" -#include "../../Indicator.mqh" +#include "../../Indicator/Indicator.h" #include "../../Storage/Objects.h" // Enums. @@ -89,8 +89,7 @@ class Indi_OHLC : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { ENUM_APPLIED_PRICE _ap = PRICE_OPEN; switch (_mode) { case INDI_OHLC_CLOSE: @@ -106,6 +105,6 @@ class Indi_OHLC : public Indicator { _ap = PRICE_LOW; break; } - return GetDataSource() PTR_DEREF GetPrice(_ap, _shift); + return GetDataSource() PTR_DEREF GetPrice(_ap, ToRelShift(_abs_shift)); } }; diff --git a/Indicators/Price/Indi_Price.mqh b/Indicators/Price/Indi_Price.mqh index a1ee87de2..9eb3b6713 100644 --- a/Indicators/Price/Indi_Price.mqh +++ b/Indicators/Price/Indi_Price.mqh @@ -22,7 +22,7 @@ // Includes. #include "../../BufferStruct.mqh" -#include "../../Indicator.mqh" +#include "../../Indicator/Indicator.h" #include "../../Platform.h" #include "../../Storage/Objects.h" @@ -66,17 +66,15 @@ class Indi_Price : public Indicator { } /** - * Checks whether indicator has a valid value for a given shift. + * Returns possible data source modes. It is a bit mask of ENUM_IDATA_SOURCE_TYPE. */ - virtual bool HasValidEntry(int _shift = 0) { return GetBarTime(_shift) != 0; } + unsigned int GetPossibleDataModes() override { return IDATA_BUILTIN; } /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); - return GetCandle() PTR_DEREF GetSpecificAppliedPriceValueStorage(iparams.GetAppliedPrice()) - PTR_DEREF Fetch(_ishift); + IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) override { + return GetCandle() PTR_DEREF GetPrice(iparams.GetAppliedPrice(), ToRelShift(_abs_shift)); } /** diff --git a/Indicators/Special/Indi_Custom.mqh b/Indicators/Special/Indi_Custom.mqh index 4e5a8a676..3d94d5ffa 100644 --- a/Indicators/Special/Indi_Custom.mqh +++ b/Indicators/Special/Indi_Custom.mqh @@ -33,7 +33,7 @@ #endif // Includes. -#include "../../Indicator.mqh" +#include "../../Indicator/Indicator.h" // Structs. @@ -98,23 +98,23 @@ class Indi_Custom : public Indicator { /** * Returns the indicator's value. */ - IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_ICUSTOM: switch (iparams.GetParamsSize()) { case 0: - _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, _mode, _ishift); + _value = + iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, _mode, ToRelShift(_abs_shift)); break; case 1: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, - iparams.GetParam(1).ToValue(), _mode, _ishift); + iparams.GetParam(1).ToValue(), _mode, ToRelShift(_abs_shift)); break; case 2: - _value = - iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, - iparams.GetParam(1).ToValue(), iparams.GetParam(2).ToValue(), _mode, _ishift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, + iparams.GetParam(1).ToValue(), iparams.GetParam(2).ToValue(), _mode, + ToRelShift(_abs_shift)); break; } break; diff --git a/Indicators/Special/Indi_Math.mqh b/Indicators/Special/Indi_Math.mqh index f413a7486..56877b515 100644 --- a/Indicators/Special/Indi_Math.mqh +++ b/Indicators/Special/Indi_Math.mqh @@ -22,7 +22,7 @@ // Includes. #include "../../BufferStruct.mqh" -#include "../../Indicator.mqh" +#include "../../Indicator/Indicator.h" #include "../../Math.enum.h" enum ENUM_MATH_OP_MODE { MATH_OP_MODE_BUILTIN, MATH_OP_MODE_CUSTOM_FUNCTION }; @@ -114,9 +114,8 @@ class Indi_Math : public Indicator { /** * Returns the indicator's value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) { double _value = EMPTY_VALUE; - int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) { case IDATA_INDICATOR: if (!indi_src.IsSet()) { @@ -134,12 +133,12 @@ class Indi_Math : public Indicator { case MATH_OP_MODE_BUILTIN: _value = Indi_Math::iMathOnIndicator(GetDataSource(), _Symbol, GetTf(), /*[*/ GetOpBuiltIn(), GetMode1(), GetMode2(), GetShift1(), - GetShift2() /*]*/, 0, _ishift, &this); + GetShift2() /*]*/, 0, ToRelShift(_abs_shift), &this); break; case MATH_OP_MODE_CUSTOM_FUNCTION: _value = Indi_Math::iMathOnIndicator(GetDataSource(), _Symbol, GetTf(), /*[*/ GetOpFunction(), GetMode1(), GetMode2(), GetShift1(), - GetShift2() /*]*/, 0, _ishift, &this); + GetShift2() /*]*/, 0, ToRelShift(_abs_shift), &this); break; } break; @@ -153,6 +152,7 @@ class Indi_Math : public Indicator { static double iMathOnIndicator(IndicatorData *_indi, string _symbol, ENUM_TIMEFRAMES _tf, ENUM_MATH_OP op, unsigned int _mode_1, unsigned int _mode_2, unsigned int _shift_1, unsigned int _shift_2, unsigned int _mode, int _shift, Indi_Math *_obj) { + INDI_REQUIRE_SHIFT_OR_RETURN_EMPTY(_indi, MathMax(_shift_1, _shift_2)); double _val_1 = _indi.GetValue(_mode_1, _shift_1); double _val_2 = _indi.GetValue(_mode_2, _shift_2); return Math::Op(op, _val_1, _val_2); @@ -161,6 +161,7 @@ class Indi_Math : public Indicator { static double iMathOnIndicator(IndicatorData *_indi, string _symbol, ENUM_TIMEFRAMES _tf, MathCustomOpFunction _op, unsigned int _mode_1, unsigned int _mode_2, unsigned int _shift_1, unsigned int _shift_2, unsigned int _mode, int _shift, Indi_Math *_obj) { + INDI_REQUIRE_SHIFT_OR_RETURN_EMPTY(_indi, MathMax(_shift_1, _shift_2)); double _val_1 = _indi.GetValue(_mode_1, _shift_1); double _val_2 = _indi.GetValue(_mode_2, _shift_2); return _op(_val_1, _val_2); diff --git a/Indicators/Tick/Indi_TickMt.mqh b/Indicators/Tick/Indi_TickMt.mqh index ed4270ad4..259d6712a 100644 --- a/Indicators/Tick/Indi_TickMt.mqh +++ b/Indicators/Tick/Indi_TickMt.mqh @@ -33,8 +33,7 @@ // Includes. #include "../../Chart.struct.static.h" #include "../../Indicator/IndicatorTick.h" - -#define INDICATOR_TICK_REAL_FETCH_HISTORY 1000 +#include "../../Indicator/IndicatorTick.provider.h" // Structs. // Params for MT patform's tick-based indicator. @@ -43,25 +42,27 @@ struct Indi_TickMtParams : IndicatorParams { }; // MT platform's tick-based indicator. -class Indi_TickMt : public IndicatorTick { - protected: - bool _fetch_history_on_first_tick; - +class Indi_TickMt : public IndicatorTick> { public: Indi_TickMt(Indi_TickMtParams &_p, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN, IndicatorData *_indi_src = NULL, int _indi_src_mode = 0) : IndicatorTick(_p.symbol, _p, IndicatorDataParams::GetInstance(2, TYPE_DOUBLE, _idstype, IDATA_RANGE_PRICE, _indi_src_mode), _indi_src) { - _fetch_history_on_first_tick = false; + Init(); } Indi_TickMt(string _symbol, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN, IndicatorData *_indi_src = NULL, int _indi_src_mode = 0, string _name = "") : IndicatorTick(_symbol, Indi_TickMtParams(), IndicatorDataParams(2, TYPE_DOUBLE, _idstype, IDATA_RANGE_PRICE, _indi_src_mode), _indi_src) { - _fetch_history_on_first_tick = false; + Init(); } + /** + * Initializes the class. + */ + void Init() {} + string GetName() override { return "Indi_TickMt"; } /** @@ -82,113 +83,78 @@ class Indi_TickMt : public IndicatorTick { * @return * Returns IndicatorDataEntry struct filled with indicator values. */ - IndicatorDataEntry GetEntry(long _index = -1) override { - int _ishift = _index >= 0 ? (int)_index : iparams.GetShift(); - long _bar_time; - _bar_time = GetBarTime(_ishift); + IndicatorDataEntry GetEntry(int _index = 0) override { + // @todo Use history to check/regenerate tick and return data entry. - TickAB _tick = itdata.GetByKey(_bar_time); - IndicatorDataEntry _entry = TickToEntry(_bar_time, _tick); + /* + int _ishift = _index >= 0 ? (int)_index : iparams.GetShift(); + long _bar_time; + _bar_time = GetBarTime(_ishift); - if (_entry.IsValid()) { - istate.is_changed = false; - istate.is_ready = true; - } + TickAB _tick = itdata.GetByKey(_bar_time); + IndicatorDataEntry _entry = TickToEntry(_bar_time, _tick); - return _entry; - } + if (_entry.IsValid()) { + istate.is_changed = false; + istate.is_ready = true; + } - void OnBecomeDataSourceFor(IndicatorData *_base_indi) override { - // Feeding base indicator with historic entries of this indicator. -#ifdef __debug__ - Print(GetFullName(), " became a data source for ", _base_indi.GetFullName()); -#endif + return _entry; + } - _fetch_history_on_first_tick = true; - } + void OnBecomeDataSourceFor(IndicatorData *_base_indi) override { + // Feeding base indicator with historic entries of this indicator. + #ifdef __debug__ + Print(GetFullName(), " became a data source for ", _base_indi.GetFullName()); + #endif - void FetchHistory() { - if (INDICATOR_TICK_REAL_FETCH_HISTORY == 0) { - // No history requested. - return; - } + _fetch_history_on_first_tick = true; + */ -#ifndef __MQL4__ - int _ticks_to_emit = 1000; + IndicatorDataEntry _default; + return _default; + } -#ifdef __debug_verbose__ - Print("Listening indicators will be now filled with ", _ticks_to_emit, - " historical entries generated by " + GetFullName()); -#endif + /** + * Fetches historic ticks for a given time range. + */ + virtual bool FetchHistoryByTimeRange(long _from_ms, long _to_ms, ARRAY_REF(TickTAB, _out_ticks)) { + ArrayResize(_out_ticks, 0); static MqlTick _tmp_ticks[]; ArrayResize(_tmp_ticks, 0); - // Number of retries for CopyTicksRange(). + // There's no history in MQL4. +#ifndef __MQL4__ int _tries = 10; - // Number of ticks copied by CopyTicksRange(). - int _num_copied = -1; - - // Number of ticks remaining to copy in order to fulfill number of minimum required ticks (_ticks_to_emit). - int _num_yet_to_copy = _ticks_to_emit; - - // In ms, the period we will be retrieving ticks for. - int _period_msc = 1000 * 60 * 60; // 1 hour distance. - int _max_periods_to_check = 24 * 7; // Two weeks should be enough. - int _periods_checked = 0; - - unsigned long _range_from = TimeCurrent() * 1000 - _period_msc; - unsigned long _range_to = TimeCurrent() * 1000 - 1; - while (_tries > 0) { - _num_copied = CopyTicksRange(GetSymbol(), _tmp_ticks, COPY_TICKS_INFO, _range_from, _range_to); + int _num_copied = CopyTicksRange(GetSymbol(), _tmp_ticks, COPY_TICKS_INFO, _from_ms, _to_ms); if (_num_copied == -1) { ResetLastError(); Sleep(1000); --_tries; } else { - _num_yet_to_copy -= _num_copied; - for (int i = 0; i < _num_copied; ++i) { - TickAB _tick(_tmp_ticks[i].ask, _tmp_ticks[i].bid); - // We can't call EmitEntry() here, as tick would go to multiple sources at the same time! + TickTAB _tick(_tmp_ticks[i]); #ifdef __debug_verbose__ - Print("Tick at ", TimeToString(_tmp_ticks[i].time, TIME_DATE | TIME_MINUTES | TIME_SECONDS), ": ", + Print("Fetched tick at ", TimeToString(_tmp_ticks[i].time, TIME_DATE | TIME_MINUTES | TIME_SECONDS), ": ", _tmp_ticks[i].ask, ", ", _tmp_ticks[i].bid); #endif - - EmitEntry(TickToEntry(_tmp_ticks[i].time, _tick), INDI_EMITTED_ENTRY_TYPE_TICK); - - if (_num_yet_to_copy <= 0) { - break; - } + ArrayPushObject(_out_ticks, _tick); } - _range_from -= _period_msc; - _range_to -= _period_msc; - if (++_periods_checked > _max_periods_to_check) { - break; - } + return true; } } - -#ifdef __debug_verbose__ - Print("Listening indicators were filled with ", (_ticks_to_emit - _num_yet_to_copy), " out of ", _ticks_to_emit, - " historical entries requested"); #endif -#endif + // To many tries. Probably no ticks at the given range. + return false; } - void OnTick() override { - if (_fetch_history_on_first_tick) { - // We wait for fetching the history for the first tick, as it won't work in OnInit(). - _fetch_history_on_first_tick = false; - FetchHistory(); - } - + void OnTick(int _global_tick_index) override { #ifdef __MQL4__ // Refreshes Ask/Bid constants. RefreshRates(); @@ -205,7 +171,10 @@ class Indi_TickMt : public IndicatorTick { // DebugBreak(); // Just emitting zeroes in case of error. TickAB _tick(0, 0); - EmitEntry(TickToEntry(TimeCurrent(), _tick), INDI_EMITTED_ENTRY_TYPE_TICK); + IndicatorDataEntry _entry(TickToEntry(TimeCurrent(), _tick)); + EmitEntry(_entry); + // Appending tick into the history. + AppendEntry(_entry); return; } @@ -223,7 +192,8 @@ class Indi_TickMt : public IndicatorTick { #endif TickAB _tick(_ask, _bid); IndicatorDataEntry _entry(TickToEntry(_time, _tick)); - StoreEntry(_entry); - EmitEntry(_entry, INDI_EMITTED_ENTRY_TYPE_TICK); + EmitEntry(_entry); + // Appending tick into the history. + AppendEntry(_entry); } }; diff --git a/Indicators/Tick/Indi_TickRandom.mqh b/Indicators/Tick/Indi_TickRandom.mqh new file mode 100644 index 000000000..c3b3a6253 --- /dev/null +++ b/Indicators/Tick/Indi_TickRandom.mqh @@ -0,0 +1,103 @@ +//+------------------------------------------------------------------+ +//| 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 + * Random (mainly for C++ testing) tick-based indicator. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../../Chart.struct.static.h" +#include "../../Indicator/IndicatorTick.h" +#include "../../Indicator/IndicatorTick.provider.h" + +// Structs. +// Params for MT patform's tick-based indicator. +struct Indi_TickRandomParams : IndicatorParams { + Indi_TickRandomParams() : IndicatorParams(INDI_TICK_RANDOM) {} +}; + +// MT platform's tick-based indicator. +class Indi_TickRandom : public IndicatorTick> { + public: + Indi_TickRandom(Indi_TickRandomParams &_p, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN, + IndicatorData *_indi_src = NULL, int _indi_src_mode = 0) + : IndicatorTick(_p.symbol, _p, + IndicatorDataParams::GetInstance(2, TYPE_DOUBLE, _idstype, IDATA_RANGE_PRICE, _indi_src_mode), + _indi_src) { + Init(); + } + Indi_TickRandom(string _symbol, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN, IndicatorData *_indi_src = NULL, + int _indi_src_mode = 0, string _name = "") + : IndicatorTick(_symbol, Indi_TickRandomParams(), + IndicatorDataParams(2, TYPE_DOUBLE, _idstype, IDATA_RANGE_PRICE, _indi_src_mode), _indi_src) { + Init(); + } + + /** + * Initializes the class. + */ + void Init() {} + + string GetName() override { return "Indi_TickRandom"; } + + /** + * Returns possible data source types. It is a bit mask of ENUM_INDI_SUITABLE_DS_TYPE. + */ + unsigned int GetSuitableDataSourceTypes() override { return INDI_SUITABLE_DS_TYPE_EXPECT_NONE; } + + /** + * Returns possible data source modes. It is a bit mask of ENUM_IDATA_SOURCE_TYPE. + */ + unsigned int GetPossibleDataModes() override { return IDATA_BUILTIN; } + + /** + * Returns the indicator's struct entry for the given shift. + */ + IndicatorDataEntry GetEntry(int _index = 0) override { + IndicatorDataEntry _default; + return _default; + } + + /** + * Fetches historic ticks for a given time range. + */ + virtual bool FetchHistoryByTimeRange(long _from_ms, long _to_ms, ARRAY_REF(TickTAB, _out_ticks)) { + // No history. + return false; + } + + void OnTick(int _global_tick_index) override { + float _ask = 1.0f + (1.0f / 32767 * MathRand()); + float _bid = 1.0f + (1.0f / 32767 * MathRand()); + TickAB _tick(, _bid); + IndicatorDataEntry _entry(TickToEntry(_time, _tick)); + EmitEntry(_entry); + // Appending tick into the history. + AppendEntry(_entry); + } +}; diff --git a/Log.mqh b/Log.mqh index 4f312b286..b42f6de5c 100644 --- a/Log.mqh +++ b/Log.mqh @@ -20,6 +20,14 @@ * */ +// Prevents processing this includes file for the second time. +#ifndef LOG_MQH +#define LOG_MQH + +// Forward class declaration. +template +class DictStruct; + // Includes. #include "Array.mqh" #include "DateTime.mqh" @@ -27,10 +35,6 @@ #include "Object.mqh" #include "Storage/Collection.mqh" -// Prevents processing this includes file for the second time. -#ifndef LOG_MQH -#define LOG_MQH - // Define assert macros. // Alias for function and line macros combined together. #define __FUNCTION_LINE__ string(__FUNCTION__) + ":" + IntegerToString(__LINE__) diff --git a/MD5.mqh b/MD5.mqh index 626fd1617..c34fce753 100644 --- a/MD5.mqh +++ b/MD5.mqh @@ -7,13 +7,23 @@ License: New BSD License - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the + following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following + disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -23,203 +33,197 @@ The MD5 algorithm is only for optimized string, the string can support MQL4 longest string. If the large file MD5 encryption, see a little improvement to this algorithm, when I was in design, also made consideration, you can quickly convert over. - Problems may occur, but the repair method is also very simple, it is the first 16 bits are added, then after 16 are summed. Note that the first 16-bit carry: - var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); (msw << 16) | (lsw & 0xFFFF); - If you're using the algorithm, but also pay attention to test the accuracy of the Chinese case, the algorithm does not take a lot of Chinese. - Some dictionaries now online MD5 crack site, in fact, do not be afraid of them, you put the initial algorithm a, b, c, d change it, it becomes a new encryption algorithm of. + Problems may occur, but the repair method is also very simple, it is the first 16 bits are added, then after 16 are + summed. Note that the first 16-bit carry: var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + + (lsw >> 16); (msw << 16) | (lsw & 0xFFFF); If you're using the algorithm, but also pay attention to test the + accuracy of the Chinese case, the algorithm does not take a lot of Chinese. Some dictionaries now online MD5 crack + site, in fact, do not be afraid of them, you put the initial algorithm a, b, c, d change it, it becomes a new + encryption algorithm of. @see: http://www.cnblogs.com/niniwzw/archive/2009/12/05/1617685.html */ - // Includes. #include "Array.mqh" -#include "Convert.mqh" +#include "Convert.basic.h" /** * Class to provide implementation of MD5 algorithm. * Based on the code published at: https://code.google.com/archive/p/md5-in-mql4/ */ class MD5 { - - public: - - /** - * Calculate MD5 checksum. - */ - static string MD5Sum(string str) { - int len = StringLen(str); - int index = len % 64; //mod 64 - int count = (len - index) / 64; - - long a = 0x67452301, b = 0xEFCDAB89, c = 0x98BADCFE, d = 0x10325476; - int buff[16], last[16], i, k = 0, last_char[4], last_index; - string item; - for (i = 0; i < count; i++) - { - item = StringSubstr(str, i * 64, 64); - Convert::String4ToIntArray(buff, item); - MD5Transform(a, b, c, d, buff); - } - ArrayInitialize(last, 0); - ArrayInitialize(last_char, 0); - last_index = 0; - if (index > 0) { - int last_num = index % 4; - count = index - last_num; - if (count > 0) { - item = StringSubstr(str, i * 64, count); - last_index = Convert::String4ToIntArray(last, item); - } - for (k = 0; k < last_num; k++) - { - last_char[k] = StringGetCharacter(str, i * 64 + count + k); - } + public: + /** + * Calculate MD5 checksum. + */ + static string MD5Sum(string str) { + int len = StringLen(str); + int index = len % 64; // mod 64 + int count = (len - index) / 64; + + long a = 0x67452301, b = 0xEFCDAB89, c = 0x98BADCFE, d = 0x10325476; + int buff[16], last[16], i, k = 0, last_char[4], last_index; + string item; + for (i = 0; i < count; i++) { + item = StringSubstr(str, i * 64, 64); + ConvertBasic::String4ToIntArray(buff, item); + MD5Transform(a, b, c, d, buff); + } + ArrayInitialize(last, 0); + ArrayInitialize(last_char, 0); + last_index = 0; + if (index > 0) { + int last_num = index % 4; + count = index - last_num; + if (count > 0) { + item = StringSubstr(str, i * 64, count); + last_index = ConvertBasic::String4ToIntArray(last, item); } - last_char[k] = 0x80; - last[last_index] = Convert::CharToInt(last_char); - if (index >= 56) { - MD5Transform(a, b, c, d, last); - ArrayInitialize(last, 0); + for (k = 0; k < last_num; k++) { + last_char[k] = StringGetCharacter(str, i * 64 + count + k); } - last[14] = len << 3; - last[15] = ((len >> 1) & 0x7fffffff) >> 28; - MD5Transform(a, b, c, d, last); - string result = StringFormat("%s%s%s%s", - Convert::IntToHex(a), Convert::IntToHex(b), Convert::IntToHex(c), Convert::IntToHex(d)); - return result; - } - - static long F(long x, long y, long z) { - return ((x & y) | ((~x) & z)); - } - - static long G(long x, long y, long z) { - return ((x & z) | (y & (~z))); - } - - static long H(long x, long y, long z) { - return ((x ^ y ^ z)); - } - - static long I(long x, long y, long z) { - return ((y ^ (x | (~z)))); } - - static long AddUnsigned(long a, long b) { - long c = a + b; - return (c); - } - - static long FF(long a, long b, long c, long d, long x, int s, long ac) { - a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac)); - return (AddUnsigned(RotateLeft(a, s), b)); - } - - static long GG(long a, long b, long c, long d, long x, int s, long ac) { - a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac)); - return (AddUnsigned(RotateLeft(a, s), b)); - } - - static long HH(long a, long b, long c, long d, long x, int s, long ac) { - a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac)); - return (AddUnsigned(RotateLeft(a, s), b)); - } - - static long II(long a, long b, long c, long d, long x, int s, long ac) { - a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac)); - return (AddUnsigned(RotateLeft(a, s), b)); - } - - /** - * Implementation of right shift operation for unsigned int. - * See: http://www.cnblogs.com/niniwzw/archive/2009/12/04/1617130.html - */ - static long RotateLeft(long lValue, int iShiftBits) { - if (iShiftBits == 32) return (lValue); - long result = (lValue << iShiftBits) | (((lValue >> 1) & 0x7fffffff) >> (31 - iShiftBits)); - return (result); - } - - /** - * Assume: ArraySize(x) == 16. - */ - static void MD5Transform(long &a, long &b, long &c, long &d, int &x[]) { - long AA, BB, CC, DD; - int S11=7, S12=12, S13=17, S14=22; - int S21=5, S22=9 , S23=14, S24=20; - int S31=4, S32=11, S33=16, S34=23; - int S41=6, S42=10, S43=15, S44=21; - - AA=a; BB=b; CC=c; DD=d; - a=FF(a,b,c,d,x[0], S11, 0xD76AA478); - d=FF(d,a,b,c,x[1], S12, 0xE8C7B756); - c=FF(c,d,a,b,x[2], S13, 0x242070DB); - b=FF(b,c,d,a,x[3], S14, 0xC1BDCEEE); - a=FF(a,b,c,d,x[4], S11, 0xF57C0FAF); - d=FF(d,a,b,c,x[5], S12, 0x4787C62A); - c=FF(c,d,a,b,x[6], S13, 0xA8304613); - b=FF(b,c,d,a,x[7], S14, 0xFD469501); - a=FF(a,b,c,d,x[8], S11, 0x698098D8); - d=FF(d,a,b,c,x[9], S12, 0x8B44F7AF); - c=FF(c,d,a,b,x[10],S13, 0xFFFF5BB1); - b=FF(b,c,d,a,x[11],S14, 0x895CD7BE); - a=FF(a,b,c,d,x[12],S11, 0x6B901122); - d=FF(d,a,b,c,x[13],S12, 0xFD987193); - c=FF(c,d,a,b,x[14],S13, 0xA679438E); - b=FF(b,c,d,a,x[15],S14, 0x49B40821); - - a=GG(a,b,c,d,x[1], S21, 0xF61E2562); - d=GG(d,a,b,c,x[6], S22, 0xC040B340); - c=GG(c,d,a,b,x[11],S23, 0x265E5A51); - b=GG(b,c,d,a,x[0], S24, 0xE9B6C7AA); - a=GG(a,b,c,d,x[5], S21, 0xD62F105D); - d=GG(d,a,b,c,x[10],S22, 0x2441453); - c=GG(c,d,a,b,x[15],S23, 0xD8A1E681); - b=GG(b,c,d,a,x[4], S24, 0xE7D3FBC8); - a=GG(a,b,c,d,x[9], S21, 0x21E1CDE6); - d=GG(d,a,b,c,x[14],S22, 0xC33707D6); - c=GG(c,d,a,b,x[3], S23, 0xF4D50D87); - b=GG(b,c,d,a,x[8], S24, 0x455A14ED); - a=GG(a,b,c,d,x[13],S21, 0xA9E3E905); - d=GG(d,a,b,c,x[2], S22, 0xFCEFA3F8); - c=GG(c,d,a,b,x[7], S23, 0x676F02D9); - b=GG(b,c,d,a,x[12],S24, 0x8D2A4C8A); - - a=HH(a,b,c,d,x[5], S31, 0xFFFA3942); - d=HH(d,a,b,c,x[8], S32, 0x8771F681); - c=HH(c,d,a,b,x[11],S33, 0x6D9D6122); - b=HH(b,c,d,a,x[14],S34, 0xFDE5380C); - a=HH(a,b,c,d,x[1], S31, 0xA4BEEA44); - d=HH(d,a,b,c,x[4], S32, 0x4BDECFA9); - c=HH(c,d,a,b,x[7], S33, 0xF6BB4B60); - b=HH(b,c,d,a,x[10],S34, 0xBEBFBC70); - a=HH(a,b,c,d,x[13],S31, 0x289B7EC6); - d=HH(d,a,b,c,x[0], S32, 0xEAA127FA); - c=HH(c,d,a,b,x[3], S33, 0xD4EF3085); - b=HH(b,c,d,a,x[6], S34, 0x4881D05); - a=HH(a,b,c,d,x[9], S31, 0xD9D4D039); - d=HH(d,a,b,c,x[12],S32, 0xE6DB99E5); - c=HH(c,d,a,b,x[15],S33, 0x1FA27CF8); - b=HH(b,c,d,a,x[2], S34, 0xC4AC5665); - - a=II(a,b,c,d,x[0], S41, 0xF4292244); - d=II(d,a,b,c,x[7], S42, 0x432AFF97); - c=II(c,d,a,b,x[14],S43, 0xAB9423A7); - b=II(b,c,d,a,x[5], S44, 0xFC93A039); - a=II(a,b,c,d,x[12],S41, 0x655B59C3); - d=II(d,a,b,c,x[3], S42, 0x8F0CCC92); - c=II(c,d,a,b,x[10],S43, 0xFFEFF47D); - b=II(b,c,d,a,x[1], S44, 0x85845DD1); - a=II(a,b,c,d,x[8], S41, 0x6FA87E4F); - d=II(d,a,b,c,x[15],S42, 0xFE2CE6E0); - c=II(c,d,a,b,x[6], S43, 0xA3014314); - b=II(b,c,d,a,x[13],S44, 0x4E0811A1); - a=II(a,b,c,d,x[4], S41, 0xF7537E82); - d=II(d,a,b,c,x[11],S42, 0xBD3AF235); - c=II(c,d,a,b,x[2], S43, 0x2AD7D2BB); - b=II(b,c,d,a,x[9], S44, 0xEB86D391); - - a=AddUnsigned(a, AA); b=AddUnsigned(b, BB); - c=AddUnsigned(c, CC); d=AddUnsigned(d, DD); + last_char[k] = 0x80; + last[last_index] = ConvertBasic::CharToInt(last_char); + if (index >= 56) { + MD5Transform(a, b, c, d, last); + ArrayInitialize(last, 0); } + last[14] = len << 3; + last[15] = ((len >> 1) & 0x7fffffff) >> 28; + MD5Transform(a, b, c, d, last); + string result = StringFormat("%s%s%s%s", ConvertBasic::IntToHex(a), ConvertBasic::IntToHex(b), + ConvertBasic::IntToHex(c), ConvertBasic::IntToHex(d)); + return result; + } + + static long F(long x, long y, long z) { return ((x & y) | ((~x) & z)); } + + static long G(long x, long y, long z) { return ((x & z) | (y & (~z))); } + + static long H(long x, long y, long z) { return ((x ^ y ^ z)); } + + static long I(long x, long y, long z) { return ((y ^ (x | (~z)))); } + + static long AddUnsigned(long a, long b) { + long c = a + b; + return (c); + } + + static long FF(long a, long b, long c, long d, long x, int s, long ac) { + a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac)); + return (AddUnsigned(RotateLeft(a, s), b)); + } + + static long GG(long a, long b, long c, long d, long x, int s, long ac) { + a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac)); + return (AddUnsigned(RotateLeft(a, s), b)); + } + + static long HH(long a, long b, long c, long d, long x, int s, long ac) { + a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac)); + return (AddUnsigned(RotateLeft(a, s), b)); + } + + static long II(long a, long b, long c, long d, long x, int s, long ac) { + a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac)); + return (AddUnsigned(RotateLeft(a, s), b)); + } + + /** + * Implementation of right shift operation for unsigned int. + * See: http://www.cnblogs.com/niniwzw/archive/2009/12/04/1617130.html + */ + static long RotateLeft(long lValue, int iShiftBits) { + if (iShiftBits == 32) return (lValue); + long result = (lValue << iShiftBits) | (((lValue >> 1) & 0x7fffffff) >> (31 - iShiftBits)); + return (result); + } + + /** + * Assume: ArraySize(x) == 16. + */ + static void MD5Transform(long &a, long &b, long &c, long &d, int &x[]) { + long AA, BB, CC, DD; + int S11 = 7, S12 = 12, S13 = 17, S14 = 22; + int S21 = 5, S22 = 9, S23 = 14, S24 = 20; + int S31 = 4, S32 = 11, S33 = 16, S34 = 23; + int S41 = 6, S42 = 10, S43 = 15, S44 = 21; + + AA = a; + BB = b; + CC = c; + DD = d; + a = FF(a, b, c, d, x[0], S11, 0xD76AA478); + d = FF(d, a, b, c, x[1], S12, 0xE8C7B756); + c = FF(c, d, a, b, x[2], S13, 0x242070DB); + b = FF(b, c, d, a, x[3], S14, 0xC1BDCEEE); + a = FF(a, b, c, d, x[4], S11, 0xF57C0FAF); + d = FF(d, a, b, c, x[5], S12, 0x4787C62A); + c = FF(c, d, a, b, x[6], S13, 0xA8304613); + b = FF(b, c, d, a, x[7], S14, 0xFD469501); + a = FF(a, b, c, d, x[8], S11, 0x698098D8); + d = FF(d, a, b, c, x[9], S12, 0x8B44F7AF); + c = FF(c, d, a, b, x[10], S13, 0xFFFF5BB1); + b = FF(b, c, d, a, x[11], S14, 0x895CD7BE); + a = FF(a, b, c, d, x[12], S11, 0x6B901122); + d = FF(d, a, b, c, x[13], S12, 0xFD987193); + c = FF(c, d, a, b, x[14], S13, 0xA679438E); + b = FF(b, c, d, a, x[15], S14, 0x49B40821); + + a = GG(a, b, c, d, x[1], S21, 0xF61E2562); + d = GG(d, a, b, c, x[6], S22, 0xC040B340); + c = GG(c, d, a, b, x[11], S23, 0x265E5A51); + b = GG(b, c, d, a, x[0], S24, 0xE9B6C7AA); + a = GG(a, b, c, d, x[5], S21, 0xD62F105D); + d = GG(d, a, b, c, x[10], S22, 0x2441453); + c = GG(c, d, a, b, x[15], S23, 0xD8A1E681); + b = GG(b, c, d, a, x[4], S24, 0xE7D3FBC8); + a = GG(a, b, c, d, x[9], S21, 0x21E1CDE6); + d = GG(d, a, b, c, x[14], S22, 0xC33707D6); + c = GG(c, d, a, b, x[3], S23, 0xF4D50D87); + b = GG(b, c, d, a, x[8], S24, 0x455A14ED); + a = GG(a, b, c, d, x[13], S21, 0xA9E3E905); + d = GG(d, a, b, c, x[2], S22, 0xFCEFA3F8); + c = GG(c, d, a, b, x[7], S23, 0x676F02D9); + b = GG(b, c, d, a, x[12], S24, 0x8D2A4C8A); + + a = HH(a, b, c, d, x[5], S31, 0xFFFA3942); + d = HH(d, a, b, c, x[8], S32, 0x8771F681); + c = HH(c, d, a, b, x[11], S33, 0x6D9D6122); + b = HH(b, c, d, a, x[14], S34, 0xFDE5380C); + a = HH(a, b, c, d, x[1], S31, 0xA4BEEA44); + d = HH(d, a, b, c, x[4], S32, 0x4BDECFA9); + c = HH(c, d, a, b, x[7], S33, 0xF6BB4B60); + b = HH(b, c, d, a, x[10], S34, 0xBEBFBC70); + a = HH(a, b, c, d, x[13], S31, 0x289B7EC6); + d = HH(d, a, b, c, x[0], S32, 0xEAA127FA); + c = HH(c, d, a, b, x[3], S33, 0xD4EF3085); + b = HH(b, c, d, a, x[6], S34, 0x4881D05); + a = HH(a, b, c, d, x[9], S31, 0xD9D4D039); + d = HH(d, a, b, c, x[12], S32, 0xE6DB99E5); + c = HH(c, d, a, b, x[15], S33, 0x1FA27CF8); + b = HH(b, c, d, a, x[2], S34, 0xC4AC5665); + + a = II(a, b, c, d, x[0], S41, 0xF4292244); + d = II(d, a, b, c, x[7], S42, 0x432AFF97); + c = II(c, d, a, b, x[14], S43, 0xAB9423A7); + b = II(b, c, d, a, x[5], S44, 0xFC93A039); + a = II(a, b, c, d, x[12], S41, 0x655B59C3); + d = II(d, a, b, c, x[3], S42, 0x8F0CCC92); + c = II(c, d, a, b, x[10], S43, 0xFFEFF47D); + b = II(b, c, d, a, x[1], S44, 0x85845DD1); + a = II(a, b, c, d, x[8], S41, 0x6FA87E4F); + d = II(d, a, b, c, x[15], S42, 0xFE2CE6E0); + c = II(c, d, a, b, x[6], S43, 0xA3014314); + b = II(b, c, d, a, x[13], S44, 0x4E0811A1); + a = II(a, b, c, d, x[4], S41, 0xF7537E82); + d = II(d, a, b, c, x[11], S42, 0xBD3AF235); + c = II(c, d, a, b, x[2], S43, 0x2AD7D2BB); + b = II(b, c, d, a, x[9], S44, 0xEB86D391); + + a = AddUnsigned(a, AA); + b = AddUnsigned(b, BB); + c = AddUnsigned(c, CC); + d = AddUnsigned(d, DD); + } }; diff --git a/Market.mqh b/Market.mqh index d044e2004..e57f9f3ca 100644 --- a/Market.mqh +++ b/Market.mqh @@ -24,27 +24,14 @@ #ifndef MARKET_MQH #define MARKET_MQH -// Forward declaration. -class Market; -class SymbolInfo; - // Includes. #include "Market.struct.h" #include "Math.h" -#include "Order.mqh" -#include "Serializer.mqh" +#include "Serializer/Serializer.h" #include "SymbolInfo.mqh" +#include "SymbolInfo.struct.static.h" #include "Task/TaskCondition.enum.h" -// Structs. -// Market info. -struct MarketData { - int empty; - // Serializers. - void SerializeStub(int _n1 = 1, int _n2 = 1, int _n3 = 1, int _n4 = 1, int _n5 = 1) {} - SerializerNodeType Serialize(Serializer &_s) { return SerializerNodeObject; } -}; - /** * Class to provide market information. */ diff --git a/Market.struct.h b/Market.struct.h index c1e4797d8..8148f7e54 100644 --- a/Market.struct.h +++ b/Market.struct.h @@ -25,10 +25,22 @@ * Includes Market's structs. */ +// Forward declaration. +class Serializer; + // Includes. #include "DateTime.struct.h" +#include "Serializer/SerializerNode.enum.h" #include "Std.h" +// Market info. +struct MarketData { + int empty; + // Serializers. + void SerializeStub(int _n1 = 1, int _n2 = 1, int _n3 = 1, int _n4 = 1, int _n5 = 1) {} + SerializerNodeType Serialize(Serializer &_s) { return SerializerNodeObject; } +}; + // Structure for trade time static methods. struct MarketTimeForex : DateTimeEntry { // Market sessions for trading Forex. diff --git a/Math.h b/Math.h index 24663c28c..371451f66 100644 --- a/Math.h +++ b/Math.h @@ -27,7 +27,7 @@ // Includes. #include "Data.struct.h" -#include "Indicator.struct.h" +#include "Indicator/Indicator.struct.h" #include "Math.define.h" #include "Math.enum.h" #include "Math.extern.h" diff --git a/Order.mqh b/Order.mqh index ab0007226..fda94260b 100644 --- a/Order.mqh +++ b/Order.mqh @@ -29,6 +29,9 @@ #ifndef ORDER_MQH #define ORDER_MQH +// Forward declaration. +class SymbolInfo; + // Includes. #include "Convert.mqh" #include "Data.define.h" @@ -38,8 +41,10 @@ #include "Order.define.h" #include "Order.enum.h" #include "Order.struct.h" -#include "SerializerConverter.mqh" -#include "SerializerJson.mqh" +#include "Serializer/Serializer.define.h" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerConverter.h" +#include "Serializer/SerializerJson.h" #include "Std.h" #include "String.mqh" #include "SymbolInfo.mqh" diff --git a/Order.struct.h b/Order.struct.h index d295b370f..0f7e11009 100644 --- a/Order.struct.h +++ b/Order.struct.h @@ -33,7 +33,7 @@ // Includes. #include "Data.struct.h" #include "Order.enum.h" -#include "Serializer.mqh" +#include "Serializer/Serializer.h" #include "SymbolInfo.struct.static.h" #include "Terminal.mqh" diff --git a/Platform.h b/Platform.h index 88ec23736..c7db1f1a7 100644 --- a/Platform.h +++ b/Platform.h @@ -27,15 +27,16 @@ */ #include "Flags.h" -#include "IndicatorBase.h" +#include "Indicator/IndicatorData.h" +#include "Indicator/tests/classes/IndicatorTfDummy.h" #include "Std.h" #ifdef __MQLBUILD__ -#include "Indicator/tests/classes/IndicatorTfDummy.h" #include "Indicators/Tick/Indi_TickMt.mqh" #define PLATFORM_DEFAULT_INDICATOR_TICK Indi_TickMt #else -#error "Platform not supported! +#include "Indicators/Tick/Indi_TickRandom.mqh" +#define PLATFORM_DEFAULT_INDICATOR_TICK Indi_TickRandom #endif #include "SymbolInfo.struct.static.h" @@ -43,6 +44,9 @@ class Platform { // Whether Init() was already called. static bool initialized; + // Global tick index. + static int global_tick_index; + // Date and time used to determine periods that passed. static DateTime time; @@ -74,6 +78,11 @@ class Platform { time.Update(); } + /** + * Returns global tick index. + */ + static int GetGlobalTickIndex() { return global_tick_index; } + /** * Performs tick on every added indicator. */ @@ -85,15 +94,18 @@ class Platform { DictStructIterator> _iter; for (_iter = indis.Begin(); _iter.IsValid(); ++_iter) { - _iter.Value() REF_DEREF Tick(); + _iter.Value() REF_DEREF Tick(global_tick_index); } for (_iter = indis_dflt.Begin(); _iter.IsValid(); ++_iter) { - _iter.Value() REF_DEREF Tick(); + _iter.Value() REF_DEREF Tick(global_tick_index); } // Will check for new time periods in consecutive Platform::UpdateTime(). time_clear_flags = true; + + // Started from 0. Will be incremented after each finished tick. + ++global_tick_index; } /** @@ -306,6 +318,7 @@ bool Platform::initialized = false; DateTime Platform::time = 0; unsigned int Platform::time_flags = 0; bool Platform::time_clear_flags = true; +int Platform::global_tick_index = 0; DictStruct> Platform::indis; DictStruct> Platform::indis_dflt; diff --git a/Redis.mqh b/Redis.mqh index 0cd498ec5..6857daf37 100644 --- a/Redis.mqh +++ b/Redis.mqh @@ -27,9 +27,9 @@ #include "Dict.mqh" #include "Object.mqh" #include "Redis.struct.h" -#include "Serializer.mqh" -#include "SerializerConversions.h" -#include "SerializerJson.mqh" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerConversions.h" +#include "Serializer/SerializerJson.h" #include "Socket.mqh" enum ENUM_REDIS_VALUE_SET { REDIS_VALUE_SET_ALWAYS, REDIS_VALUE_SET_IF_NOT_EXIST, REDIS_VALUE_SET_IF_ALREADY_EXIST }; diff --git a/Redis.struct.h b/Redis.struct.h index 82f9dc91b..dade26291 100644 --- a/Redis.struct.h +++ b/Redis.struct.h @@ -31,7 +31,7 @@ #endif // Includes. -#include "SerializerConversions.h" +#include "Serializer/SerializerConversions.h" // Forward declaration. class Serializer; diff --git a/ISerializable.h b/Serializer/Serializable.h similarity index 98% rename from ISerializable.h rename to Serializer/Serializable.h index 2d35ac4b6..341e4294b 100644 --- a/ISerializable.h +++ b/Serializer/Serializable.h @@ -34,7 +34,7 @@ class Serializer; -class ISerializable { +class Serializable { public: virtual SerializerNodeType Serialize(Serializer &s) = 0; }; diff --git a/Serializer.define.h b/Serializer/Serializer.define.h similarity index 100% rename from Serializer.define.h rename to Serializer/Serializer.define.h diff --git a/Serializer.enum.h b/Serializer/Serializer.enum.h similarity index 100% rename from Serializer.enum.h rename to Serializer/Serializer.enum.h diff --git a/Serializer.mqh b/Serializer/Serializer.h similarity index 93% rename from Serializer.mqh rename to Serializer/Serializer.h index acecedfb0..f7d65e66c 100644 --- a/Serializer.mqh +++ b/Serializer/Serializer.h @@ -25,20 +25,19 @@ #define SERIALIZER_MQH // Includes. -#include "Convert.mqh" +#include "../Convert.basic.h" +#include "../Terminal.define.h" #include "Serializer.define.h" #include "Serializer.enum.h" -#include "SerializerNode.mqh" -#include "SerializerNodeIterator.mqh" -#include "SerializerNodeParam.mqh" -#include "Terminal.define.h" +#include "SerializerNode.h" +#include "SerializerNodeIterator.h" +#include "SerializerNodeParam.h" #define SERIALIZER_DEFAULT_FP_PRECISION 8 // Forward declarations. template class SerializerIterator; -class IndicatorBase; class Serializer { protected: @@ -380,21 +379,6 @@ class Serializer { } } - template - void Pass(T& self, string name, IndicatorBase*& value, unsigned int flags = SERIALIZER_FIELD_FLAG_DEFAULT) { - if (_mode == Serialize) { - if (!IsFieldVisible(_flags, flags)) { - return; - } - - PassObject(self, name, value, flags); - } else { - Print("Error: Deserialization of IndicatorBase* cannot be done as method is abstract!"); - DebugBreak(); - value = nullptr; - } - } - /** * Serializes or unserializes simple value. */ @@ -436,17 +420,17 @@ class Serializer { SerializerNodeParamType paramType = PTR_ATTRIB(PTR_ATTRIB(child, GetValueParam()), GetType()); switch (paramType) { case SerializerNodeParamBool: - Convert::BoolToType(PTR_ATTRIB(PTR_ATTRIB(child, GetValueParam()), _integral)._bool, value); + ConvertBasic::BoolToType(PTR_ATTRIB(PTR_ATTRIB(child, GetValueParam()), _integral)._bool, value); break; case SerializerNodeParamLong: - Convert::LongToType(PTR_ATTRIB(PTR_ATTRIB(child, GetValueParam()), _integral)._long, value); + ConvertBasic::LongToType(PTR_ATTRIB(PTR_ATTRIB(child, GetValueParam()), _integral)._long, value); break; case SerializerNodeParamDouble: - Convert::DoubleToType(PTR_ATTRIB(PTR_ATTRIB(child, GetValueParam()), _integral)._double, value); + ConvertBasic::DoubleToType(PTR_ATTRIB(PTR_ATTRIB(child, GetValueParam()), _integral)._double, value); break; case SerializerNodeParamString: // There shouldn't be a conversion to int! - Convert::StringToType(PTR_ATTRIB(PTR_ATTRIB(child, GetValueParam()), _string), value); + ConvertBasic::StringToType(PTR_ATTRIB(PTR_ATTRIB(child, GetValueParam()), _string), value); break; default: Print("Error: Wrong param type ", paramType, "!"); diff --git a/SerializerBinary.mqh b/Serializer/SerializerBinary.h similarity index 97% rename from SerializerBinary.mqh rename to Serializer/SerializerBinary.h index ab9eb4a32..cbcb3c69d 100644 --- a/SerializerBinary.mqh +++ b/Serializer/SerializerBinary.h @@ -25,10 +25,10 @@ #define SERIALIZER_BINARY_MQH // Includes. -#include "DictBase.mqh" -#include "Object.mqh" -#include "Serializer.mqh" -#include "SerializerNode.mqh" +#include "../DictBase.mqh" +#include "../Object.mqh" +#include "Serializer.h" +#include "SerializerNode.h" class Log; diff --git a/SerializerConversions.h b/Serializer/SerializerConversions.h similarity index 97% rename from SerializerConversions.h rename to Serializer/SerializerConversions.h index d61862a51..5a64e1076 100644 --- a/SerializerConversions.h +++ b/Serializer/SerializerConversions.h @@ -30,10 +30,10 @@ #pragma once #endif -#include "Convert.extern.h" -#include "DateTime.extern.h" -#include "Object.mqh" -#include "Refs.struct.h" +#include "../Convert.extern.h" +#include "../DateTime.extern.h" +#include "../Object.mqh" +#include "../Refs.struct.h" class SerializerConversions { public: diff --git a/SerializerConverter.mqh b/Serializer/SerializerConverter.h similarity index 97% rename from SerializerConverter.mqh rename to Serializer/SerializerConverter.h index 2cf2f3a7f..3b6b5418e 100644 --- a/SerializerConverter.mqh +++ b/Serializer/SerializerConverter.h @@ -20,20 +20,24 @@ * */ +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + // Prevents processing this includes file for the second time. #ifndef SERIALIZER_CONVERTER_MQH #define SERIALIZER_CONVERTER_MQH -#include "SerializerDict.mqh" // Forward declarations. class SerializerNode; // Includes. -#include "File.mqh" -#include "Object.mqh" +#include "../File.mqh" #include "Serializer.enum.h" -#include "Serializer.mqh" -#include "SerializerNode.mqh" +#include "Serializer.h" +#include "SerializerDict.h" +#include "SerializerNode.h" class SerializerConverter { public: diff --git a/SerializerCsv.mqh b/Serializer/SerializerCsv.h similarity index 97% rename from SerializerCsv.mqh rename to Serializer/SerializerCsv.h index ffb17fefb..f7a97a5a4 100644 --- a/SerializerCsv.mqh +++ b/Serializer/SerializerCsv.h @@ -25,14 +25,14 @@ #define SERIALIZER_CSV_MQH // Includes. -#include "Dict.mqh" -#include "DictObject.mqh" -#include "DictStruct.mqh" -#include "Matrix.mqh" -#include "MiniMatrix.h" -#include "Object.mqh" -#include "SerializerConverter.mqh" -#include "SerializerNode.mqh" +#include "../Dict.mqh" +#include "../DictObject.mqh" +#include "../DictStruct.mqh" +#include "../Matrix.mqh" +#include "../MiniMatrix.h" +#include "../Object.mqh" +#include "SerializerConverter.h" +#include "SerializerNode.h" struct CsvTitle { int column_index; diff --git a/SerializerDict.mqh b/Serializer/SerializerDict.h similarity index 98% rename from SerializerDict.mqh rename to Serializer/SerializerDict.h index 279a068ef..83fa2ea80 100644 --- a/SerializerDict.mqh +++ b/Serializer/SerializerDict.h @@ -25,7 +25,7 @@ #define SERIALIZER_DICT_MQH // Includes. -#include "SerializerNode.mqh" +#include "SerializerNode.h" enum ENUM_SERIALIZER_DICT_FLAGS {}; diff --git a/SerializerJson.mqh b/Serializer/SerializerJson.h similarity index 98% rename from SerializerJson.mqh rename to Serializer/SerializerJson.h index bb6fb1a1e..e2d3d198a 100644 --- a/SerializerJson.mqh +++ b/Serializer/SerializerJson.h @@ -25,12 +25,12 @@ #define SERIALIZER_JSON_MQH // Includes. -#include "DictBase.mqh" -#include "Object.mqh" +#include "../DictBase.mqh" +#include "../Object.mqh" +#include "../String.extern.h" #include "Serializer.enum.h" -#include "Serializer.mqh" -#include "SerializerNode.mqh" -#include "String.extern.h" +#include "Serializer.h" +#include "SerializerNode.h" class Log; diff --git a/SerializerNode.enum.h b/Serializer/SerializerNode.enum.h similarity index 100% rename from SerializerNode.enum.h rename to Serializer/SerializerNode.enum.h diff --git a/SerializerNode.mqh b/Serializer/SerializerNode.h similarity index 98% rename from SerializerNode.mqh rename to Serializer/SerializerNode.h index e130aef88..516817b71 100644 --- a/SerializerNode.mqh +++ b/Serializer/SerializerNode.h @@ -21,14 +21,14 @@ */ // Prevents processing this includes file for the second time. -#ifndef JSON_NODE_MQH -#define JSON_NODE_MQH +#ifndef SERIALIZER_NODE_H +#define SERIALIZER_NODE_H // Includes. -#include "Math.extern.h" +#include "../Math.extern.h" +#include "../Terminal.define.h" #include "SerializerNode.enum.h" -#include "SerializerNodeParam.mqh" -#include "Terminal.define.h" +#include "SerializerNodeParam.h" class SerializerNode { protected: @@ -368,4 +368,4 @@ class SerializerNode { } }; -#endif +#endif // SERIALIZER_NODE_H diff --git a/SerializerNodeIterator.mqh b/Serializer/SerializerNodeIterator.h similarity index 96% rename from SerializerNodeIterator.mqh rename to Serializer/SerializerNodeIterator.h index 331e5ab93..f3cfe6a20 100644 --- a/SerializerNodeIterator.mqh +++ b/Serializer/SerializerNodeIterator.h @@ -21,11 +21,11 @@ */ // Prevents processing this includes file for the second time. -#ifndef JSON_ITERATOR_MQH -#define JSON_ITERATOR_MQH +#ifndef SERIALIZER_NODE_ITERATOR_H +#define SERIALIZER_NODE_ITERATOR_H -#include "Serializer.mqh" -#include "SerializerNode.mqh" +#include "Serializer.h" +#include "SerializerNode.h" class SerializerNode; class Serializer; diff --git a/SerializerNodeParam.mqh b/Serializer/SerializerNodeParam.h similarity index 99% rename from SerializerNodeParam.mqh rename to Serializer/SerializerNodeParam.h index 820a440c2..7ba0a834e 100644 --- a/SerializerNodeParam.mqh +++ b/Serializer/SerializerNodeParam.h @@ -22,8 +22,9 @@ // Prevents processing this includes file for the second time. #include "SerializerConversions.h" -#ifndef JSON_PARAM_MQH -#define JSON_PARAM_MQH + +#ifndef SERIALIZER_NODE_PARAM_H +#define SERIALIZER_NODE_PARAM_H /** * Enumeration. @@ -334,6 +335,4 @@ SerializerNodeParam* SerializerNodeParam::FromString(string& value) { return param; } - - -#endif +#endif // SERIALIZER_NODE_PARAM_H diff --git a/SerializerObject.mqh b/Serializer/SerializerObject.h similarity index 90% rename from SerializerObject.mqh rename to Serializer/SerializerObject.h index 2a98b583d..296106211 100644 --- a/SerializerObject.mqh +++ b/Serializer/SerializerObject.h @@ -25,11 +25,11 @@ #define SERIALIZER_OBJECT_MQH // Includes. -#include "DictBase.mqh" -#include "Object.mqh" -#include "Serializer.mqh" -#include "SerializerConverter.mqh" -#include "SerializerNode.mqh" +#include "../DictBase.mqh" +#include "../Object.mqh" +#include "Serializer.h" +#include "SerializerConverter.h" +#include "SerializerNode.h" class Log; diff --git a/SerializerSqlite.mqh b/Serializer/SerializerSqlite.h similarity index 97% rename from SerializerSqlite.mqh rename to Serializer/SerializerSqlite.h index db722442a..3ad38d373 100644 --- a/SerializerSqlite.mqh +++ b/Serializer/SerializerSqlite.h @@ -25,9 +25,9 @@ #define SERIALIZER_SQL_MQH // Includes. -#include "Database.mqh" -#include "SerializerConverter.mqh" -#include "SerializerCsv.mqh" +#include "../Database.mqh" +#include "SerializerConverter.h" +#include "SerializerCsv.h" class SerializerSqlite { public: diff --git a/tests/SerializerTest.mq4 b/Serializer/tests/Serializer.test.mq4 similarity index 97% rename from tests/SerializerTest.mq4 rename to Serializer/tests/Serializer.test.mq4 index bb9b208a8..b6c98a8f1 100644 --- a/tests/SerializerTest.mq4 +++ b/Serializer/tests/Serializer.test.mq4 @@ -25,4 +25,4 @@ */ // Includes. -#include "SerializerTest.mq5" +#include "Serializer.test.mq5" diff --git a/tests/SerializerTest.mq5 b/Serializer/tests/Serializer.test.mq5 similarity index 96% rename from tests/SerializerTest.mq5 rename to Serializer/tests/Serializer.test.mq5 index 34398c515..d218cb208 100644 --- a/tests/SerializerTest.mq5 +++ b/Serializer/tests/Serializer.test.mq5 @@ -27,19 +27,21 @@ */ // Includes. -#include "../BufferStruct.mqh" -#include "../Chart.mqh" -#include "../Config.mqh" -#include "../Data.struct.h" -#include "../DictStruct.mqh" -#include "../Serializer.mqh" -#include "../SerializerBinary.mqh" -#include "../SerializerCsv.mqh" -#include "../SerializerDict.mqh" -#include "../SerializerJson.mqh" -#include "../SerializerNode.mqh" -#include "../SerializerObject.mqh" -#include "../Test.mqh" +#include "../../BufferStruct.mqh" +#include "../../Chart.mqh" +#include "../../Config.mqh" +#include "../../Data.define.h" +#include "../../Data.struct.h" +#include "../../Data.struct.serialize.h" +#include "../../DictStruct.mqh" +#include "../../Test.mqh" +#include "../Serializer.h" +#include "../SerializerBinary.h" +#include "../SerializerCsv.h" +#include "../SerializerDict.h" +#include "../SerializerJson.h" +#include "../SerializerNode.h" +#include "../SerializerObject.h" struct SerializableSubEntry { public: diff --git a/Std.h b/Std.h index 9605cbeb1..c41abd901 100644 --- a/Std.h +++ b/Std.h @@ -34,6 +34,10 @@ #include #endif +#ifndef __MQL__ +#define __FUNCSIG__ __FUNCTION__ +#endif + #ifdef __MQL__ #define ASSIGN_TO_THIS(TYPE, VALUE) ((TYPE)this) = ((TYPE)VALUE) #else @@ -52,6 +56,7 @@ #define PTR_TO_REF(PTR) PTR #define MAKE_REF_FROM_PTR(TYPE, NAME, PTR) TYPE* NAME = PTR #define REF_DEREF .Ptr(). +#define int64 long #else #define GET_PTR(obj) (*obj) #define THIS_ATTR this-> @@ -63,6 +68,7 @@ #define PTR_TO_REF(PTR) (*PTR) #define MAKE_REF_FROM_PTR(TYPE, NAME, PTR) TYPE& NAME = PTR #define REF_DEREF .Ptr()-> +#define int64 long long #endif // References. @@ -93,7 +99,9 @@ * @usage * ARRAY_REF(, ) */ +#define ARRAY_TYPE(T) T[] #define ARRAY_REF(T, N) REF(T) N ARRAY_DECLARATION_BRACKETS +#define FIXED_ARRAY_REF(T, N, S) ARRAY_REF(T, N) #define CONST_ARRAY_REF(T, N) const N ARRAY_DECLARATION_BRACKETS @@ -112,12 +120,15 @@ #define CONST_REF_TO(T) const T& /** + * Reference to the array. * * @usage * ARRAY_REF(, ) */ -#define ARRAY_REF(T, N) _cpp_array& N +#define ARRAY_TYPE(T) _cpp_array +#define ARRAY_REF(T, N) ARRAY_TYPE(T)& N +#define FIXED_ARRAY_REF(T, N, S) T(&N)[S] #define CONST_ARRAY_REF(T, N) const _cpp_array& N @@ -166,6 +177,10 @@ class _cpp_array { m_isSeries = r.m_isSeries; } + std::vector& str() { return m_data; } + + void push(const T& value) { m_data.push_back(value); } + void operator=(const _cpp_array& r) { m_data = r.m_data; m_isSeries = r.m_isSeries; @@ -196,6 +211,17 @@ class _cpp_array { */ int size() const { return (int)m_data.size(); } + void resize(int new_size, int reserve_size = 0) { + // E.g., size = 10, new_size = 90, reserve_size = 50 + // thus: new_reserve_size = new_size + reserve_size - (new_size % reserve_size) + // which is: 90 + reserve_size - (90 % reserve_size) = 90 + 50 - 40 = 100. + if (reserve_size > 0) { + new_size = reserve_size - (new_size % reserve_size); + } + m_data.reserve(new_size); + m_data.resize(new_size); + } + /** * Checks whether */ @@ -209,6 +235,20 @@ class _cpp_array { void setIsSeries(bool _isSeries) { m_isSeries = _isSeries; } }; +#ifdef EMSCRIPTEN +#include + +#define REGISTER_ARRAY_OF(N, T, D) \ + EMSCRIPTEN_BINDINGS(N) { \ + emscripten::register_vector(D "CppVector"); \ + emscripten::class_<_cpp_array>(D) \ + .constructor() \ + .function("Push", &_cpp_array::push) \ + .function("Size", &_cpp_array::size); \ + } + +#endif + template class _cpp_array; #endif @@ -219,7 +259,7 @@ class color { unsigned int value; public: - color(unsigned int _color) { value = _color; } + color(unsigned int _color = 0) { value = _color; } color& operator=(unsigned int _color) { value = _color; return *this; @@ -283,22 +323,63 @@ class InvalidEnumValue { }; #ifndef __MQL__ +struct _WRONG_VALUE { + template + operator T() { + return (T)-1; + } +} WRONG_VALUE; + +const char* _empty_string_c = ""; +const string _empty_string = ""; + // Converter of NULL_VALUE into expected type. e.g., "int x = NULL_VALUE" will end up with "x = 0". struct _NULL_VALUE { template explicit operator T() const { return (T)0; } + } NULL_VALUE; +/** + * Converting an enumeration value of any type to a text form. + * + * @docs + * - https://www.mql5.com/en/docs/convert/enumtostring + */ +string EnumToString(int _value) { + std::stringstream ss; + // We really don't want to mess with type reflection here (if possible at all). So we are outputting the input + // integer. + ss << _value; + return ss.str(); +} + template <> -inline _NULL_VALUE::operator const std::string() const { - return ""; +_NULL_VALUE::operator string() const { + return _empty_string; } +#define NULL_STRING "" #else #define NULL_VALUE NULL +#define NULL_STRING NULL #endif +#ifndef __MQL__ +#include "Chart.enum.h" +/** + * Returns currently selected period for platform. + */ +// @fixit Should fetch selected period from somewhere. +extern ENUM_TIMEFRAMES Period(); + +#endif + +#define RUNTIME_ERROR(MSG) \ + Print(MSG); \ + DebugBreak(); + /** * Standarization of specifying ArraySetAsSeries for OnCalculate(). * Automatically determines required ArraySetAsSeries for buffers on start and diff --git a/Storage/ItemsHistory.h b/Storage/ItemsHistory.h new file mode 100644 index 000000000..d1edaed1e --- /dev/null +++ b/Storage/ItemsHistory.h @@ -0,0 +1,494 @@ +//+------------------------------------------------------------------+ +//| 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 . + * + */ + +// Ignore processing of this file if already included. +#ifndef ITEMS_HISTORY_H +#define ITEMS_HISTORY_H + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +#include "../DictStruct.mqh" +#include "../Refs.mqh" + +/** + * Direction used by ItemsHistoryItemProvider's methods. + */ +enum ENUM_ITEMS_HISTORY_DIRECTION { ITEMS_HISTORY_DIRECTION_FORWARD, ITEMS_HISTORY_DIRECTION_BACKWARD }; + +/** + * Forward declaration. + */ +template +class ItemsHistory; + +/** + * Generates/regenerates history for ItemsHistory class. Should be subclassed. + */ +template +class ItemsHistoryItemProvider : public Dynamic { + public: + /** + * Constructor. + */ + ItemsHistoryItemProvider() {} + + /** + * Retrieves given number of items starting from the given microseconds or index (inclusive). "_dir" identifies if we + * want previous or next items from selected starting point. + */ + void GetItems(ItemsHistory>* _history, long _from_time_ms, + ENUM_ITEMS_HISTORY_DIRECTION _dir, int _num_items, ARRAY_REF(IV, _out_arr)) { + // Method is called if there is a missing item (candle) in the history. We need to regenerate it. + Print("Error: Retrieving items by this item provider is not implemented!"); + DebugBreak(); + } + + /** + * Time of the first possible item/candle/tick. + */ + virtual long GetInitialTimeMs() { + // Item's provider does not implement GetInitialTimeMs(), but it should. We'll use current time for the time of the + // first item. + return (long)TimeCurrent() * 1000; + } + + /** + * Returns information about item provider. + */ + virtual string ToString() { return "Abstract items history item provider."; } +}; + +/** + * A continuous history of items. Appending new item may remove the + * oldest one and vice versa. We can't remove iems in-between the history. + * + * Indices in history's dict are incremented when adding new items, so + * first item will have index 0, second one will have index 1. + * + * When items are prepended, there could be a situation where the most + * recent items dissapeared and must be regenerated. + */ +template +class ItemsHistory { + // Provides items from bound provider. + Ref item_provider; + + // Holds items per its index. "shift" property indicates how indices + // are shifted from their index. + DictStruct history; + + // Maximum number of entries in history dict. + unsigned int history_max_size; + + // How indices are shifted from their stored index. Shift is incremented + // after each appended item. + int current_index; + + // Index of the first valid iem. Items between range + // shift <-> first_valid_shift must be regenerated in order to access + // them. Note that we can't regenerate item only from the given shift. + // We have to regenerate all between given shift <- first_valid_shift. + int first_valid_index; + + // Index of the most old item in the history ever added. + int first_valid_index_ever; + + // Index of the last valid item. items between range + // last_valid_shift <-> shift must be regenerated in order to access + // them. Note that we can't regenerate item only from the given shift. + // We have to regenerate all between last_valid_shift -> given shift. + int last_valid_index; + + /// Maximum number of items that occupied the history. + unsigned int peak_size; + + public: + /** + * Constructor + */ + ItemsHistory(unsigned int _history_max_size = 0) + : history_max_size(_history_max_size), + current_index(0), + first_valid_index(0), + first_valid_index_ever(0), + last_valid_index(0), + peak_size(0) {} + + /** + * Returns item provider. + */ + PT* GetItemProvider() { return item_provider.Ptr(); } + + /** + * Sets item provider. + */ + void SetItemProvider(PT* _item_provider) { item_provider = _item_provider; } + + /** + * Gets time in milliseconds of the last(oldest) item's time in current history time or 0. + */ + /* + long GetLastValidTimeInCache() { + return history.GetByKey(last_valid_index).GetTime(); + } + */ + + /** + * Returns maximum number of items that occupied the history. Could be used e.g., to determine how many bars could be + * retrieved from history and past the history. + */ + unsigned int GetPeakSize() { return peak_size; } + + /** + * Will regenerate items from item provider. "_dir" indicates if we have to prepend or append items. + */ + void RegenerateHistory(int _from_index, int _to_index, ENUM_ITEMS_HISTORY_DIRECTION _dir) { +#ifdef __debug_items_history__ + Print("RegenerateHistory(", _from_index, ", ", _to_index, ", ", EnumToString(_dir), "), ", GetInfo()); +#endif + + static ARRAY(IV, _items); // Items generated by provider. + ArrayResize(_items, 0); + + int _item_count = _to_index - _from_index + 1; + long _from_time_ms; + IV _item; + + // Calculating time to be passed to GetItems(). + if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { + if (history.Size() == 0) { +#ifdef __debug_items_history__ + Print("RegenerateHistory: Getting initial time from item provider"); +#endif + + // Time from we'll be getting items will be the time of the first possible item/candle/tick. + _from_time_ms = item_provider REF_DEREF GetInitialTimeMs(); + } else { +#ifdef __debug_items_history__ + Print("RegenerateHistory: Getting last valid item at index ", last_valid_index); +#endif + + // Time will be the time of last valid item + item's length + 1ms. + // Note that ticks have length of 0ms, so next tick could be at least 1ms after the previous tick. + _item = GetItemByIndex(last_valid_index); + _from_time_ms = _item.GetTimeMs() + _item.GetLengthMs() + 1; + } + + long _current_time_ms = TimeCurrent() * 1000; + + if (_from_time_ms > (long)TimeCurrent() * 1000) { + // There won't be items in the future. + return; + } + } else if (_dir == ITEMS_HISTORY_DIRECTION_BACKWARD) { + if (history.Size() == 0) { + // Time from we'll be getting items will be the time of the first possible item/candle/tick - 1ms. + _from_time_ms = item_provider REF_DEREF GetInitialTimeMs() - 1; + } else { +#ifdef __debug_items_history__ + Print("RegenerateHistory: Getting first valid item at index ", first_valid_index); +#endif + + // Time will be the time of the first valid item - 1ms. + _item = GetItemByIndex(first_valid_index); + _from_time_ms = _item.GetTimeMs() - 1; + } + } else { + Print("Error: We shouldn't be here!"); + DebugBreak(); + return; + } + + item_provider REF_DEREF GetItems(THIS_PTR, _from_time_ms, _dir, _item_count, _items); + + if (ArraySize(_items) != _item_count) { +// There's really no problem if number of generated items are less than +// requested. +// @todo However, if there's too many calls for RegenerateHistory then we +// need to find a way to make it exit earlier. +#ifdef __debug_items_history__ + Print("RegenerateHistory: Notice: Requested ", _item_count, " historic items, got ", ArraySize(_items), + ". from index = ", _from_index, ", to index = ", _to_index, ", dir = ", EnumToString(_dir), " (", GetInfo(), + ")"); +#endif + return; + } + + for (int i = 0; i < _item_count; ++i) { + if (_dir == ITEMS_HISTORY_DIRECTION_FORWARD) { + Append(_items[i], false); + } else if (_dir == ITEMS_HISTORY_DIRECTION_BACKWARD) { + Prepend(_items[i], false); + } + } + } + + string GetInfo() { + string _out; + _out += "first_valid_index_ever = " + IntegerToString(first_valid_index_ever) + ", "; + _out += "first_valid_index = " + IntegerToString(first_valid_index) + ", "; + _out += "current_index = " + IntegerToString(current_index) + ", "; + _out += "history_size = " + IntegerToString(history.Size()); + return _out; + } + + /** + * Appends item to the history and increments history shift, so current item will be the added one. + * + * If shift is lower than last_valid_shift then we need to regenerate history between last_valid_shift and shift. + */ + void Append(IV& _item, bool _allow_regenerate = true) { + if (peak_size > 0) { + // There was at least one prepended/appended item, so indices relates to existing items. + if (history_max_size != 0 && history.Size() >= history_max_size) { + // We need to remove first item from the history (the oldest one). + // Print("Removing item #", first_valid_index, " from the history."); + history.Unset(first_valid_index++); + } + + if (_allow_regenerate && last_valid_index < current_index) { +#ifdef __debug_items_history__ + Print("Append: Missing history between index ", last_valid_index, " and ", current_index, + ". We will try to regenerate it."); +#endif + // May call Append() multiple times with regenerated items. + RegenerateHistory(last_valid_index, current_index, ITEMS_HISTORY_DIRECTION_BACKWARD); + + // @todo Check if history was fully regenerated. + } + + // Incrementing current index and setting last valid index to the same value. + last_valid_index = ++current_index; + } + + // Adding item to the newly set index or index 0. + history.Set(current_index, _item); // if peak_size == 0 then current_index = 0. + + peak_size = MathMax(peak_size, history.Size()); + } + + /** + * Prepends item to the history. + * + * If shift is lower than last_valid_shift then we need to regenerate history between last_valid_shift and shift. + */ + void Prepend(IV& _item, bool _allow_regenerate = true) { + if (peak_size > 0) { + // There was at least one prepended/appended item, so indices relates to existing items. + if (history_max_size != 0 && history.Size() >= history_max_size) { + // We need to remove last item from the history (the newest one). + // Print("Removing item #", last_valid_index, " from the history."); + history.Unset(last_valid_index--); + } + + if (_allow_regenerate && first_valid_index_ever < current_index) { +#ifdef __debug_items_history__ + Print("Prepend: Missing history between index ", first_valid_index, " and ", current_index, + ". We will try to regenerate it."); +#endif + + // May call Prepend() multiple times with regenerated items. + RegenerateHistory(first_valid_index, current_index, ITEMS_HISTORY_DIRECTION_FORWARD); + + // @todo Check if history was fully regenerated. + } + + // last_valid_index stays on its own, because it is not a first item to be added to the history. + --first_valid_index; + } else { + // It is a first item to be prepended. last_valid_index will be now + // negative. That means we don't have information about item at + // index/shift 0. In order to retrieve item at index/shift 0 the history + // must be regenerated from last_valid_index (not inclusive) to index 0. + // Effectively, we would just need to regenerate item at index 0. + // current_index will stay at index 0. + last_valid_index = --first_valid_index; // last_valid_index = -1. + } + + // Adding item to the newly set index. + history.Set(first_valid_index, _item); + + // Saving index of the most old item in the history ever added. + first_valid_index_ever = MathMin(first_valid_index_ever, first_valid_index); + + peak_size = MathMax(peak_size, history.Size()); + } + + /** + * Updates item in the history. History must contain item with the given index! + */ + void Update(IV& _item, int _index) { + if (!history.KeyExists(_index)) { + Print("Error: You may only update existing items! History doesn't contain item with index ", _index, "."); + DebugBreak(); + } + history.Set(_index, _item); + } + + /** + * Ensures that the given shift exists. Tries to regenerate the history if it does not. + */ + bool EnsureShiftExists(int _shift) { + if (history.Size() == 0) { + return false; + } + +#ifdef __debug_items_history__ + Print("EnsureShiftExists(", _shift, ")"); +#endif + + int _index = GetShiftIndex(_shift); + if (_index < first_valid_index) { + RegenerateHistory(_index, first_valid_index - 1, ITEMS_HISTORY_DIRECTION_BACKWARD); + } else if (_index > last_valid_index) { + RegenerateHistory(last_valid_index + 1, _index, ITEMS_HISTORY_DIRECTION_FORWARD); + } + + return history.KeyExists(_index); + } + + /** + * Returns item at given index. Index must exist! + */ + IV GetItemByIndex(int _index, bool _try_regenerate = true) { + IV _item; + +#ifdef __debug_items_history__ + Print("GetItemByIndex(", _index, ", try_regenerate = ", _try_regenerate, ")"); +#endif + + if (!TryGetItemByIndex(_index, _item, _try_regenerate)) { + Print("Error: Given index ", _index, + " is outside the range of valid items! Errored: ", item_provider REF_DEREF ToString()); + DebugBreak(); + } + return _item; + } + + /** + * Tries to get item at given index. + */ + bool TryGetItemByIndex(int _index, IV& _out_item, bool _try_regenerate = true) { + if (history.Size() == 0 || _index < first_valid_index || _index > last_valid_index) { + if (!_try_regenerate) { + // Print("Notice: Missing history. Tried to get item at index ", _index); + return false; + } + +#ifdef __debug_items_history__ + Print("TryGetItemByIndex(", _index, ", try_regenerate = ", _try_regenerate, ")"); +#endif + + // Whether we need to prepend old items or append new ones. + ENUM_ITEMS_HISTORY_DIRECTION _dir = + _index < first_valid_index ? ITEMS_HISTORY_DIRECTION_BACKWARD : ITEMS_HISTORY_DIRECTION_FORWARD; + RegenerateHistory(_index < first_valid_index ? _index : (last_valid_index - 1), + _index < first_valid_index ? (first_valid_index - 1) : _index, _dir); + // Trying to get item again, but without regeneration at this time. + return TryGetItemByIndex(_index, _out_item, false); + } + _out_item = history.GetByKey(_index); + return true; + } + + /** + * Returns index of the current item. 0 if there were no items added or there + * is a single item. + */ + int GetCurrentIndex() { return current_index; } + + /** + * Returns history index from the given shift. + */ + int GetShiftIndex(int _shift) { return current_index - _shift; } + + /** + * Returns item at given shift. Shift must exist! + */ + IV GetItemByShift(int _shift, bool _try_regenerate = true) { + return GetItemByIndex(GetShiftIndex(_shift), _try_regenerate); + } + + /** + * Tries to get item at given shift. + */ + bool TryGetItemByShift(int _shift, IV& _out_item, bool _try_regenerate = true) { + return TryGetItemByIndex(GetShiftIndex(_shift), _out_item, _try_regenerate); + } + + /** + * Returns item time in milliseconds for the given shift. + */ + long GetItemTimeByShiftMsc(int _shift) { + if (!EnsureShiftExists(_shift)) { + // There won't be item at given shift. + return (datetime)0; + } + + return GetItemByShift(_shift).GetTimeMs(); + } + + /** + * Returns bar date and time for the given shift. + */ + datetime GetItemTimeByShift(int _shift) { +#ifdef __debug_items_history__ + Print("GetItemTimeByShift(", _shift, "), ", GetInfo()); +#endif + + if (!EnsureShiftExists(_shift)) { + // There won't be item at given shift. + return (datetime)0; + } + + return (datetime)(GetItemTimeByShiftMsc(_shift) / 1000); + } + + /** + * Removes recently added item. + */ + bool RemoveRecentItem() { + history.Unset(last_valid_index); + + // Going back to previous item. + current_index = --last_valid_index; + + // Peak size is less by one item. + --peak_size; + + return history.Size() > 0; + } + + /** + * Removes recently added items. + */ + void RemoveRecentItems(int _num_to_remove = INT_MAX) { + while (_num_to_remove-- > 0 && RemoveRecentItem()) { + // Removing item one by one ^^. + } + } +}; + +#endif diff --git a/Storage/ValueStorage.applied_price.h b/Storage/ValueStorage.applied_price.h index 4ae05ec4b..b7de7fd42 100644 --- a/Storage/ValueStorage.applied_price.h +++ b/Storage/ValueStorage.applied_price.h @@ -54,19 +54,20 @@ class AppliedPriceValueStorage : public HistoryValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - double Fetch(int _shift) override { + double Fetch(int _rel_shift) override { switch (ap) { case PRICE_OPEN: case PRICE_HIGH: case PRICE_LOW: case PRICE_CLOSE: - return Fetch(ap, _shift); + return Fetch(ap, _rel_shift); case PRICE_MEDIAN: - return (Fetch(PRICE_HIGH, _shift) + Fetch(PRICE_LOW, _shift)) / 2; + return (Fetch(PRICE_HIGH, _rel_shift) + Fetch(PRICE_LOW, _rel_shift)) / 2; case PRICE_TYPICAL: - return (Fetch(PRICE_HIGH, _shift) + Fetch(PRICE_LOW, _shift) + Fetch(PRICE_CLOSE, _shift)) / 3; + return (Fetch(PRICE_HIGH, _rel_shift) + Fetch(PRICE_LOW, _rel_shift) + Fetch(PRICE_CLOSE, _rel_shift)) / 3; case PRICE_WEIGHTED: - return (Fetch(PRICE_HIGH, _shift) + Fetch(PRICE_LOW, _shift) + (2 * Fetch(PRICE_CLOSE, _shift))) / 4; + return (Fetch(PRICE_HIGH, _rel_shift) + Fetch(PRICE_LOW, _rel_shift) + (2 * Fetch(PRICE_CLOSE, _rel_shift))) / + 4; default: Print("We shouldn't be here!"); DebugBreak(); @@ -74,7 +75,9 @@ class AppliedPriceValueStorage : public HistoryValueStorage { return 0.0; } - double Fetch(ENUM_APPLIED_PRICE _ap, int _shift) { return indi_candle REF_DEREF GetPrice(_ap, RealShift(_shift)); } + double Fetch(ENUM_APPLIED_PRICE _ap, int _rel_shift) { + return indi_candle REF_DEREF GetPrice(_ap, RealShift(_rel_shift)); + } static double GetApplied(ValueStorage &_open, ValueStorage &_high, ValueStorage &_low, ValueStorage &_close, int _shift, ENUM_APPLIED_PRICE _ap) { diff --git a/Storage/ValueStorage.h b/Storage/ValueStorage.h index 70c431691..ec40c2d80 100644 --- a/Storage/ValueStorage.h +++ b/Storage/ValueStorage.h @@ -34,7 +34,7 @@ #define VALUE_STORAGE_H // Includes. -#include "../SerializerConversions.h" +#include "../Serializer/SerializerConversions.h" #include "../Util.h" #include "Objects.h" @@ -153,7 +153,7 @@ class ValueStorage : public IValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - virtual C Fetch(int _shift) { + virtual C Fetch(int _rel_shift) { Alert("Fetching data by shift is not supported from this value storage!"); DebugBreak(); return (C)0; @@ -221,7 +221,8 @@ void ArrayInitialize(ValueStorage &_storage, C _value) { * ValueStorage-compatible wrapper for ArrayCopy. */ template -int ArrayCopy(D &_target[], ValueStorage &_source, int _dst_start = 0, int _src_start = 0, int count = WHOLE_ARRAY) { +int ArrayCopy(ARRAY_REF(D, _target), ValueStorage &_source, int _dst_start = 0, int _src_start = 0, + int count = WHOLE_ARRAY) { if (count == WHOLE_ARRAY) { count = ArraySize(_source); } diff --git a/Storage/ValueStorage.history.h b/Storage/ValueStorage.history.h index 3494b0e41..31883a03f 100644 --- a/Storage/ValueStorage.history.h +++ b/Storage/ValueStorage.history.h @@ -31,10 +31,11 @@ #endif // Includes. +#include "../Indicator/IndicatorData.h" #include "ValueStorage.h" // Forward declarations. -class IndicatorBase; +class IndicatorData; template class ValueStorage; @@ -54,7 +55,7 @@ class HistoryValueStorage : public ValueStorage { /** * Constructor. */ - HistoryValueStorage(IndicatorBase* _indi_candle, bool _is_series = false) + HistoryValueStorage(IndicatorData* _indi_candle, bool _is_series = false) : indi_candle(_indi_candle), is_series(_is_series) { if (_indi_candle == nullptr) { Print("You have to pass IndicatorCandle-compatible indicator as parameter to HistoryValueStorage!"); @@ -83,6 +84,8 @@ class HistoryValueStorage : public ValueStorage { /** * Number of bars passed from the start. There will be a single bar at the start. + * + * Note that number of bars are decremented by iparams.shift of the candle indicator. */ int BarsFromStart() { if (!indi_candle.ObjectExists()) { diff --git a/Storage/ValueStorage.indicator.h b/Storage/ValueStorage.indicator.h index d2a4b97eb..ad69719bc 100644 --- a/Storage/ValueStorage.indicator.h +++ b/Storage/ValueStorage.indicator.h @@ -54,5 +54,5 @@ class IndicatorBufferValueStorage : public HistoryValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - C Fetch(int _shift) override { return indi_candle REF_DEREF GetValue(mode, RealShift(_shift)); } + C Fetch(int _rel_shift) override { return indi_candle REF_DEREF GetValue(mode, RealShift(_rel_shift)); } }; diff --git a/Storage/ValueStorage.native.h b/Storage/ValueStorage.native.h index b55c029c3..81281c45f 100644 --- a/Storage/ValueStorage.native.h +++ b/Storage/ValueStorage.native.h @@ -1,6 +1,6 @@ //+------------------------------------------------------------------+ //| EA31337 framework | -//| Copyright 2016-2023, EA31337 Ltd | +//| Copyright 2016-2021, EA31337 Ltd | //| https://github.com/EA31337 | //+------------------------------------------------------------------+ @@ -68,30 +68,23 @@ class NativeValueStorage : public ValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. + * + * Note that this storage type operates on absolute shifts! */ - C Fetch(int _shift) override { - if (_shift < 0 || _shift >= Size()) { + C Fetch(int _abs_shift) override { + if (_abs_shift < 0 || _abs_shift >= Size()) { + // Alert("Error: NativeValueStorage: Invalid buffer data index: ", _shift, ". Buffer size: ", Size()); + // DebugBreak(); return (C)EMPTY_VALUE; } - int _index = GetRealIndex(_shift); - - return _values[_index]; + return _values[_abs_shift]; } /** * Stores value at a given shift. Takes into consideration as-series flag. */ - void Store(int _shift, C _value) override { - if (_shift < 0 || _shift >= Size()) { - Alert("Error: NativeValueStorage: Invalid buffer data index: ", _shift, ". Buffer size: ", Size()); - DebugBreak(); - } - - int _index = GetRealIndex(_shift); - - Array::ArrayStore(_values, _index, _value, 4096); - } + void Store(int _shift, C _value) override { Array::ArrayStore(_values, _shift, _value, 4096); } /** * Inserts new value at the end of the buffer. If buffer works as As-Series, diff --git a/Storage/ValueStorage.price_median.h b/Storage/ValueStorage.price_median.h index 6ba406e46..3752af417 100644 --- a/Storage/ValueStorage.price_median.h +++ b/Storage/ValueStorage.price_median.h @@ -24,6 +24,9 @@ * Median price version of ValueStorage. */ +// Forward declarations. +class IndicatorBase; + // Includes. #include "ObjectsCache.h" #include "ValueStorage.history.h" @@ -46,9 +49,9 @@ class PriceMedianValueStorage : public HistoryValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - double Fetch(int _shift) override { + double Fetch(int _rel_shift) override { ResetLastError(); - double _value = indi_candle REF_DEREF GetOHLC(RealShift(_shift)).GetMedian(); + double _value = indi_candle REF_DEREF GetOHLC(RealShift(_rel_shift)).GetMedian(); if (_LastError != ERR_NO_ERROR) { Print("Cannot fetch OHLC! Error: ", _LastError); DebugBreak(); diff --git a/Storage/ValueStorage.price_typical.h b/Storage/ValueStorage.price_typical.h index 91337dc77..4bf51f786 100644 --- a/Storage/ValueStorage.price_typical.h +++ b/Storage/ValueStorage.price_typical.h @@ -46,9 +46,9 @@ class PriceTypicalValueStorage : public HistoryValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - double Fetch(int _shift) override { + double Fetch(int _rel_shift) override { ResetLastError(); - double _value = indi_candle REF_DEREF GetOHLC(RealShift(_shift)).GetTypical(); + double _value = indi_candle REF_DEREF GetOHLC(RealShift(_rel_shift)).GetTypical(); if (_LastError != ERR_NO_ERROR) { Print("Cannot fetch OHLC! Error: ", _LastError); DebugBreak(); diff --git a/Storage/ValueStorage.price_weighted.h b/Storage/ValueStorage.price_weighted.h index 1fab60e08..17818583e 100644 --- a/Storage/ValueStorage.price_weighted.h +++ b/Storage/ValueStorage.price_weighted.h @@ -46,9 +46,9 @@ class PriceWeightedValueStorage : public HistoryValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - double Fetch(int _shift) override { + double Fetch(int _rel_shift) override { ResetLastError(); - double _value = indi_candle REF_DEREF GetOHLC(RealShift(_shift)).GetWeighted(); + double _value = indi_candle REF_DEREF GetOHLC(RealShift(_rel_shift)).GetWeighted(); if (_LastError != ERR_NO_ERROR) { Print("Cannot fetch OHLC! Error: ", _LastError); DebugBreak(); diff --git a/Storage/ValueStorage.spread.h b/Storage/ValueStorage.spread.h index 4089d5401..ead33fbcf 100644 --- a/Storage/ValueStorage.spread.h +++ b/Storage/ValueStorage.spread.h @@ -47,5 +47,5 @@ class SpreadValueStorage : public HistoryValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - long Fetch(int _shift) override { return indi_candle REF_DEREF GetSpread(RealShift(_shift)); } + long Fetch(int _rel_shift) override { return indi_candle REF_DEREF GetSpread(RealShift(_rel_shift)); } }; diff --git a/Storage/ValueStorage.tick_volume.h b/Storage/ValueStorage.tick_volume.h index 8754c2869..40e6a78a9 100644 --- a/Storage/ValueStorage.tick_volume.h +++ b/Storage/ValueStorage.tick_volume.h @@ -46,5 +46,5 @@ class TickVolumeValueStorage : public HistoryValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - long Fetch(int _shift) override { return indi_candle REF_DEREF GetVolume(RealShift(_shift)); } + long Fetch(int _rel_shift) override { return indi_candle REF_DEREF GetVolume(RealShift(_rel_shift)); } }; diff --git a/Storage/ValueStorage.time.h b/Storage/ValueStorage.time.h index 1f48a1871..be83827e9 100644 --- a/Storage/ValueStorage.time.h +++ b/Storage/ValueStorage.time.h @@ -47,5 +47,5 @@ class TimeValueStorage : public HistoryValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - datetime Fetch(int _shift) override { return indi_candle REF_DEREF GetBarTime(RealShift(_shift)); } + datetime Fetch(int _rel_shift) override { return indi_candle REF_DEREF GetBarTime(RealShift(_rel_shift)); } }; diff --git a/Storage/ValueStorage.volume.h b/Storage/ValueStorage.volume.h index d1686366f..39bd4d8ba 100644 --- a/Storage/ValueStorage.volume.h +++ b/Storage/ValueStorage.volume.h @@ -46,9 +46,9 @@ class VolumeValueStorage : public HistoryValueStorage { /** * Fetches value from a given shift. Takes into consideration as-series flag. */ - long Fetch(int _shift) override { + long Fetch(int _rel_shift) override { ResetLastError(); - long _volume = indi_candle REF_DEREF GetVolume(RealShift(_shift)); + long _volume = indi_candle REF_DEREF GetVolume(RealShift(_rel_shift)); if (_LastError != ERR_NO_ERROR) { Print("Cannot fetch iVolume! Error: ", _LastError); DebugBreak(); diff --git a/Storage/tests/ItemsHistory.mq4 b/Storage/tests/ItemsHistory.mq4 new file mode 100644 index 000000000..eaed0b07c --- /dev/null +++ b/Storage/tests/ItemsHistory.mq4 @@ -0,0 +1,28 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2020, 31337 Investments 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 + * Test functionality of ItemsHistory class. + */ + +// Includes. +#include "ItemsHistory.mq5" diff --git a/Storage/tests/ItemsHistory.mq5 b/Storage/tests/ItemsHistory.mq5 new file mode 100644 index 000000000..43ac6abf3 --- /dev/null +++ b/Storage/tests/ItemsHistory.mq5 @@ -0,0 +1,86 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2020, 31337 Investments 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 + * Test functionality of ItemsHistory class. + */ + +// Defines +#define INDI_CANDLE_HISTORY_SIZE 4 + +// Includes +#include "../../Platform.h" +#include "../../Test.mqh" +#include "../ItemsHistory.h" + +// Candles buffer. +ARRAY(BarOHLC, _ohlcs); + +/** + * Implements OnInit(). + */ +int OnInit() { + Platform::Init(); + + return (GetLastError() > 0 ? INIT_FAILED : INIT_SUCCEEDED); +}; + +/** + * Implements OnTick(). + */ +void OnTick() { + Platform::Tick(); + + IndicatorData* _candles = Platform::FetchDefaultCandleIndicator(_Symbol, PERIOD_CURRENT); + + if (_candles PTR_DEREF IsNewBar()) { + BarOHLC _ohlc = _candles PTR_DEREF GetOHLC(0); + ArrayPushObject(_ohlcs, _ohlc); + + Print(_ohlc.ToCSV()); + + if (_candles PTR_DEREF GetBarIndex() == 1) { + // Updating first candle to be sure it was formed by all possible ticks. + _ohlcs[0] = _candles PTR_DEREF GetOHLC(1); + } + + if (_candles PTR_DEREF GetBarIndex() == INDI_CANDLE_HISTORY_SIZE) { + // Now first candle should be forgotten by candle history. We'll check if candle regeneration works. + Print("First candle was: ", _ohlcs[0].ToCSV()); + + BarOHLC _ohlc_regenerated = _candles PTR_DEREF GetOHLC(INDI_CANDLE_HISTORY_SIZE); + Print("Regenerated candle is: ", _ohlc_regenerated.ToCSV()); + + if (_ohlcs[0] != _ohlc_regenerated) { + Print("Error: Candle regeneration resulted in OHLC/time difference!"); + Print("Expected: ", _ohlcs[0].ToCSV()); + Print("Got: ", _ohlc_regenerated.ToCSV()); + ExpertRemove(); + } + } + } +} + +/** + * Implements OnDeinit(). + */ +void OnDeinit(const int reason) {} diff --git a/Strategy.mqh b/Strategy.mqh index 6958cf969..115b7660a 100644 --- a/Strategy.mqh +++ b/Strategy.mqh @@ -30,7 +30,7 @@ class Trade; // Includes. #include "Data.struct.h" #include "Dict.mqh" -#include "Indicator.mqh" +#include "Indicator/Indicator.h" #include "Market.mqh" #include "Object.mqh" #include "Strategy.enum.h" @@ -284,9 +284,9 @@ class Strategy : public Taskable { /** * Executes OnTick() on every attached indicator. */ - void Tick() { + void Tick(int _global_tick_index) { for (DictIterator> it = indicators.Begin(); it.IsValid(); ++it) { - it.Value() REF_DEREF Tick(); + it.Value() REF_DEREF Tick(_global_tick_index); } } diff --git a/Strategy.struct.h b/Strategy.struct.h index f3a5b5e04..3e25ac4a0 100644 --- a/Strategy.struct.h +++ b/Strategy.struct.h @@ -31,7 +31,7 @@ #endif // Includes. -#include "Serializer.mqh" +#include "Serializer/Serializer.h" #include "Strategy.enum.h" #include "Strategy.struct.pricestop.h" #include "Task/Task.struct.h" @@ -374,7 +374,7 @@ struct StgParams { struct Stg_Params { string symbol; ENUM_TIMEFRAMES tf; - Stg_Params() : symbol(_Symbol), tf((ENUM_TIMEFRAMES)_Period) {} + Stg_Params() : symbol(_Symbol), tf((ENUM_TIMEFRAMES)Period()) {} }; /* Structure for strategy's process results. */ diff --git a/SymbolInfo.extern.h b/SymbolInfo.extern.h index 92f4a1ce8..0d2f6018b 100644 --- a/SymbolInfo.extern.h +++ b/SymbolInfo.extern.h @@ -23,7 +23,7 @@ // Includes. #include "Order.enum.h" #include "SymbolInfo.enum.h" -#include "Tick.struct.h" +#include "Tick/Tick.struct.h" // Define external global functions. #ifndef __MQL__ diff --git a/SymbolInfo.mqh b/SymbolInfo.mqh index d53c2fd47..45dbc5b12 100644 --- a/SymbolInfo.mqh +++ b/SymbolInfo.mqh @@ -24,9 +24,6 @@ #ifndef SYMBOLINFO_MQH #define SYMBOLINFO_MQH -// Forward declaration. -class SymbolInfo; - // Includes symbol defines, enums and structs. #include "SymbolInfo.define.h" #include "SymbolInfo.enum.h" @@ -35,10 +32,14 @@ class SymbolInfo; #include "SymbolInfo.struct.h" #include "SymbolInfo.struct.static.h" +// Forward declaration. +class Log; +class SymbolInfo; + // Includes. #include "Log.mqh" -#include "Serializer.mqh" -#include "SerializerNode.enum.h" +#include "Serializer/Serializer.h" +#include "Serializer/SerializerNode.enum.h" /** * Class to provide symbol information. @@ -230,9 +231,7 @@ class SymbolInfo : public Object { * Get number of points per pip. * */ - unsigned int GetPointsPerPip() { - return sprops.pts_per_pip; - } + unsigned int GetPointsPerPip() { return sprops.pts_per_pip; } /** * Get the point size in the quote currency. diff --git a/SymbolInfo.struct.h b/SymbolInfo.struct.h index 5e1096ad3..e7488b05a 100644 --- a/SymbolInfo.struct.h +++ b/SymbolInfo.struct.h @@ -31,15 +31,16 @@ #endif // Includes. -#include "ISerializable.h" +#include "Serializer/Serializable.h" #include "Std.h" #include "SymbolInfo.struct.static.h" -#include "Tick.struct.h" +#include "Tick/Tick.struct.h" +#include "Serializer/Serializer.h" // Defines struct to store symbol data. struct SymbolInfoEntry #ifndef __MQL__ - : public ISerializable + : public Serializable #endif { double bid; // Current Bid price. @@ -152,8 +153,6 @@ struct SymbolInfoProp { SerializerNodeType Serialize(Serializer& _s); }; -#include "Serializer.mqh" - SerializerNodeType SymbolInfoEntry::Serialize(Serializer& _s) { _s.Pass(THIS_REF, "ask", ask); _s.Pass(THIS_REF, "bid", bid); diff --git a/SymbolInfo.struct.static.h b/SymbolInfo.struct.static.h index 4aba73a3c..f845ba690 100644 --- a/SymbolInfo.struct.static.h +++ b/SymbolInfo.struct.static.h @@ -28,7 +28,7 @@ #include "MQL5.mqh" #include "Order.enum.h" #include "Std.h" -#include "Tick.struct.h" +#include "Tick/Tick.struct.h" /** * Struct to provide symbol information. @@ -128,7 +128,7 @@ struct SymbolInfoStatic { */ static double GetPipValue(string _symbol) { unsigned int _pdigits = GetPipDigits(_symbol); - return 10 >> _pdigits; + return 1.0 / MathPow(10, (int)_pdigits); } /** diff --git a/Task/TaskAction.struct.h b/Task/TaskAction.struct.h index 02345ad6e..5b4d87baf 100644 --- a/Task/TaskAction.struct.h +++ b/Task/TaskAction.struct.h @@ -31,10 +31,14 @@ // Includes. #include "../Data.struct.h" +#include "../Serializer/Serializer.define.h" #include "../Std.h" #include "../Terminal.define.h" #include "Task.enum.h" +// Forward declarations. +class Serializer; + /* Entry for TaskAction class. */ struct TaskActionEntry { public: @@ -182,15 +186,7 @@ struct TaskActionEntry { ::ArrayResize(args, _index - 1); } // Serializers - SerializerNodeType Serialize(Serializer &s) { - s.Pass(THIS_REF, "flags", flags); - s.Pass(THIS_REF, "id", id); - s.Pass(THIS_REF, "time_last_run", time_last_run); - s.Pass(THIS_REF, "tries", tries); - s.PassEnum(THIS_REF, "freq", freq); - s.PassArray(THIS_REF, "args", args); - return SerializerNodeObject; - } + SerializerNodeType Serialize(Serializer &s); SERIALIZER_EMPTY_STUB; }; diff --git a/Task/TaskAction.struct.serialize.h b/Task/TaskAction.struct.serialize.h new file mode 100644 index 000000000..54d0fc39e --- /dev/null +++ b/Task/TaskAction.struct.serialize.h @@ -0,0 +1,44 @@ +//+------------------------------------------------------------------+ +//| 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 TaskAction's structure serialization methods. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Serializer/Serializer.h" +#include "TaskAction.struct.h" + +SerializerNodeType TaskActionEntry::Serialize(Serializer &s) { + s.Pass(THIS_REF, "flags", flags); + s.Pass(THIS_REF, "id", id); + s.Pass(THIS_REF, "time_last_run", time_last_run); + s.Pass(THIS_REF, "tries", tries); + s.PassEnum(THIS_REF, "freq", freq); + s.PassArray(THIS_REF, "args", args); + return SerializerNodeObject; +} diff --git a/Task/TaskCondition.struct.h b/Task/TaskCondition.struct.h index 6b5318d73..c2af7fc3a 100644 --- a/Task/TaskCondition.struct.h +++ b/Task/TaskCondition.struct.h @@ -31,10 +31,15 @@ // Includes. #include "../Data.struct.h" +#include "../Serializer/Serializer.define.h" +#include "../Serializer/Serializer.enum.h" #include "../Std.h" #include "../Terminal.define.h" #include "Task.enum.h" +// Forward declarations. +class Serializer; + struct TaskConditionEntry { public: /* Enumerations */ @@ -198,16 +203,7 @@ struct TaskConditionEntry { public: // Serializers - SerializerNodeType Serialize(Serializer &s) { - s.Pass(THIS_REF, "flags", flags); - s.Pass(THIS_REF, "id", id); - s.Pass(THIS_REF, "last_check", last_check); - s.Pass(THIS_REF, "last_success", last_success); - s.Pass(THIS_REF, "tries", tries); - s.PassEnum(THIS_REF, "freq", freq); - s.PassArray(THIS_REF, "args", args); - return SerializerNodeObject; - } + SerializerNodeType Serialize(Serializer &s); SERIALIZER_EMPTY_STUB; }; diff --git a/Task/TaskCondition.struct.serialize.h b/Task/TaskCondition.struct.serialize.h new file mode 100644 index 000000000..36385b572 --- /dev/null +++ b/Task/TaskCondition.struct.serialize.h @@ -0,0 +1,44 @@ +//+------------------------------------------------------------------+ +//| 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 TaskCondition's structures serialization methods. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "TaskCondition.struct.h" + +SerializerNodeType TaskConditionEntry::Serialize(Serializer &s) { + s.Pass(THIS_REF, "flags", flags); + s.Pass(THIS_REF, "id", id); + s.Pass(THIS_REF, "last_check", last_check); + s.Pass(THIS_REF, "last_success", last_success); + s.Pass(THIS_REF, "tries", tries); + s.PassEnum(THIS_REF, "freq", freq); + s.PassArray(THIS_REF, "args", args); + return SerializerNodeObject; +} diff --git a/Task/TaskGetter.struct.h b/Task/TaskGetter.struct.h index ac35a6ee1..f17f16d45 100644 --- a/Task/TaskGetter.struct.h +++ b/Task/TaskGetter.struct.h @@ -31,6 +31,7 @@ // Includes. #include "../Data.struct.h" +#include "../Serializer/Serializer.define.h" #include "../Std.h" #include "../Terminal.define.h" #include "Task.enum.h" @@ -169,15 +170,7 @@ struct TaskGetterEntry { // @todo: for(). } // Serializers - SerializerNodeType Serialize(Serializer &s) { - s.Pass(THIS_REF, "flags", flags); - s.Pass(THIS_REF, "id", id); - s.Pass(THIS_REF, "time_last_get", time_last_get); - s.Pass(THIS_REF, "tries", tries); - s.PassEnum(THIS_REF, "freq", freq); - s.PassArray(THIS_REF, "args", args); - return SerializerNodeObject; - } + SerializerNodeType Serialize(Serializer &s); SERIALIZER_EMPTY_STUB; }; diff --git a/Task/TaskGetter.struct.serialize.h b/Task/TaskGetter.struct.serialize.h new file mode 100644 index 000000000..6afcaa1ac --- /dev/null +++ b/Task/TaskGetter.struct.serialize.h @@ -0,0 +1,44 @@ +//+------------------------------------------------------------------+ +//| 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 TaskGetter's structure serialization methods. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Serializer/Serializer.h" +#include "TaskGetter.struct.h" + +SerializerNodeType TaskGetterEntry::Serialize(Serializer &s) { + s.Pass(THIS_REF, "flags", flags); + s.Pass(THIS_REF, "id", id); + s.Pass(THIS_REF, "time_last_get", time_last_get); + s.Pass(THIS_REF, "tries", tries); + s.PassEnum(THIS_REF, "freq", freq); + s.PassArray(THIS_REF, "args", args); + return SerializerNodeObject; +} diff --git a/Task/TaskManager.h b/Task/TaskManager.h index f559a15d3..d827222f4 100644 --- a/Task/TaskManager.h +++ b/Task/TaskManager.h @@ -36,8 +36,8 @@ // Includes. #include "../DictObject.mqh" -#include "../SerializerConverter.mqh" -#include "../SerializerJson.mqh" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerJson.h" #include "Task.struct.h" #include "TaskObject.h" diff --git a/Task/TaskSetter.struct.h b/Task/TaskSetter.struct.h index b1759e927..8c8cf3bb4 100644 --- a/Task/TaskSetter.struct.h +++ b/Task/TaskSetter.struct.h @@ -31,6 +31,7 @@ // Includes. #include "../Data.struct.h" +#include "../Serializer/Serializer.h" #include "../Std.h" #include "Task.enum.h" diff --git a/Task/tests/TaskManager.test.mq5 b/Task/tests/TaskManager.test.mq5 index 58d371ecd..a8e4ad9c7 100644 --- a/Task/tests/TaskManager.test.mq5 +++ b/Task/tests/TaskManager.test.mq5 @@ -28,7 +28,10 @@ struct DataParamEntry; // Includes. +#include "../../Data.struct.serialize.h" #include "../../Test.mqh" +#include "../TaskAction.struct.serialize.h" +#include "../TaskCondition.struct.serialize.h" #include "../TaskManager.h" // Define test classes. diff --git a/Terminal.enum.h b/Terminal.enum.h index 7cc7e5b47..c1df1d2c2 100644 --- a/Terminal.enum.h +++ b/Terminal.enum.h @@ -31,7 +31,7 @@ #endif // Includes. -#include "Indicator.define.h" +#include "Indicator/Indicator.define.h" // Defines user error enumeration. enum ENUM_USER_ERR { USER_ERR_INVALID_ARGUMENT }; diff --git a/Test.mqh b/Test.mqh index 9869a50ed..83bb03664 100644 --- a/Test.mqh +++ b/Test.mqh @@ -46,6 +46,13 @@ return (ret); \ } +#define assertEqualOrExit(current, expected, msg) \ + if ((current) != (expected)) { \ + Alert(msg + " - Assert fail. Expected ", expected, ", but got ", current, \ + " in " + __FILE__ + ":" + (string)__LINE__); \ + ExpertRemove(); \ + } + #define assertFalseOrFail(cond, msg) \ if ((cond)) { \ Alert(msg + " - Assert fail on " + #cond + " in " + __FILE__ + ":" + (string)__LINE__); \ diff --git a/Tests.mqh b/Tests.mqh index 3823fb496..212333e9e 100644 --- a/Tests.mqh +++ b/Tests.mqh @@ -21,7 +21,7 @@ */ // Includes. -#include "Indicator.mqh" +#include "Indicator/Indicator.h" #include "Market.mqh" /** diff --git a/Tick.struct.h b/Tick/Tick.struct.h similarity index 81% rename from Tick.struct.h rename to Tick/Tick.struct.h index ddc8a4f31..8cb0897ca 100644 --- a/Tick.struct.h +++ b/Tick/Tick.struct.h @@ -30,7 +30,7 @@ #endif // Includes. -#include "DateTime.extern.h" +#include "../DateTime.extern.h" #ifndef __MQL__ /** @@ -67,8 +67,21 @@ struct TickAB { */ template struct TickTAB : TickAB { - datetime time; // Time of the last prices update. + long time_ms; // Time of the last prices update. // Struct constructors. - TickTAB(datetime _time = 0, T _ask = 0, T _bid = 0) : time(_time), TickAB(_ask, _bid) {} - TickTAB(MqlTick &_tick) : time(_tick.time), TickAB(_tick) {} + TickTAB(long _time_ms = 0, T _ask = 0, T _bid = 0) : time_ms(_time_ms), TickAB(_ask, _bid) {} + TickTAB(MqlTick &_tick) : time_ms(_tick.time_msc), TickAB(_tick) {} + + /** + * Method used by ItemsHistory. + */ + long GetTimeMs() { return time_ms; } + + /** + * Method used by ItemsHistory. + */ + long GetLengthMs() { + // Ticks have length of 0ms, so next tick could be at least 1ms after the previous tick. + return 0; + } }; diff --git a/Tick/TickManager.h b/Tick/TickManager.h index d04f04546..ffad7e4cb 100644 --- a/Tick/TickManager.h +++ b/Tick/TickManager.h @@ -40,10 +40,7 @@ class TickManager : public BufferStruct { /** * Init code (called on constructor). */ - void Init() { - AddFlags(DICT_FLAG_FILL_HOLES_UNSORTED); - SetOverflowListener(TickManagerOverflowListener, 10); - } + void Init() { SetOverflowListener(BufferStructOverflowListener, 10); } public: /** @@ -73,22 +70,4 @@ class TickManager : public BufferStruct { // template // void Set(STRUCT_ENUM(TickManagerParams, ENUM_TSM_PARAMS_PROP) _prop, T _value) // { params.Set(_prop, _value); } - - /* Other methods */ - - /** - * Function should return true if resize can be made, or false to overwrite current slot. - */ - static bool TickManagerOverflowListener(ENUM_DICT_OVERFLOW_REASON _reason, int _size, int _num_conflicts) { - switch (_reason) { - case DICT_OVERFLOW_REASON_FULL: - // We allow resize if dictionary size is less than 86400 slots. - return _size < 86400; - case DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS: - default: - // When there is too many conflicts, we just reject doing resize, so first conflicting slot will be reused. - break; - } - return false; - } }; diff --git a/Trade.mqh b/Trade.mqh index 151063147..bb6fc778f 100644 --- a/Trade.mqh +++ b/Trade.mqh @@ -34,7 +34,7 @@ class Trade; #include "Chart.mqh" #include "Convert.mqh" #include "DictStruct.mqh" -#include "IndicatorData.mqh" +#include "Indicator/IndicatorData.h" #include "Math.h" #include "Object.mqh" #include "Order.mqh" @@ -84,6 +84,8 @@ class Trade : public Taskable { Trade(TradeParams &_tparams, IndicatorBase *_indi_candle) : indi_candle(_indi_candle), tparams(_tparams), order_last(NULL) { Init(); + SetName(); + OrdersLoadByMagic(tparams.magic_no); }; /** @@ -951,7 +953,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. ENUM_ORDER_REASON_CLOSE _reason = ORDER_REASON_CLOSED_UNKNOWN, string _comment = "") { int _oid = 0, _closed = 0; Ref _order; - _comment = _comment != "" ? _comment : "TOCVP:"; + _comment = _comment != "" ? _comment : __FUNCTION__; for (DictStructIterator> iter = orders_active.Begin(); iter.IsValid(); ++iter) { _order = iter.Value(); if (_order.Ptr().IsOpen(true)) { @@ -962,19 +964,14 @@ HistorySelect(0, TimeCurrent()); // Select history for access. OrderMoveToHistory(_order.Ptr()); order_last = _order; } else { - logger.Error( - StringFormat("Failed to close the order: %d! Error: %d (%s)", _order.Ptr().Get(ORDER_PROP_TICKET), - _order.Ptr().Get(ORDER_PROP_LAST_ERROR), - Terminal::GetErrorText(_order.Ptr().Get(ORDER_PROP_LAST_ERROR))), - __FUNCTION_LINE__); - continue; + logger.AddLastError(__FUNCTION_LINE__, _order.Ptr().Get(ORDER_PROP_LAST_ERROR)); + return -1; } } } else { OrderMoveToHistory(_order.Ptr()); } } - logger.Flush(); return _closed; } @@ -992,7 +989,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. ENUM_ORDER_REASON_CLOSE _reason = ORDER_REASON_CLOSED_UNKNOWN, string _comment = "") { int _oid = 0, _closed = 0; Ref _order; - _comment = _comment != "" ? _comment : "TOCVP2:"; + _comment = _comment != "" ? _comment : __FUNCTION__; for (DictStructIterator> iter = orders_active.Begin(); iter.IsValid(); ++iter) { _order = iter.Value(); if (_order.Ptr().IsOpen(true)) { @@ -1001,15 +998,14 @@ HistorySelect(0, TimeCurrent()); // Select history for access. Math::Compare(_order.Ptr().Get((E)_prop2), _value2, _op)) { if (!_order.Ptr().OrderClose(_reason, _comment)) { #ifndef __MQL4__ - // @fixme: GH-571 & GH-706. + // @fixme: GH-571. logger.Info(__FUNCTION_LINE__, _order.Ptr().ToString()); #endif - logger.Error( - StringFormat("Failed to close the order: %d! Error: %d (%s)", _order.Ptr().Get(ORDER_PROP_TICKET), - _order.Ptr().Get(ORDER_PROP_LAST_ERROR), - Terminal::GetErrorText(_order.Ptr().Get(ORDER_PROP_LAST_ERROR))), - __FUNCTION_LINE__); - continue; + // @fixme: GH-570. + // logger.AddLastError(__FUNCTION_LINE__, _order.Ptr().Get(ORDER_PROP_LAST_ERROR)); + logger.Warning("Issue with closing the order!", __FUNCTION_LINE__); + ResetLastError(); + return -1; } order_last = _order; _closed++; @@ -1018,7 +1014,6 @@ HistorySelect(0, TimeCurrent()); // Select history for access. OrderMoveToHistory(_order.Ptr()); } } - logger.Flush(); return _closed; } @@ -1989,8 +1984,6 @@ HistorySelect(0, TimeCurrent()); // Select history for access. return _result; } - /* TaskActions */ - /** * Runs an action. */ diff --git a/Trade/TradeSignal.struct.h b/Trade/TradeSignal.struct.h index 7827b415c..41d864b34 100644 --- a/Trade/TradeSignal.struct.h +++ b/Trade/TradeSignal.struct.h @@ -27,8 +27,8 @@ // Includes. #include "../Chart.enum.h" -#include "../SerializerConverter.mqh" -#include "../SerializerJson.mqh" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerJson.h" // Defines. #define SIGNAL_CLOSE_BUY_FILTER STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_FLAG_CLOSE_BUY_FILTER) diff --git a/Trade/TradeSignalManager.h b/Trade/TradeSignalManager.h index 0b644e677..84e89480c 100644 --- a/Trade/TradeSignalManager.h +++ b/Trade/TradeSignalManager.h @@ -202,10 +202,10 @@ class TradeSignalManager : Dynamic { static bool SignalOverflowCallback(ENUM_DICT_OVERFLOW_REASON _reason, int _size, int _num_conflicts) { static int cache_limit = 1000; switch (_reason) { - case DICT_OVERFLOW_REASON_FULL: + case DICT_LISTENER_FULL_CAN_RESIZE: // We allow resize if dictionary size is less than 86400 slots. return _size < cache_limit; - case DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS: + case DICT_LISTENER_CONFLICTS_CAN_OVERWRITE: default: // When there is too many conflicts, we just reject doing resize, so first conflicting slot will be reused. break; diff --git a/Util.h b/Util.h index 50950b11e..8e1f88f76 100644 --- a/Util.h +++ b/Util.h @@ -30,7 +30,7 @@ #endif // Includes. -#include "SerializerConversions.h" +#include "Serializer/SerializerConversions.h" /** * Utility methods. diff --git a/tests/3DTest.mq5 b/tests/3DTest.mq5 index 878a2986a..6b73372a9 100644 --- a/tests/3DTest.mq5 +++ b/tests/3DTest.mq5 @@ -43,7 +43,7 @@ #include "../BufferStruct.mqh" #include "../Chart.mqh" #include "../Platform.h" -#include "../Serializer.mqh" +#include "../Serializer/Serializer.h" #include "../Test.mqh" /** diff --git a/tests/BufferStructTest.mq5 b/tests/BufferStructTest.mq5 index 42b9e52a9..c68d6b4bc 100644 --- a/tests/BufferStructTest.mq5 +++ b/tests/BufferStructTest.mq5 @@ -28,8 +28,9 @@ #include "../BufferStruct.mqh" #include "../Data.define.h" #include "../Data.struct.h" -#include "../SerializerConverter.mqh" -#include "../SerializerJSON.mqh" +#include "../Data.struct.serialize.h" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerJSON.h" #include "../Std.h" #include "../Test.mqh" diff --git a/tests/CompileTest.mq5 b/tests/CompileTest.mq5 index 1e05b27b8..0f5153d1c 100644 --- a/tests/CompileTest.mq5 +++ b/tests/CompileTest.mq5 @@ -26,15 +26,13 @@ // 3D includes (MQL5 only). #ifdef __MQL5__ -/* @fixme +#include "../3D/Chart3D.h" #include "../3D/Cube.h" #include "../3D/Devices/MTDX/MTDXDevice.h" #include "../3D/Devices/MTDX/MTDXIndexBuffer.h" #include "../3D/Devices/MTDX/MTDXShader.h" #include "../3D/Devices/MTDX/MTDXVertexBuffer.h" #include "../3D/Frontends/MT5Frontend.h" -#include "../3D/Chart3D.h" -*/ #endif // Forward declaration. @@ -44,6 +42,7 @@ struct IndicatorParams; #include "../Account/AccountMt.h" #include "../Array.mqh" #include "../Task/TaskAction.h" +//#include "../BasicTrade.mqh" // @removeme #include "../Buffer.mqh" #include "../BufferFXT.mqh" #include "../BufferStruct.mqh" @@ -62,15 +61,9 @@ struct IndicatorParams; #include "../DrawIndicator.mqh" #include "../EA.mqh" #include "../File.mqh" -#include "../ISerializable.h" -#include "../Indicator.define.h" -#include "../Indicator.mqh" -#include "../IndicatorBase.h" -#include "../IndicatorData.mqh" // #include "../Inet.mqh" #include "../Log.mqh" #include "../MD5.mqh" -#include "../Storage/Collection.mqh" #include "../Storage/IValueStorage.h" #include "../Task/TaskCondition.h" //#include "../MQL4.mqh" // @removeme @@ -93,18 +86,7 @@ struct IndicatorParams; #include "../Report.mqh" #include "../Storage/Objects.h" #include "../Storage/ObjectsCache.h" -#include "../Serializer.mqh" -#include "../SerializerBinary.mqh" -#include "../SerializerConversions.h" -#include "../SerializerConverter.mqh" -#include "../SerializerCsv.mqh" -#include "../SerializerDict.mqh" -#include "../SerializerJson.mqh" -#include "../SerializerNode.mqh" -#include "../SerializerNodeIterator.mqh" -#include "../SerializerNodeParam.mqh" -#include "../SerializerObject.mqh" -#include "../SerializerSqlite.mqh" +// #include "../SVG.mqh" // @removeme #include "../Session.mqh" #include "../SetFile.mqh" #include "../Socket.mqh" @@ -124,17 +106,41 @@ struct IndicatorParams; #include "../Task/Taskable.h" #include "../Terminal.mqh" // #include "../Tester.mqh" // @removeme +#include "../Storage/Collection.mqh" #include "../Storage/ValueStorage.h" -#include "../Tests.mqh" // #include "../Tests.mqh" // @removeme #include "../Timer.mqh" #include "../Trade.mqh" #include "../Util.h" #include "../Web.mqh" -// Includes indicator files. +// Includes Indicator files. +#include "../Indicator/Indicator.define.h" +#include "../Indicator/Indicator.h" +#include "../Indicator/IndicatorBase.h" +#include "../Indicator/IndicatorData.h" +#include "../Indicator/IndicatorCandle.h" +#include "../Indicator/IndicatorRenko.h" +#include "../Indicator/IndicatorTf.h" +#include "../Indicator/IndicatorTick.h" +#include "../Indicator/IndicatorTickSource.h" #include "../Indicators/indicators.h" +// Includes Serializer files. +#include "../Serializer/Serializable.h" +#include "../Serializer/Serializer.h" +#include "../Serializer/SerializerBinary.h" +#include "../Serializer/SerializerConversions.h" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerCsv.h" +#include "../Serializer/SerializerDict.h" +#include "../Serializer/SerializerJson.h" +#include "../Serializer/SerializerNode.h" +#include "../Serializer/SerializerNodeIterator.h" +#include "../Serializer/SerializerNodeParam.h" +#include "../Serializer/SerializerObject.h" +#include "../Serializer/SerializerSqlite.h" + /** * Implements Init event handler. */ diff --git a/tests/ConfigTest.mq5 b/tests/ConfigTest.mq5 index 71e3b0fdf..7c62bb7f4 100644 --- a/tests/ConfigTest.mq5 +++ b/tests/ConfigTest.mq5 @@ -29,9 +29,9 @@ #include "../Data.define.h" #include "../Dict.mqh" #include "../DictObject.mqh" -#include "../SerializerConverter.mqh" -#include "../SerializerCsv.mqh" -#include "../SerializerJson.mqh" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerCsv.h" +#include "../Serializer/SerializerJson.h" #include "../Test.mqh" class Test { diff --git a/tests/DictTest.mq5 b/tests/DictTest.mq5 index c46c462ea..be4c19a2d 100644 --- a/tests/DictTest.mq5 +++ b/tests/DictTest.mq5 @@ -29,9 +29,9 @@ #include "../DictObject.mqh" #include "../DictStruct.mqh" #include "../Object.mqh" -#include "../Serializer.mqh" -#include "../SerializerConverter.mqh" -#include "../SerializerJson.mqh" +#include "../Serializer/Serializer.h" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerJson.h" #include "../Test.mqh" class DictTestClass { @@ -55,15 +55,18 @@ class DictTestClass { // Function should return true if resize can be made, or false to overwrite current slot. bool Dict14_OverflowListener(ENUM_DICT_OVERFLOW_REASON _reason, int _size, int _num_conflicts) { + static int cache_limit = 10; switch (_reason) { - case DICT_OVERFLOW_REASON_FULL: + case DICT_LISTENER_FULL_CAN_RESIZE: + case DICT_LISTENER_NOT_PERFORMANT_CAN_RESIZE: // We allow resize if dictionary size is less than 10 slots. - return _size < 10; - case DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS: - default: - // When there is too many conflicts, we just reject doing resize, so first conflicting slot will be reused. - return false; + return _size < cache_limit; + case DICT_LISTENER_CONFLICTS_CAN_OVERWRITE: + // We start to overwrite slots when we can't make dict bigger and there is at least 10 consecutive conflicts while + // inserting new value. + return _size >= cache_limit && _num_conflicts >= 10; } + return true; } /** diff --git a/tests/DrawIndicatorTest.mq5 b/tests/DrawIndicatorTest.mq5 index cccedb21c..d12c26762 100644 --- a/tests/DrawIndicatorTest.mq5 +++ b/tests/DrawIndicatorTest.mq5 @@ -27,7 +27,7 @@ // Includes. #include "../DictStruct.mqh" #include "../DrawIndicator.mqh" -#include "../Indicator.struct.serialize.h" +#include "../Indicator/Indicator.struct.serialize.h" #include "../Indicators/Indi_Bands.mqh" #include "../Indicators/Indi_Demo.mqh" #include "../Indicators/Indi_MA.mqh" @@ -67,7 +67,7 @@ void OnTick() { for (DictIterator> iter = Platform::GetIndicators() PTR_DEREF Begin(); iter.IsValid(); ++iter) { IndicatorData *_indi = iter.Value().Ptr(); - _indi.OnTick(); + _indi.OnTick(Platform::GetGlobalTickIndex()); IndicatorDataEntry _entry = _indi.GetEntry(); if (_indi.Get(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_READY)) && _entry.IsValid()) { PrintFormat("%s: bar %d: %s", _indi.GetName(), bar_processed, _indi.ToString()); diff --git a/tests/IndicatorsTest.mq5 b/tests/IndicatorsTest.mq5 index ff2158e82..317ef3b8c 100644 --- a/tests/IndicatorsTest.mq5 +++ b/tests/IndicatorsTest.mq5 @@ -27,6 +27,7 @@ // Defines. // #define __debug__ // Enables debug. // #define __debug_verbose__ +// #define __debug_items_history__ // Forward declaration. struct DataParamEntry; @@ -35,14 +36,14 @@ struct DataParamEntry; //#include "../ChartMt.h" #include "../Dict.mqh" #include "../DictObject.mqh" -#include "../Indicator.mqh" +#include "../Indicator/Indicator.h" #include "../Indicator/tests/classes/IndicatorTfDummy.h" #include "../Indicators/Bitwise/indicators.h" #include "../Indicators/Tick/Indi_TickMt.mqh" #include "../Indicators/indicators.h" #include "../Platform.h" -#include "../SerializerConverter.mqh" -#include "../SerializerJson.mqh" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerJson.h" #include "../Std.h" #include "../Test.mqh" @@ -82,7 +83,7 @@ int OnInit() { ResetLastError(); // Print indicator values. - _result &= PrintIndicators(__FUNCTION__); + //_result &= PrintIndicators(__FUNCTION__); assertEqualOrFail(_LastError, ERR_NO_ERROR, StringFormat("Error: %d", GetLastError())); ResetLastError(); @@ -97,9 +98,9 @@ void OnTick() { IndicatorData* _candles = Platform::FetchDefaultCandleIndicator(_Symbol, PERIOD_CURRENT); if (_candles PTR_DEREF IsNewBar()) { - if (_candles PTR_DEREF GetBarIndex() > 200) { + if (_candles PTR_DEREF GetBarIndex() > 550) { ExpertRemove(); - } + } if (indis.Size() == 0) { return; @@ -120,6 +121,9 @@ void OnTick() { IndicatorData* _indi = iter.Value().Ptr(); IndicatorDataEntry _entry(_indi PTR_DEREF GetEntry()); + // if (_indi.GetType() != INDI_AMA) + // continue; + if (_indi PTR_DEREF Get(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_READY))) { if (_entry.IsValid()) { PrintFormat("%s: bar %d: %s", _indi PTR_DEREF GetFullName(), _candles PTR_DEREF GetBars(), diff --git a/tests/LogTest.mq5 b/tests/LogTest.mq5 index 584468c30..0b7d3cb1e 100644 --- a/tests/LogTest.mq5 +++ b/tests/LogTest.mq5 @@ -27,6 +27,7 @@ // Includes. #include "../DictStruct.mqh" #include "../Log.mqh" +#include "../Refs.struct.h" #include "../Test.mqh" // Variables. diff --git a/tests/OrderTest.mq5 b/tests/OrderTest.mq5 index a8a25bfab..90e383e7e 100644 --- a/tests/OrderTest.mq5 +++ b/tests/OrderTest.mq5 @@ -28,8 +28,8 @@ #include "../Chart.mqh" #include "../Order.mqh" #include "../Platform.h" -#include "../SerializerConverter.mqh" -#include "../SerializerJson.mqh" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerJson.h" #include "../Test.mqh" // Global defines. diff --git a/tests/ValueStorageTest.mq5 b/tests/ValueStorageTest.mq5 index 2230fa6c6..6ae29378d 100644 --- a/tests/ValueStorageTest.mq5 +++ b/tests/ValueStorageTest.mq5 @@ -30,8 +30,8 @@ // Includes. #include "../Indicators/Indi_MA.mqh" #include "../Indicators/Price/Indi_Price.mqh" -#include "../SerializerConverter.mqh" -#include "../SerializerJson.mqh" +#include "../Serializer/SerializerConverter.h" +#include "../Serializer/SerializerJson.h" #include "../Storage/ValueStorage.h" #include "../Test.mqh"