From 00001304f0216660b8d793c0ab06c5bb7126bfdb Mon Sep 17 00:00:00 2001 From: Adrian Kierzkowski Date: Fri, 22 Mar 2024 20:21:07 +0100 Subject: [PATCH 1/2] Refs EA31337/EA31337-indicators-stats#2. Closed to finish Indi_AccountStats indicator. --- Account/Account.h | 1 + Account/AccountBase.h | 40 ++++ Account/AccountMt.h | 15 +- Indicator.enum.h | 25 ++- Indicator/IndicatorCandle.h | 28 ++- Indicator/IndicatorTick.h | 2 +- Indicator/tests/classes/IndicatorTfDummy.h | 12 +- Indicator/tests/classes/IndicatorTickDummy.h | 16 +- IndicatorData.mqh | 10 +- Indicators/Account/Indi_AccountStats.mqh | 207 ++++++++++++++++++ .../Account/tests/Indi_AccountStats.test.mq4 | 27 +++ .../Account/tests/Indi_AccountStats.test.mq5 | 61 ++++++ Indicators/Indi_AMA.mqh | 8 - Indicators/Tick/Indi_TickMt.mqh | 6 +- Storage/IValueStorage.h | 5 + Storage/ValueStorage.h | 9 + Storage/ValueStorage.native.h | 19 +- 17 files changed, 444 insertions(+), 47 deletions(-) create mode 100644 Indicators/Account/Indi_AccountStats.mqh create mode 100644 Indicators/Account/tests/Indi_AccountStats.test.mq4 create mode 100644 Indicators/Account/tests/Indi_AccountStats.test.mq5 diff --git a/Account/Account.h b/Account/Account.h index 5e50510d5..a42e5822c 100644 --- a/Account/Account.h +++ b/Account/Account.h @@ -56,4 +56,5 @@ class Account : public AccountBase { */ ~Account() {} }; + #endif // ACCOUNT_H diff --git a/Account/AccountBase.h b/Account/AccountBase.h index 9d3ae7253..187649570 100644 --- a/Account/AccountBase.h +++ b/Account/AccountBase.h @@ -56,6 +56,46 @@ class AccountBase : public Dynamic { * Class deconstructor. */ ~AccountBase() {} + + /** + * Returns balance value of the current account. + */ + virtual datetime GetDateTime() { return TimeCurrent(); }; + + /** + * Returns balance value of the current account. + */ + virtual float GetBalance() = 0; + + /** + * Returns credit value of the current account. + */ + virtual float GetCredit() = 0; + + /** + * Returns profit value of the current account. + */ + virtual float GetProfit() = 0; + + /** + * Returns equity value of the current account. + */ + virtual float GetEquity() = 0; + + /** + * Returns margin value of the current account. + */ + virtual float GetMarginUsed() = 0; + + /** + * Returns free margin value of the current account. + */ + virtual float GetMarginFree() = 0; + + /** + * Get account available margin. + */ + virtual float GetMarginAvail() = 0; }; #endif // ACCOUNTBASE_H diff --git a/Account/AccountMt.h b/Account/AccountMt.h index c03c300f9..66f053591 100644 --- a/Account/AccountMt.h +++ b/Account/AccountMt.h @@ -42,12 +42,13 @@ class AccountMt; #include "Account.define.h" #include "Account.enum.h" #include "Account.extern.h" +#include "Account.h" #include "Account.struct.h" /** * Class to provide functions that return parameters of the current account. */ -class AccountMt { +class AccountMt : public AccountBase { protected: // Struct variables. BufferStruct entries; @@ -136,7 +137,7 @@ class AccountMt { * Returns balance value of the current account. */ static double AccountBalance() { return AccountInfoDouble(ACCOUNT_BALANCE); } - float GetBalance() { + float GetBalance() override { // @todo: Adds caching. // return UpdateStats(ACC_BALANCE, AccountBalance()); return (float)AccountMt::AccountBalance(); @@ -146,7 +147,7 @@ class AccountMt { * Returns credit value of the current account. */ static double AccountCredit() { return AccountInfoDouble(ACCOUNT_CREDIT); } - float GetCredit() { + float GetCredit() override { // @todo: Adds caching. // return UpdateStats(ACC_CREDIT, AccountCredit()); return (float)AccountMt::AccountCredit(); @@ -156,7 +157,7 @@ class AccountMt { * Returns profit value of the current account. */ static double AccountProfit() { return AccountInfoDouble(ACCOUNT_PROFIT); } - float GetProfit() { + float GetProfit() override { // @todo: Adds caching. // return UpdateStats(ACC_PROFIT, AccountProfit()); return (float)AccountMt::AccountProfit(); @@ -166,7 +167,7 @@ class AccountMt { * Returns equity value of the current account. */ static double AccountEquity() { return AccountInfoDouble(ACCOUNT_EQUITY); } - float GetEquity() { + float GetEquity() override { // @todo: Adds caching. // return UpdateStats(ACC_EQUITY, AccountEquity()); return (float)AccountMt::AccountEquity(); @@ -198,7 +199,7 @@ class AccountMt { * Returns free margin value of the current account. */ static double AccountFreeMargin() { return AccountInfoDouble(ACCOUNT_MARGIN_FREE); } - float GetMarginFree() { + float GetMarginFree() override { // @todo: Adds caching. // return UpdateStats(ACC_MARGIN_FREE, AccountFreeMargin()); return (float)AccountMt::AccountFreeMargin(); @@ -267,7 +268,7 @@ class AccountMt { * Get account available margin. */ static double AccountAvailMargin() { return fmin(AccountFreeMargin(), AccountTotalBalance()); } - float GetMarginAvail() { return (float)AccountAvailMargin(); } + float GetMarginAvail() override { return (float)AccountAvailMargin(); } /** * Returns the calculation mode of free margin allowed to open orders on the current account. diff --git a/Indicator.enum.h b/Indicator.enum.h index b2b07a3a4..c20b4e8b6 100644 --- a/Indicator.enum.h +++ b/Indicator.enum.h @@ -44,6 +44,7 @@ enum ENUM_INDICATOR_ACTION { enum ENUM_INDICATOR_TYPE { INDI_NONE = 0, // (None) INDI_AC, // Accelerator Oscillator + INDI_ACCOUNT_STATS, // Account Stats INDI_AD, // Accumulation/Distribution INDI_ADX, // Average Directional Index INDI_ADXW, // ADX by Welles Wilder @@ -237,9 +238,24 @@ enum ENUM_INDI_VS_TYPE { 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 + 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) + // Indicator flags. enum ENUM_INDI_FLAGS { INDI_FLAG_INDEXABLE_BY_SHIFT, // Indicator supports indexation by shift. @@ -269,4 +285,11 @@ enum ENUM_INDI_DS_MODE_KIND { INDI_DS_MODE_KIND_AP, // Mode is a value from ENUM_APPLIED_PRICE enumeration. It is used to retrieve value storage // based on ENUM_INDI_VS_TYPE enumeration, e.g., PRICE_OPEN becomes ENUM_INDI_VS_PRICE_OPEN. }; + +// Type of entry +enum ENUM_INDI_EMITTED_ENTRY_TYPE { + INDI_EMITTED_ENTRY_TYPE_PARENT, // Undetermined type of entry from direct parent indicator. + INDI_EMITTED_ENTRY_TYPE_TICK, + INDI_EMITTED_ENTRY_TYPE_CANDLE, +}; //+------------------------------------------------------------------+ diff --git a/Indicator/IndicatorCandle.h b/Indicator/IndicatorCandle.h index 8221afe82..da34844ca 100644 --- a/Indicator/IndicatorCandle.h +++ b/Indicator/IndicatorCandle.h @@ -32,6 +32,7 @@ // Includes. #include "../Buffer/BufferCandle.h" #include "../Candle.struct.h" +#include "../Indicator.enum.h" #include "../Indicator.mqh" #include "../Storage/ValueStorage.price_median.h" #include "../Storage/ValueStorage.price_typical.h" @@ -310,7 +311,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); + EmitEntry(_entry, INDI_EMITTED_ENTRY_TYPE_CANDLE); } } @@ -342,7 +343,7 @@ class IndicatorCandle : public Indicator { /** * Adds tick's price to the matching candle and updates its OHLC values. */ - void UpdateCandle(long _tick_timestamp, double _price) { + CandleOCTOHLC UpdateCandle(long _tick_timestamp, double _price) { long _candle_timestamp = CalcCandleTimestamp(_tick_timestamp); #ifdef __debug_verbose__ @@ -368,6 +369,8 @@ class IndicatorCandle : public Indicator { } icdata.Add(_candle, _candle_timestamp); + + return _candle; } /** @@ -380,12 +383,25 @@ class IndicatorCandle : public Indicator { /** * Called when data source emits new entry (historic or future one). */ - void OnDataSourceEntry(IndicatorDataEntry& entry) override { - // Updating candle from bid price. - UpdateCandle(entry.timestamp, entry[1]); + 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(CalcCandleTimestamp(entry.timestamp)); + 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); }; /** diff --git a/Indicator/IndicatorTick.h b/Indicator/IndicatorTick.h index 5438923cb..cf01dfe83 100644 --- a/Indicator/IndicatorTick.h +++ b/Indicator/IndicatorTick.h @@ -164,7 +164,7 @@ class IndicatorTick : public Indicator { void EmitHistory() override { for (DictStructIterator> iter(itdata.Begin()); iter.IsValid(); ++iter) { IndicatorDataEntry _entry = TickToEntry(iter.Key(), iter.Value()); - EmitEntry(_entry); + EmitEntry(_entry, INDI_EMITTED_ENTRY_TYPE_TICK); } } diff --git a/Indicator/tests/classes/IndicatorTfDummy.h b/Indicator/tests/classes/IndicatorTfDummy.h index 019b800d8..f49a8161d 100644 --- a/Indicator/tests/classes/IndicatorTfDummy.h +++ b/Indicator/tests/classes/IndicatorTfDummy.h @@ -48,11 +48,13 @@ class IndicatorTfDummy : public IndicatorTf { string GetName() override { return "IndicatorTfDummy(" + IntegerToString(iparams.spc) + ")"; } - 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); + 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; + } #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 dfd59e9a3..3e6bab0ab 100644 --- a/Indicator/tests/classes/IndicatorTickDummy.h +++ b/Indicator/tests/classes/IndicatorTickDummy.h @@ -59,13 +59,13 @@ class IndicatorTickDummy : public IndicatorTick _t7(4.2f, 4.21f); TickAB _t8(4.8f, 4.81f); - EmitEntry(TickToEntry(1000, _t1)); - EmitEntry(TickToEntry(1500, _t2)); - EmitEntry(TickToEntry(2000, _t3)); - EmitEntry(TickToEntry(3000, _t4)); - EmitEntry(TickToEntry(4000, _t5)); - EmitEntry(TickToEntry(4100, _t6)); - EmitEntry(TickToEntry(4200, _t7)); - EmitEntry(TickToEntry(4800, _t8)); + EmitEntry(TickToEntry(1000, _t1), INDI_EMITTED_ENTRY_TYPE_TICK); + EmitEntry(TickToEntry(1500, _t2), INDI_EMITTED_ENTRY_TYPE_TICK); + EmitEntry(TickToEntry(2000, _t3), INDI_EMITTED_ENTRY_TYPE_TICK); + EmitEntry(TickToEntry(3000, _t4), INDI_EMITTED_ENTRY_TYPE_TICK); + EmitEntry(TickToEntry(4000, _t5), INDI_EMITTED_ENTRY_TYPE_TICK); + EmitEntry(TickToEntry(4100, _t6), INDI_EMITTED_ENTRY_TYPE_TICK); + EmitEntry(TickToEntry(4200, _t7), INDI_EMITTED_ENTRY_TYPE_TICK); + EmitEntry(TickToEntry(4800, _t8), INDI_EMITTED_ENTRY_TYPE_TICK); }; }; diff --git a/IndicatorData.mqh b/IndicatorData.mqh index 46225b8d3..bc0c498b1 100644 --- a/IndicatorData.mqh +++ b/IndicatorData.mqh @@ -1616,10 +1616,10 @@ class IndicatorData : public IndicatorBase { /** * Sends entry to listening indicators. */ - void EmitEntry(IndicatorDataEntry& entry) { + void EmitEntry(IndicatorDataEntry& entry, ENUM_INDI_EMITTED_ENTRY_TYPE type = INDI_EMITTED_ENTRY_TYPE_PARENT) { for (int i = 0; i < ArraySize(listeners); ++i) { if (listeners[i].ObjectExists()) { - listeners[i].Ptr().OnDataSourceEntry(entry); + listeners[i].Ptr().OnDataSourceEntry(entry, type); } } } @@ -1683,7 +1683,11 @@ class IndicatorData : public IndicatorBase { /** * Called when data source emits new entry (historic or future one). */ - virtual void OnDataSourceEntry(IndicatorDataEntry& entry){}; + 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 OnTick() {} diff --git a/Indicators/Account/Indi_AccountStats.mqh b/Indicators/Account/Indi_AccountStats.mqh new file mode 100644 index 000000000..da45fef7d --- /dev/null +++ b/Indicators/Account/Indi_AccountStats.mqh @@ -0,0 +1,207 @@ +//+------------------------------------------------------------------+ +//| 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 . + * + */ + +// Includes. +#include "../../Account/AccountBase.h" +#include "../../BufferStruct.mqh" +#include "../../Indicator.mqh" +#include "../../Platform.h" +#include "../../Storage/Objects.h" + +// Structs. +struct Indi_AccountStats_Params : IndicatorParams { + // Applied price. + ENUM_APPLIED_PRICE ap; + + // Account to use. + Ref account; + + // Struct constructor. + Indi_AccountStats_Params(AccountBase *_account = nullptr, int _shift = 0) + : IndicatorParams(INDI_ACCOUNT_STATS), account(_account) { + SetShift(_shift); + }; + Indi_AccountStats_Params(Indi_AccountStats_Params &_params) { THIS_REF = _params; }; + + // Getters. + AccountBase *GetAccount() { return account.Ptr(); } + ENUM_APPLIED_PRICE GetAppliedPrice() override { return ap; } + + // Setters. + void SetAccount(AccountBase *_account) { account = _account; } + void SetAppliedPrice(ENUM_APPLIED_PRICE _ap) { ap = _ap; } +}; + +/** + * Price Indicator. + */ +class Indi_AccountStats : public Indicator { + Ref> buffer_date_time; + Ref> buffer_balance; + Ref> buffer_credit; + Ref> buffer_equity; + Ref> buffer_profit; + Ref> buffer_margin_used; + Ref> buffer_margin_free; + Ref> buffer_margin_avail; + + public: + /** + * Class constructor. + */ + Indi_AccountStats(Indi_AccountStats_Params &_p, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN, + IndicatorData *_indi_src = NULL, int _indi_src_mode = 0) + : Indicator(_p, + IndicatorDataParams::GetInstance(INDI_VS_TYPE_ACCOUNT_STATS_BUFFERS_COUNT, TYPE_DOUBLE, _idstype, + IDATA_RANGE_PRICE, _indi_src_mode), + _indi_src) { + InitAccountStats(); + }; + Indi_AccountStats(int _shift = 0, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN, IndicatorData *_indi_src = NULL, + int _indi_src_mode = 0) + : Indicator(Indi_AccountStats_Params(), + IndicatorDataParams::GetInstance(INDI_VS_TYPE_ACCOUNT_STATS_BUFFERS_COUNT, TYPE_DOUBLE, _idstype, + IDATA_RANGE_PRICE, _indi_src_mode), + _indi_src) { + InitAccountStats(); + }; + void InitAccountStats() { + buffer_date_time = new NativeValueStorage(); + buffer_balance = new NativeValueStorage(); + buffer_credit = new NativeValueStorage(); + buffer_equity = new NativeValueStorage(); + buffer_profit = new NativeValueStorage(); + buffer_margin_used = new NativeValueStorage(); + buffer_margin_free = new NativeValueStorage(); + buffer_margin_avail = new NativeValueStorage(); + } + + /** + * Returns possible data source types. It is a bit mask of ENUM_INDI_SUITABLE_DS_TYPE. + */ + virtual unsigned int GetSuitableDataSourceTypes() { + // We require that candle indicator is attached. + return INDI_SUITABLE_DS_TYPE_CANDLE; + } + + /** + * Returns possible data source modes. It is a bit mask of ENUM_IDATA_SOURCE_TYPE. + */ + unsigned int GetPossibleDataModes() override { return IDATA_BUILTIN; } + + /** + * Checks whether indicator has a valid value for a given shift. + */ + virtual bool HasValidEntry(int _shift = 0) { return GetBarTime(_shift) != 0; } + + /** + * Returns the indicator's value. + */ + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + + // Converting mode into value storage type. + ENUM_INDI_VS_TYPE _vs_type = (ENUM_INDI_VS_TYPE)(INDI_VS_TYPE_ACCOUNT_STATS_INDEX_FIRST + _mode); + + // Retrieving data from specific value storage. + switch (_vs_type) { + case INDI_VS_TYPE_ACCOUNT_STATS_DATE_TIME: + return ((ValueStorage *)GetSpecificValueStorage(_vs_type))PTR_DEREF FetchSeries(_ishift); + case INDI_VS_TYPE_ACCOUNT_STATS_BALANCE: + case INDI_VS_TYPE_ACCOUNT_STATS_CREDIT: + case INDI_VS_TYPE_ACCOUNT_STATS_EQUITY: + case INDI_VS_TYPE_ACCOUNT_STATS_PROFIT: + case INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_USED: + case INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_FREE: + case INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_AVAIL: + return ((ValueStorage *)GetSpecificValueStorage(_vs_type))PTR_DEREF FetchSeries(_ishift); + default: + Alert("Error: Indi_AccountStats: Invalid mode passed to GetEntryValue()!"); + DebugBreak(); + return EMPTY_VALUE; + } + } + + /** + * Returns value storage of given kind. + */ + IValueStorage *GetSpecificValueStorage(ENUM_INDI_VS_TYPE _type) override { + // Returning Price indicator which provides applied price in the only buffer #0. + switch (_type) { + case INDI_VS_TYPE_ACCOUNT_STATS_DATE_TIME: + return buffer_date_time.Ptr(); + case INDI_VS_TYPE_ACCOUNT_STATS_BALANCE: + return buffer_balance.Ptr(); + case INDI_VS_TYPE_ACCOUNT_STATS_CREDIT: + return buffer_credit.Ptr(); + case INDI_VS_TYPE_ACCOUNT_STATS_EQUITY: + return buffer_equity.Ptr(); + case INDI_VS_TYPE_ACCOUNT_STATS_PROFIT: + return buffer_profit.Ptr(); + case INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_USED: + return buffer_margin_used.Ptr(); + case INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_FREE: + return buffer_margin_free.Ptr(); + case INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_AVAIL: + return buffer_margin_avail.Ptr(); + default: + // Trying in parent class. + return Indicator::GetSpecificValueStorage(_type); + } + } + + /** + * Checks whether indicator support given value storage type. + */ + bool HasSpecificValueStorage(ENUM_INDI_VS_TYPE _type) override { + switch (_type) { + case INDI_VS_TYPE_ACCOUNT_STATS_DATE_TIME: + case INDI_VS_TYPE_ACCOUNT_STATS_BALANCE: + case INDI_VS_TYPE_ACCOUNT_STATS_CREDIT: + case INDI_VS_TYPE_ACCOUNT_STATS_EQUITY: + case INDI_VS_TYPE_ACCOUNT_STATS_PROFIT: + case INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_USED: + case INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_FREE: + case INDI_VS_TYPE_ACCOUNT_STATS_MARGIN_AVAIL: + return true; + default: + // Trying in parent class. + return Indicator::HasSpecificValueStorage(_type); + } + } + + /** + * 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) { + Indicator::OnDataSourceEntry(entry, type); + + if (type != INDI_EMITTED_ENTRY_TYPE_CANDLE) { + return; + } + + // Adding new account stats entry. + + Print("New candle: ", entry.ToString()); + } +}; diff --git a/Indicators/Account/tests/Indi_AccountStats.test.mq4 b/Indicators/Account/tests/Indi_AccountStats.test.mq4 new file mode 100644 index 000000000..8356c50b5 --- /dev/null +++ b/Indicators/Account/tests/Indi_AccountStats.test.mq4 @@ -0,0 +1,27 @@ +//+------------------------------------------------------------------+ +//| 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 Indi_AC indicator class. + */ + +#include "Indi_AC.test.mq5" diff --git a/Indicators/Account/tests/Indi_AccountStats.test.mq5 b/Indicators/Account/tests/Indi_AccountStats.test.mq5 new file mode 100644 index 000000000..c81e0da79 --- /dev/null +++ b/Indicators/Account/tests/Indi_AccountStats.test.mq5 @@ -0,0 +1,61 @@ +//+------------------------------------------------------------------+ +//| 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 . + */ + +// Includes. +#include "../../../Account/AccountMt.h" +#include "../../../Platform.h" +#include "../../../Test.mqh" +#include "../Indi_AccountStats.mqh" + +/** + * @file + * Test functionality of Indi_AccountStats indicator class. + */ + +Ref indi_account_mt; + +int OnInit() { + Ref account_mt = new AccountMt(); + Indi_AccountStats_Params indi_params(account_mt.Ptr()); + indi_account_mt = new Indi_AccountStats(indi_params); + + Platform::Init(); + + Platform::AddWithDefaultBindings(indi_account_mt.Ptr()); + + bool _result = true; + assertTrueOrFail(indi_account_mt REF_DEREF IsValid(), "Error on IsValid!"); + return (_result && _LastError == ERR_NO_ERROR ? INIT_SUCCEEDED : INIT_FAILED); +} + +void OnTick() { + Platform::Tick(); + if (Platform::IsNewHour()) { + IndicatorDataEntry _entry = indi_account_mt REF_DEREF GetEntry(); + bool _is_ready = indi_account_mt REF_DEREF Get(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_READY)); + bool _is_valid = _entry.IsValid(); + Print(indi_account_mt REF_DEREF ToString(), _is_ready ? "" : " (Not yet ready)"); + if (_is_ready && !_is_valid) { + Print(indi_account_mt REF_DEREF ToString(), " (Invalid entry!)"); + assertTrueOrExit(_entry.IsValid(), "Invalid entry!"); + } + } +} \ No newline at end of file diff --git a/Indicators/Indi_AMA.mqh b/Indicators/Indi_AMA.mqh index ec615c8ac..4fde34ef7 100644 --- a/Indicators/Indi_AMA.mqh +++ b/Indicators/Indi_AMA.mqh @@ -256,14 +256,6 @@ class Indi_AMA : public Indicator { return _value; } - /** - * Called when data source emits new entry (historic or future one). - */ - void OnDataSourceEntry(IndicatorDataEntry &entry) override { - // Just to be able to make a breakpoint here. - int x = 4; - }; - /** * Called if data source is requested, but wasn't yet set. May be used to initialize indicators that must operate on * some data source. diff --git a/Indicators/Tick/Indi_TickMt.mqh b/Indicators/Tick/Indi_TickMt.mqh index 726004fff..b60ee2d04 100644 --- a/Indicators/Tick/Indi_TickMt.mqh +++ b/Indicators/Tick/Indi_TickMt.mqh @@ -159,7 +159,7 @@ class Indi_TickMt : public IndicatorTick { _tmp_ticks[i].ask, ", ", _tmp_ticks[i].bid); #endif - EmitEntry(TickToEntry(_tmp_ticks[i].time, _tick)); + EmitEntry(TickToEntry(_tmp_ticks[i].time, _tick), INDI_EMITTED_ENTRY_TYPE_TICK); if (_num_yet_to_copy <= 0) { break; @@ -205,7 +205,7 @@ class Indi_TickMt : public IndicatorTick { // DebugBreak(); // Just emitting zeroes in case of error. TickAB _tick(0, 0); - EmitEntry(TickToEntry(TimeCurrent(), _tick)); + EmitEntry(TickToEntry(TimeCurrent(), _tick), INDI_EMITTED_ENTRY_TYPE_TICK); return; } @@ -224,6 +224,6 @@ class Indi_TickMt : public IndicatorTick { TickAB _tick(_ask, _bid); IndicatorDataEntry _entry(TickToEntry(_time, _tick)); StoreEntry(_entry); - EmitEntry(_entry); + EmitEntry(_entry, INDI_EMITTED_ENTRY_TYPE_TICK); } }; diff --git a/Storage/IValueStorage.h b/Storage/IValueStorage.h index 290c2552d..6f4897bf6 100644 --- a/Storage/IValueStorage.h +++ b/Storage/IValueStorage.h @@ -72,6 +72,11 @@ class IValueStorage : public Dynamic { DebugBreak(); return false; } + + /** + * Returns real array index for this given shift. + **/ + virtual int GetRealIndex(int _shift) { return IsSeries() ? (Size() - 1 - _shift) : _shift; } }; /** diff --git a/Storage/ValueStorage.h b/Storage/ValueStorage.h index 6c7979ba0..865b2d651 100644 --- a/Storage/ValueStorage.h +++ b/Storage/ValueStorage.h @@ -181,6 +181,15 @@ class ValueStorage : public IValueStorage { DebugBreak(); } + /** + * Inserts new value at the end of the buffer. If buffer works as As-Series, + * then new value will act as the one at index 0. + */ + virtual void Append(C _value) { + Alert(__FUNCSIG__, " does not implement Append()!"); + DebugBreak(); + } + /** * Sets buffer drawing attributes. Currently does nothing. */ diff --git a/Storage/ValueStorage.native.h b/Storage/ValueStorage.native.h index 46ca12565..0c6180881 100644 --- a/Storage/ValueStorage.native.h +++ b/Storage/ValueStorage.native.h @@ -60,19 +60,28 @@ class NativeValueStorage : public ValueStorage { * Fetches value from a given shift. Takes into consideration as-series flag. */ C Fetch(int _shift) override { - if (_shift < 0 || _shift >= ArraySize(_values)) { + if (_shift < 0 || _shift >= Size()) { return (C)EMPTY_VALUE; - // Print("Invalid buffer data index: ", _shift, ". Buffer size: ", ArraySize(_values)); - // DebugBreak(); } - return _values[_shift]; + int _index = GetRealIndex(_shift); + + return _values[_index]; } /** * Stores value at a given shift. Takes into consideration as-series flag. */ - void Store(int _shift, C _value) override { Array::ArrayStore(_values, _shift, _value, 4096); } + 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); + } /** * Returns number of values available to fetch (size of the values buffer). From 5330e448f2b4cb7bbc5a540b5c19cbcef32537ff Mon Sep 17 00:00:00 2001 From: Adrian Kierzkowski Date: Wed, 27 Mar 2024 15:13:43 +0100 Subject: [PATCH 2/2] WIP. Refs EA31337/EA31337-indicators-stats#2. Indi_AccountStats closer to be finished. Awaiting to fix MT5 compiler bug. --- IndicatorData.mqh | 48 ++++++++++--------- Indicators/Account/Indi_AccountStats.mqh | 26 ++++++++-- .../Account/tests/Indi_AccountStats.test.mq5 | 2 +- Storage/ValueStorage.native.h | 9 ++++ 4 files changed, 58 insertions(+), 27 deletions(-) diff --git a/IndicatorData.mqh b/IndicatorData.mqh index bc0c498b1..990c2e1f3 100644 --- a/IndicatorData.mqh +++ b/IndicatorData.mqh @@ -68,7 +68,7 @@ class IndicatorData : public IndicatorBase { case IDATA_ICUSTOM: break; case IDATA_INDICATOR: - if (indi_src.IsSet() == NULL) { + if (indi_src.IsSet()) { // Indi_Price* _indi_price = Indi_Price::GetCached(GetSymbol(), GetTf(), iparams.GetShift()); // SetDataSource(_indi_price, true, PRICE_OPEN); } @@ -109,7 +109,7 @@ class IndicatorData : public IndicatorBase { /** * Class constructor. */ - IndicatorData(const IndicatorDataParams& _idparams, IndicatorBase* _indi_src = NULL, int _indi_mode = 0) + IndicatorData(const IndicatorDataParams& _idparams, IndicatorBase* _indi_src = nullptr, int _indi_mode = 0) : do_draw(false), idparams(_idparams), indi_src(_indi_src) { Init(); } @@ -572,9 +572,9 @@ class IndicatorData : public IndicatorBase { * Returns currently selected data source doing validation. */ IndicatorData* GetDataSource(bool _validate = true) { - IndicatorData* _result = NULL; + IndicatorData* _result = nullptr; - if (GetDataSourceRaw() != NULL) { + if (GetDataSourceRaw() != nullptr) { _result = GetDataSourceRaw(); } else if (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_SRC_ID)) != -1) { int _source_id = Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_SRC_ID)); @@ -603,7 +603,7 @@ class IndicatorData : public IndicatorBase { // Requesting potential data source. _result = OnDataSourceRequest(); - if (_result != NULL) { + if (_result != nullptr) { // Initializing with new data source. SetDataSource(_result); Set(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE), IDATA_INDICATOR); @@ -621,7 +621,7 @@ class IndicatorData : public IndicatorBase { * Returns given data source type. Used by i*OnIndicator methods if indicator's Calculate() uses other indicators. */ IndicatorData* GetDataSource(ENUM_INDICATOR_TYPE _type) { - IndicatorData* _result = NULL; + IndicatorData* _result = nullptr; if (indicators.KeyExists((int)_type)) { _result = indicators[(int)_type].Ptr(); } else { @@ -697,11 +697,15 @@ class IndicatorData : public IndicatorBase { } if (Get(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE)) == IDATA_INDICATOR && - GetDataSourceRaw() == NULL && _try_initialize) { + GetDataSourceRaw() == nullptr && _try_initialize) { SetDataSource(OnDataSourceRequest()); } - return GetDataSourceRaw() != NULL; + IndicatorData* _ptr = GetDataSourceRaw(); + + bool _result = _ptr != nullptr; + + return _result; } /** @@ -802,7 +806,7 @@ class IndicatorData : public IndicatorBase { indi_src.Ptr().RemoveListener(THIS_PTR); } indi_src = _indi; - if (_indi != NULL) { + if (_indi != nullptr) { 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); @@ -862,7 +866,7 @@ class IndicatorData : public IndicatorBase { last_tick_time = _current_time; // Checking and potentially initializing new data source. - if (HasDataSource(true) != NULL) { + if (HasDataSource(true)) { // Ticking data source if not yet ticked. GetDataSource().Tick(); } @@ -905,13 +909,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 == NULL) { + if (_target == nullptr) { Alert("Internal Error! _target is NULL in ", __FUNCTION_LINE__, "."); DebugBreak(); return; } - if (_source == NULL) { + if (_source == nullptr) { Alert("Error! You have to select source indicator's via SetDataSource()."); DebugBreak(); return; @@ -1129,7 +1133,7 @@ class IndicatorData : public IndicatorBase { _originator PTR_DEREF GetFullName(), "!"); DebugBreak(); } - return NULL; + return nullptr; } } @@ -1146,7 +1150,7 @@ class IndicatorData : public IndicatorBase { /** * Returns the indicator's struct value via index. */ - virtual IndicatorDataEntry GetEntry(long _index = 0) = NULL; + virtual IndicatorDataEntry GetEntry(long _index = 0) = 0; /** * Returns the indicator's struct value via timestamp. @@ -1171,7 +1175,7 @@ class IndicatorData : public IndicatorBase { /** * Returns the indicator's entry value. */ - virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = 0) = NULL; + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = 0) = 0; /** * Returns the shift of the maximum value over a specific number of periods depending on type. @@ -1238,7 +1242,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) = NULL; + virtual IndicatorSignal GetSignals(int _count = 3, int _shift = 0, int _mode1 = 0, int _mode2 = 0) = 0; /** * Returns spread for the bar. @@ -1331,7 +1335,7 @@ class IndicatorData : public IndicatorBase { ", only PRICE_(OPEN|HIGH|LOW|CLOSE|MEDIAN|TYPICAL|WEIGHTED) are currently supported by " "IndicatorBase::GetSpecificAppliedPriceValueStorage()!"); DebugBreak(); - return NULL; + return nullptr; } } @@ -1374,7 +1378,7 @@ class IndicatorData : public IndicatorBase { "Volume) in the hierarchy!"); DebugBreak(); } - return NULL; + return nullptr; } /** @@ -1390,7 +1394,7 @@ class IndicatorData : public IndicatorBase { virtual IValueStorage* GetSpecificValueStorage(ENUM_INDI_VS_TYPE _type) { Print("Error: ", GetFullName(), " indicator has no storage type ", EnumToString(_type), "!"); DebugBreak(); - return NULL; + return nullptr; } /** @@ -1632,7 +1636,7 @@ class IndicatorData : public IndicatorBase { /** * Provides built-in indicators whose can be used as data source. */ - virtual IndicatorData* FetchDataSource(ENUM_INDICATOR_TYPE _id) { return NULL; } + virtual IndicatorData* FetchDataSource(ENUM_INDICATOR_TYPE _id) { return nullptr; } /** * Checks whether indicator support given value storage type. @@ -1700,7 +1704,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 NULL; + return nullptr; } /** @@ -1708,7 +1712,7 @@ class IndicatorData : public IndicatorBase { */ virtual IndicatorData* DataSourceRequestReturnDefault(int _applied_price) { DebugBreak(); - return NULL; + return nullptr; } /** diff --git a/Indicators/Account/Indi_AccountStats.mqh b/Indicators/Account/Indi_AccountStats.mqh index da45fef7d..975064d96 100644 --- a/Indicators/Account/Indi_AccountStats.mqh +++ b/Indicators/Account/Indi_AccountStats.mqh @@ -145,7 +145,6 @@ class Indi_AccountStats : public Indicator { * Returns value storage of given kind. */ IValueStorage *GetSpecificValueStorage(ENUM_INDI_VS_TYPE _type) override { - // Returning Price indicator which provides applied price in the only buffer #0. switch (_type) { case INDI_VS_TYPE_ACCOUNT_STATS_DATE_TIME: return buffer_date_time.Ptr(); @@ -200,8 +199,27 @@ class Indi_AccountStats : public Indicator { return; } - // Adding new account stats entry. - - Print("New candle: ", entry.ToString()); + // New candle means that account stats for current index 0 will be that we + // will now extract and store in the buffers. + + // Extracting current account stats. + datetime stats_date_time = iparams.GetAccount() PTR_DEREF GetDateTime(); + float stats_balance = iparams.GetAccount() PTR_DEREF GetBalance(); + float stats_credit = iparams.GetAccount() PTR_DEREF GetCredit(); + float stats_profit = iparams.GetAccount() PTR_DEREF GetProfit(); + float stats_equity = iparams.GetAccount() PTR_DEREF GetEquity(); + float stats_margin_used = iparams.GetAccount() PTR_DEREF GetMarginUsed(); + float stats_margin_free = iparams.GetAccount() PTR_DEREF GetMarginFree(); + float stats_margin_avail = iparams.GetAccount() PTR_DEREF GetMarginAvail(); + + // Appending account stats into buffers. + buffer_date_time REF_DEREF Append(stats_date_time); + buffer_balance REF_DEREF Append(stats_balance); + buffer_credit REF_DEREF Append(stats_credit); + buffer_profit REF_DEREF Append(stats_profit); + buffer_equity REF_DEREF Append(stats_equity); + buffer_margin_used REF_DEREF Append(stats_margin_used); + buffer_margin_free REF_DEREF Append(stats_margin_free); + buffer_margin_avail REF_DEREF Append(stats_margin_avail); } }; diff --git a/Indicators/Account/tests/Indi_AccountStats.test.mq5 b/Indicators/Account/tests/Indi_AccountStats.test.mq5 index c81e0da79..9f72f4356 100644 --- a/Indicators/Account/tests/Indi_AccountStats.test.mq5 +++ b/Indicators/Account/tests/Indi_AccountStats.test.mq5 @@ -48,7 +48,7 @@ int OnInit() { void OnTick() { Platform::Tick(); - if (Platform::IsNewHour()) { + if (Platform::IsNewMinute()) { IndicatorDataEntry _entry = indi_account_mt REF_DEREF GetEntry(); bool _is_ready = indi_account_mt REF_DEREF Get(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_READY)); bool _is_valid = _entry.IsValid(); diff --git a/Storage/ValueStorage.native.h b/Storage/ValueStorage.native.h index 0c6180881..7fb8219fc 100644 --- a/Storage/ValueStorage.native.h +++ b/Storage/ValueStorage.native.h @@ -83,6 +83,15 @@ class NativeValueStorage : public ValueStorage { Array::ArrayStore(_values, _index, _value, 4096); } + /** + * Inserts new value at the end of the buffer. If buffer works as As-Series, + * then new value will act as the one at index 0. + */ + void Append(C _value) override { + Resize(Size() + 1, 4096); + Store(Size() - 1, _value); + } + /** * Returns number of values available to fetch (size of the values buffer). */