From 8ac67d310aff80c103ce06eb29e35b3647838ae0 Mon Sep 17 00:00:00 2001 From: Adrian Kierzkowski Date: Wed, 4 Aug 2021 20:17:11 +0200 Subject: [PATCH] Added Indi_AppliedPrice indicator and fixed some of the "delete invalid pointer" errors. --- EA.mqh | 51 +++++---- Indicator.enum.h | 1 + Indicator.mqh | 23 ++++ Indicators/Indi_AppliedPrice.mqh | 160 +++++++++++++++++++++++++++ Indicators/Indi_CCI.mqh | 20 +++- Indicators/Indi_Envelopes.mqh | 21 ++-- Matrix.mqh | 8 +- Profiler.mqh | 44 ++++---- Refs.struct.h | 33 +++++- Std.h | 2 + Strategy.mqh | 14 ++- Trade.mqh | 181 ++++++++++++++++--------------- 12 files changed, 399 insertions(+), 159 deletions(-) create mode 100644 Indicators/Indi_AppliedPrice.mqh diff --git a/EA.mqh b/EA.mqh index e27d3ea7c..47c1b7c90 100644 --- a/EA.mqh +++ b/EA.mqh @@ -57,10 +57,10 @@ class EA { Account *account; DictObject>> strats; DictStruct tasks; - Market *market; + Ref market; Ref logger; SummaryReport *report; - Terminal *terminal; + Ref terminal; // Data variables. BufferStruct data_chart; @@ -102,9 +102,7 @@ class EA { ProcessTasks(); // Deinitialize classes. Object::Delete(account); - Object::Delete(market); Object::Delete(report); - Object::Delete(terminal); } Log *Logger() { return logger.Ptr(); } @@ -274,12 +272,12 @@ class EA { if (estate.IsEnabled()) { eresults.Reset(); if (estate.IsActive()) { - market.SetTick(SymbolInfoStatic::GetTick(_Symbol)); + GetMarket().SetTick(SymbolInfoStatic::GetTick(_Symbol)); ProcessPeriods(); for (DictObjectIterator>> iter_tf = strats.Begin(); iter_tf.IsValid(); ++iter_tf) { ENUM_TIMEFRAMES _tf = iter_tf.Key(); - ProcessTickByTf(_tf, market.GetLastTick()); + ProcessTickByTf(_tf, GetMarket().GetLastTick()); } if (eresults.last_error > ERR_NO_ERROR) { logger.Ptr().Flush(); @@ -624,12 +622,12 @@ class EA { * Update EA state flags. */ void UpdateStateFlags() { - estate.SetFlag(EA_STATE_FLAG_CONNECTED, terminal.IsConnected()); - estate.SetFlag(EA_STATE_FLAG_LIBS_ALLOWED, terminal.IsLibrariesAllowed()); - estate.SetFlag(EA_STATE_FLAG_OPTIMIZATION, terminal.IsOptimization()); - estate.SetFlag(EA_STATE_FLAG_TESTING, terminal.IsTesting()); - estate.SetFlag(EA_STATE_FLAG_TRADE_ALLOWED, terminal.IsTradeAllowed()); - estate.SetFlag(EA_STATE_FLAG_VISUAL_MODE, terminal.IsVisualMode()); + estate.SetFlag(EA_STATE_FLAG_CONNECTED, GetTerminal().IsConnected()); + estate.SetFlag(EA_STATE_FLAG_LIBS_ALLOWED, GetTerminal().IsLibrariesAllowed()); + estate.SetFlag(EA_STATE_FLAG_OPTIMIZATION, GetTerminal().IsOptimization()); + estate.SetFlag(EA_STATE_FLAG_TESTING, GetTerminal().IsTesting()); + estate.SetFlag(EA_STATE_FLAG_TRADE_ALLOWED, GetTerminal().IsTradeAllowed()); + estate.SetFlag(EA_STATE_FLAG_VISUAL_MODE, GetTerminal().IsVisualMode()); } /** @@ -676,7 +674,7 @@ class EA { case EA_COND_IS_ENABLED: return estate.IsEnabled(); case EA_COND_IS_NOT_CONNECTED: - estate.SetFlag(EA_STATE_FLAG_CONNECTED, terminal.IsConnected()); + estate.SetFlag(EA_STATE_FLAG_CONNECTED, GetTerminal().IsConnected()); return !estate.IsConnected(); case EA_COND_ON_NEW_MINUTE: // On new minute. return (estate.new_periods & DATETIME_MINUTE) != 0; @@ -775,6 +773,16 @@ class EA { /* Getters */ + /** + * Returns pointer to Terminal object. + */ + Market *GetMarket() { return market.Ptr(); } + + /** + * Returns pointer to Market object. + */ + Terminal *GetTerminal() { return terminal.Ptr(); } + /** * Gets EA's name. */ @@ -843,7 +851,7 @@ class EA { /** * Gets pointer to market details. */ - Market *Market() { return market; } + Market *Market() { return market.Ptr(); } /** * Gets pointer to strategies. @@ -853,12 +861,7 @@ class EA { /** * Gets pointer to symbol details. */ - SymbolInfo *SymbolInfo() { return (SymbolInfo *)market; } - - /** - * Gets pointer to terminal instance. - */ - Terminal *Terminal() { return terminal; } + SymbolInfo *SymbolInfo() { return (SymbolInfo *)GetMarket(); } /* Setters */ @@ -920,7 +923,13 @@ class EA { */ SerializerNodeType Serialize(Serializer &_s) { _s.Pass(THIS_REF, "account", account, SERIALIZER_FIELD_FLAG_DYNAMIC); - _s.Pass(THIS_REF, "market", market, SERIALIZER_FIELD_FLAG_DYNAMIC); + + // In MQL it will be: Market* _market = GetMarket(); + // In C++ it will be: Market& _market = GetMarket(); + // It is needed as PassObject() expects reference to object instead of its pointer. + MAKE_REF_FROM_PTR(Market, _market, GetMarket()); + _s.PassObject(THIS_REF, "market", _market, SERIALIZER_FIELD_FLAG_DYNAMIC); + for (DictObjectIterator>> _iter_tf = GetStrategies().Begin(); _iter_tf.IsValid(); ++_iter_tf) { ENUM_TIMEFRAMES _tf = _iter_tf.Key(); diff --git a/Indicator.enum.h b/Indicator.enum.h index 10a2034be..04ed760db 100644 --- a/Indicator.enum.h +++ b/Indicator.enum.h @@ -49,6 +49,7 @@ enum ENUM_INDICATOR_TYPE { INDI_ADXW, // ADX by Welles Wilder INDI_ALLIGATOR, // Alligator INDI_AMA, // Adaptive Moving Average + INDI_APPLIED_PRICE, // Applied Price over OHLC Indicator INDI_AO, // Awesome Oscillator INDI_ASI, // Accumulation Swing Index INDI_ATR, // Average True Range diff --git a/Indicator.mqh b/Indicator.mqh index 6ccf7a3e1..2f13fadc1 100644 --- a/Indicator.mqh +++ b/Indicator.mqh @@ -128,6 +128,9 @@ class Indicator : public Chart { if (iparams.indi_data_source != NULL && iparams.indi_managed) { // User selected custom, managed data source. + if (CheckPointer(iparams.indi_data_source) == POINTER_INVALID) { + DebugBreak(); + } delete iparams.indi_data_source; iparams.indi_data_source = NULL; } @@ -444,6 +447,26 @@ class Indicator : public Chart { } } + /** + * Checks whether indicator have given mode index. + * + * If given mode is -1 (default one) and indicator has exactly one mode, then mode index will be replaced by 0. + */ + void ValidateDataSourceMode(int& _out_mode) { + if (_out_mode == -1) { + // First mode will be used by default, or, if selected indicator has more than one mode, error will happen. + if (iparams.max_modes != 1) { + Alert("Error: ", GetName(), " must have exactly one possible mode in order to skip using SetDataSourceMode()!"); + DebugBreak(); + } + _out_mode = 0; + } else if (_out_mode + 1 > (int)iparams.max_modes) { + Alert("Error: ", GetName(), " have ", iparams.max_modes, " mode(s) buy you tried to reference mode with index ", + _out_mode, "! Ensure that you properly set mode via SetDataSourceMode()."); + DebugBreak(); + } + } + /** * Provides built-in indicators whose can be used as data source. */ diff --git a/Indicators/Indi_AppliedPrice.mqh b/Indicators/Indi_AppliedPrice.mqh new file mode 100644 index 000000000..a16501379 --- /dev/null +++ b/Indicators/Indi_AppliedPrice.mqh @@ -0,0 +1,160 @@ +//+------------------------------------------------------------------+ +//| 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 "../BufferStruct.mqh" +#include "../Indicator.mqh" + +// Structs. +struct AppliedPriceParams : IndicatorParams { + unsigned int smooth_period; + unsigned int chv_period; + ENUM_MA_METHOD smooth_method; + // Struct constructor. + void CHVParams(int _mode) { + chv_period = _chv_period; + itype = INDI_APPLIED_PRICE; + max_modes = 1; + SetDataValueType(TYPE_DOUBLE); + SetDataValueRange(IDATA_RANGE_PRICE); + SetDataSourceType(IDATA_ICUSTOM); + }; + void CHVParams(CHVParams &_params) { this = _params; }; +}; + +/** + * Implements the "Applied Price over OHCL Indicator" indicator, e.g. over Indi_Price. + */ +class Indi_AppliedPrice : public Indicator { + protected: + AppliedPrice params; + + public: + /** + * Class constructor. + */ + Indi_AppliedPrice(AppliedPriceParams &_params) : params(_params){}; + Indi_AppliedPrice() : Indicator(INDI_APPLIED_PRICE){}; + + static double iCCIOnIndicator(Indicator *_indi, int _applied_price, int _shift = 0) { + double _ohlc[4]; + _indi.GetArray(_ohlc, 4); + return BarOHLC::GetAppliedPrice(_applied_price, _ohlc[0], _ohlc[1], _ohlc[2], _ohlc[3]); + } + + /** + * Returns the indicator's value. + */ + double GetValue(int _shift = 0) { + ResetLastError(); + double _value = EMPTY_VALUE; + switch (params.idstype) { + case IDATA_ICUSTOM: + if (GetDataSourceMode() == -1) { + Print( + "Please use SetDataSourceMode() to select source indicator's buffer. Note that SetAppliedPrice() can be " + "used only with built-in or compiled indicators, but not with indicator-on-indicator mode."); + DebugBreak(); + } + _value = Indi_AppliedPrice::iAppliedPriceOnIndicator(GetDataSource(), GetDataSourceMode(), _shift); + break; + default: + SetUserError(ERR_INVALID_PARAMETER); + } + istate.is_ready = _LastError == ERR_NO_ERROR; + istate.is_changed = false; + return _value; + } + + /** + * Returns the indicator's struct value. + */ + IndicatorDataEntry GetEntry(int _shift = 0) { + long _bar_time = GetBarTime(_shift); + unsigned int _position; + IndicatorDataEntry _entry(params.max_modes); + if (idata.KeyExists(_bar_time, _position)) { + _entry = idata.GetByPos(_position); + } else { + _entry.timestamp = GetBarTime(_shift); + _entry.values[0] = GetValue(_shift); + _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, !_entry.HasValue(NULL) && !_entry.HasValue(EMPTY_VALUE)); + if (_entry.IsValid()) { + _entry.AddFlags(_entry.GetDataTypeFlag(params.GetDataValueType())); + idata.Add(_entry, _bar_time); + } + } + return _entry; + } + + /** + * Returns the indicator's entry value. + */ + MqlParam GetEntryValue(int _shift = 0, int _mode = 0) { + MqlParam _param = {TYPE_DOUBLE}; + _param.double_value = GetEntry(_shift)[_mode]; + return _param; + } + + /* Getters */ + + /** + * Get smooth period. + */ + unsigned int GetSmoothPeriod() { return params.smooth_period; } + + /** + * Get Chaikin period. + */ + unsigned int GetCHVPeriod() { return params.chv_period; } + + /** + * Get smooth method. + */ + ENUM_MA_METHOD GetSmoothMethod() { return params.smooth_method; } + + /* Setters */ + + /** + * Get smooth period. + */ + void SetSmoothPeriod(unsigned int _smooth_period) { + istate.is_changed = true; + params.smooth_period = _smooth_period; + } + + /** + * Get Chaikin period. + */ + void SetCHVPeriod(unsigned int _chv_period) { + istate.is_changed = true; + params.chv_period = _chv_period; + } + + /** + * Set smooth method. + */ + void SetSmoothMethod(ENUM_MA_METHOD _smooth_method) { + istate.is_changed = true; + params.smooth_method = _smooth_method; + } +}; diff --git a/Indicators/Indi_CCI.mqh b/Indicators/Indi_CCI.mqh index eb10e7f96..06acae63b 100644 --- a/Indicators/Indi_CCI.mqh +++ b/Indicators/Indi_CCI.mqh @@ -115,16 +115,18 @@ class Indi_CCI : public Indicator { #endif } - static double iCCIOnIndicator(Indicator *_indi, string _symbol, ENUM_TIMEFRAMES _tf, unsigned int _period, - ENUM_APPLIED_PRICE _applied_price, int _shift = 0) { - double _indi_value_buffer[], _ohlc[4]; + static double iCCIOnIndicator(Indicator *_indi, string _symbol, ENUM_TIMEFRAMES _tf, unsigned int _period, int _mode, + int _shift = 0) { + _indi.ValidateDataSourceMode(_mode); + + double _indi_value_buffer[]; IndicatorDataEntry _entry(_indi.GetParams().GetMaxModes()); ArrayResize(_indi_value_buffer, _period); for (int i = _shift; i < (int)_shift + (int)_period; i++) { - _indi[i].GetArray(_ohlc, 4); - _indi_value_buffer[i - _shift] = BarOHLC::GetAppliedPrice(_applied_price, _ohlc[0], _ohlc[1], _ohlc[2], _ohlc[3]); + // Getting value from single, selected buffer. + _indi_value_buffer[i - _shift] = _indi[i].GetValue(_mode); } double d; @@ -192,8 +194,14 @@ class Indi_CCI : public Indicator { break; case IDATA_INDICATOR: // @fixit Somehow shift isn't used neither in MT4 nor MT5. + if (GetDataSourceMode() == -1) { + Print( + "Please use SetDataSourceMode() to select source indicator's buffer. Note that SetAppliedPrice() can be " + "used only with built-in or compiled indicators, but not with indicator-on-indicator mode."); + DebugBreak(); + } _value = Indi_CCI::iCCIOnIndicator(GetDataSource(), Get(CHART_PARAM_SYMBOL), - Get(CHART_PARAM_TF), GetPeriod(), GetAppliedPrice(), + Get(CHART_PARAM_TF), GetPeriod(), GetDataSourceMode(), _shift /* + params.shift*/); break; } diff --git a/Indicators/Indi_Envelopes.mqh b/Indicators/Indi_Envelopes.mqh index 5c4966321..d5462fa79 100644 --- a/Indicators/Indi_Envelopes.mqh +++ b/Indicators/Indi_Envelopes.mqh @@ -148,26 +148,25 @@ class Indi_Envelopes : public Indicator { static double iEnvelopesOnIndicator(Indicator *_indi, string _symbol, ENUM_TIMEFRAMES _tf, int _ma_period, ENUM_MA_METHOD _ma_method, // (MT4/MT5): MODE_SMA, MODE_EMA, MODE_SMMA, MODE_LWMA - int _ma_shift, - ENUM_APPLIED_PRICE _ap, // (MT4/MT5): PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, - // PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED - double _deviation, + int _indi_mode, // Source indicator's mode index. May be -1 to use first buffer + int _ma_shift, double _deviation, int _mode, // (MT4 _mode): 0 - MODE_MAIN, 1 - MODE_UPPER, 2 - MODE_LOWER; (MT5 // _mode): 0 - UPPER_LINE, 1 - LOWER_LINE int _shift = 0) { - double _indi_value_buffer[], _ohlc[4]; + _indi.ValidateDataSourceMode(_indi_mode); + + double _indi_value_buffer[]; double _result; int i; ArrayResize(_indi_value_buffer, _ma_period); for (i = _shift; i < (int)_shift + (int)_ma_period; i++) { - _indi[i].GetArray(_ohlc, 4); - _indi_value_buffer[i - _shift] = BarOHLC::GetAppliedPrice(_ap, _ohlc[0], _ohlc[1], _ohlc[2], _ohlc[3]); + _indi_value_buffer[i - _shift] = _indi[i].GetValue(_indi_mode); } Indi_PriceFeeder indi_price_feeder(_indi_value_buffer); - MAParams ma_params(_ma_period, _ma_shift, _ma_method, /*unused*/ _ap); + MAParams ma_params(_ma_period, _ma_shift, _ma_method); ma_params.SetDataSource(&indi_price_feeder, false, 0); Indi_MA indi_ma(ma_params); @@ -206,8 +205,8 @@ class Indi_Envelopes : public Indicator { return iEnvelopesOnArray(array, total, ma_period, ma_method, ma_shift, deviation, mode, shift); #else Indi_PriceFeeder indi_price_feeder(array); - return Indi_Envelopes::iEnvelopesOnIndicator(&indi_price_feeder, NULL, NULL, ma_period, ma_method, ma_shift, - (ENUM_APPLIED_PRICE)-1, deviation, mode, shift); + return Indi_Envelopes::iEnvelopesOnIndicator(&indi_price_feeder, NULL, NULL, ma_period, ma_method, + /* indi_mode */ 0, ma_shift, deviation, mode, shift); #endif } @@ -232,7 +231,7 @@ class Indi_Envelopes : public Indicator { case IDATA_INDICATOR: _value = Indi_Envelopes::iEnvelopesOnIndicator( GetDataSource(), Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF), GetMAPeriod(), - GetMAMethod(), GetMAShift(), GetAppliedPrice(), GetDeviation(), _mode, _shift); + GetMAMethod(), GetDataSourceMode(), GetMAShift(), GetDeviation(), _mode, _shift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Matrix.mqh b/Matrix.mqh index 2c1c4c3ca..069588d7f 100644 --- a/Matrix.mqh +++ b/Matrix.mqh @@ -846,7 +846,11 @@ class Matrix { /** * Destructor. */ - ~Matrix() { delete ptr_first_dimension; } + ~Matrix() { + if (ptr_first_dimension != NULL) { + delete ptr_first_dimension; + } + } /** * Index operator. Returns container or value accessor. @@ -1237,7 +1241,7 @@ class Matrix { else median = array[len / 2]; - return (X) median; + return (X)median; } return MinOf((X)0); } diff --git a/Profiler.mqh b/Profiler.mqh index 92ae5d87e..15065de37 100644 --- a/Profiler.mqh +++ b/Profiler.mqh @@ -25,36 +25,34 @@ #include "Timer.mqh" // Defines macros. -#define PROFILER_SET_MIN(ms) Profiler::min_time = ms; -#define PROFILER_START \ - static Timer *_timer = NULL; \ +#define PROFILER_SET_MIN(ms) Profiler::min_time = ms; +#define PROFILER_START \ + static Timer *_timer = NULL; \ _timer = _timer ? _timer : new Timer(__FUNCTION__); \ - ((Timer *) Profiler::timers.Get(_timer)).Start(); + ((Timer *)Profiler::timers.Get(_timer)).Start(); -#define PROFILER_STOP ((Timer *) Profiler::timers.Get(_timer)).Stop(); -#define PROFILER_STOP_PRINT ((Timer *) Profiler::timers.Get(_timer)).Stop().PrintOnMax(Profiler::min_time); -#define PROFILER_PRINT Print(Profiler::timers.ToString(Profiler::min_time)); -#define PROFILER_DEINIT Profiler::Deinit(); +#define PROFILER_STOP ((Timer *)Profiler::timers.Get(_timer)).Stop(); +#define PROFILER_STOP_PRINT ((Timer *)Profiler::timers.Get(_timer)).Stop().PrintOnMax(Profiler::min_time); +#define PROFILER_PRINT Print(Profiler::timers.ToString(Profiler::min_time)); +#define PROFILER_DEINIT Profiler::Deinit(); /** * Class to provide performance profiler functionality. */ class Profiler { - - public: - - // Variables. - static Collection *timers; - static ulong min_time; - - /* Class methods */ - - /** - * Class deconstructor. - */ - Profiler() { }; - ~Profiler() { Deinit(); }; - static void Deinit() { delete Profiler::timers; }; + public: + // Variables. + static Collection *timers; + static ulong min_time; + + /* Class methods */ + + /** + * Class deconstructor. + */ + Profiler(){}; + ~Profiler() { Deinit(); }; + static void Deinit() { delete Profiler::timers; }; }; // Initialize static global variables. diff --git a/Refs.struct.h b/Refs.struct.h index db7728b2d..d14ae48d6 100644 --- a/Refs.struct.h +++ b/Refs.struct.h @@ -34,7 +34,7 @@ class Refs; class ReferenceCounter; template - struct WeakRef; +struct WeakRef; /** * Class used to hold strong reference to reference-counted object. @@ -87,8 +87,14 @@ struct Ref { */ void Unset() { if (ptr_object != NULL) { + if (ptr_object.ptr_ref_counter == NULL) { + // Object is not reference counted. Maybe a stack-based one? + return; + } + if (CheckPointer(ptr_object) == POINTER_INVALID) { // Double check the pointer for invalid references. Can happen very rarely. + DebugBreak(); return; } // Dropping strong reference. @@ -99,6 +105,12 @@ struct Ref { // No more strong references. if (!ptr_object.ptr_ref_counter.num_weak_refs) { + if (CheckPointer(ptr_object.ptr_ref_counter) == POINTER_INVALID) { + // Serious problem. + DebugBreak(); + return; + } + // Also no more weak references. delete ptr_object.ptr_ref_counter; ptr_object.ptr_ref_counter = NULL; @@ -110,6 +122,12 @@ struct Ref { // Avoiding delete loop for cyclic references. X* ptr_to_delete = ptr_object; + if (CheckPointer(ptr_to_delete) == POINTER_INVALID) { + // Serious problem. + DebugBreak(); + return; + } + // Avoiding double deletion in Dynamic's destructor. ptr_object.ptr_ref_counter = NULL; ptr_object = NULL; @@ -257,8 +275,21 @@ struct WeakRef { Print("Refs: Deleting object ", ptr_ref_counter.ptr_object); #endif + if (CheckPointer(ptr_ref_counter.ptr_object) == POINTER_INVALID) { + // Serious problem. + DebugBreak(); + return; + } + delete ptr_ref_counter.ptr_object; } + + if (CheckPointer(stored_ptr_ref_counter) == POINTER_INVALID) { + // Serious problem. + DebugBreak(); + return; + } + delete stored_ptr_ref_counter; } } diff --git a/Std.h b/Std.h index 9b19fa690..912c657b9 100644 --- a/Std.h +++ b/Std.h @@ -49,11 +49,13 @@ typedef unsigned short ushort; #define THIS_REF this #define PTR_ATTRIB(O, A) O.A #define PTR_TO_REF(PTR) PTR +#define MAKE_REF_FROM_PTR(TYPE, NAME, PTR) TYPE* NAME = PTR #else #define THIS_PTR (this) #define THIS_REF (*this) #define PTR_ATTRIB(O, A) O->A #define PTR_TO_REF(PTR) (*PTR) +#define MAKE_REF_FROM_PTR(TYPE, NAME, PTR) TYPE& NAME = PTR #endif // References. diff --git a/Strategy.mqh b/Strategy.mqh index 06226666e..b2e1246a8 100644 --- a/Strategy.mqh +++ b/Strategy.mqh @@ -72,7 +72,7 @@ class Strategy : public Object { Dict fdata; Dict idata; DictStruct tasks; - Log logger; // Log instance. + Ref logger; // Log instance. MqlTick last_tick; StgProcessResult sresult; Strategy *strat_sl, *strat_tp; // Strategy pointers for stop-loss and profit-take. @@ -118,6 +118,8 @@ class Strategy : public Object { Strategy::OnInit(); } + Log *GetLogger() { return logger.Ptr(); } + /** * Class copy constructor. */ @@ -214,7 +216,7 @@ class Strategy : public Object { sresult.stops_invalid_sl += (unsigned short)sl_valid; sresult.stops_invalid_tp += (unsigned short)tp_valid; } else { - logger.Error("Error loading SL/TP objects!", __FUNCTION_LINE__); + GetLogger().Error("Error loading SL/TP objects!", __FUNCTION_LINE__); } } else { trade.OrderMoveToHistory(_order); @@ -813,7 +815,7 @@ class Strategy : public Object { } return _result; default: - logger.Error(StringFormat("Invalid EA condition: %s!", EnumToString(_cond), __FUNCTION_LINE__)); + GetLogger().Error(StringFormat("Invalid EA condition: %s!", EnumToString(_cond), __FUNCTION_LINE__)); return false; } } @@ -901,7 +903,7 @@ class Strategy : public Object { sparams.Suspended(false); return true; default: - logger.Error(StringFormat("Invalid Strategy action: %s!", EnumToString(_action), __FUNCTION_LINE__)); + GetLogger().Error(StringFormat("Invalid Strategy action: %s!", EnumToString(_action), __FUNCTION_LINE__)); return false; } return _result; @@ -964,7 +966,7 @@ class Strategy : public Object { * _order Order Instance of order which got opened. */ virtual void OnOrderOpen(Order &_order) { - if (logger.GetLevel() >= V_INFO) { + if (GetLogger().GetLevel() >= V_INFO) { // logger.Info(_order.ToString(), (string)_order.GetTicket()); // @fixme: memory leaks. ResetLastError(); } @@ -1006,7 +1008,7 @@ class Strategy : public Object { virtual void OnPeriod(unsigned int _periods = DATETIME_NONE) { if ((_periods & DATETIME_MINUTE) != 0) { // New minute started. - logger.Flush(); + GetLogger().Flush(); } if ((_periods & DATETIME_HOUR) != 0) { // New hour started. diff --git a/Trade.mqh b/Trade.mqh index 716be4769..d5a6f4dce 100644 --- a/Trade.mqh +++ b/Trade.mqh @@ -46,11 +46,11 @@ class Trade; class Trade { public: Account account; - Chart chart; + Ref chart; DictStruct> orders_active; DictStruct> orders_history; DictStruct> orders_pending; - Log logger; // Trade logger. + Ref logger; // Trade logger. TradeParams tparams; // Trade parameters. TradeStates tstates; // Trade states. TradeStats tstats; // Trade statistics. @@ -68,7 +68,8 @@ class Trade { SetName(); OrdersLoadByMagic(); }; - Trade(TradeParams &_tparams, ChartParams &_cparams) : chart(_cparams), tparams(_tparams), order_last(NULL) { + Trade(TradeParams &_tparams, ChartParams &_cparams) + : chart(new Chart(_cparams)), tparams(_tparams), order_last(NULL) { SetName(); OrdersLoadByMagic(); }; @@ -104,7 +105,7 @@ class Trade { */ template T Get(ENUM_CHART_PARAM _param) { - return chart.Get(_param); + return GetChart().Get(_param); } /** @@ -179,8 +180,8 @@ class Trade { * Sets default name of trade instance. */ void SetName() { - name = StringFormat("%s@%s", chart.Get(CHART_PARAM_SYMBOL), - ChartTf::TfToString(chart.Get(CHART_PARAM_TF))); + name = StringFormat("%s@%s", GetChart().Get(CHART_PARAM_SYMBOL), + ChartTf::TfToString(GetChart().Get(CHART_PARAM_TF))); } /** @@ -197,9 +198,9 @@ class Trade { */ bool IsPeak(ENUM_ORDER_TYPE _cmd, int _shift = 0) { bool _result = false; - double _high = chart.GetHigh(_shift + 1); - double _low = chart.GetLow(_shift + 1); - double _open = chart.GetOpenOffer(_cmd); + double _high = GetChart().GetHigh(_shift + 1); + double _low = GetChart().GetLow(_shift + 1); + double _open = GetChart().GetOpenOffer(_cmd); if (_low != _high) { switch (_cmd) { case ORDER_TYPE_BUY: @@ -218,17 +219,17 @@ class Trade { */ bool IsPivot(ENUM_ORDER_TYPE _cmd, int _shift = 0) { bool _result = false; - double _high = chart.GetHigh(_shift + 1); - double _low = chart.GetLow(_shift + 1); - double _close = chart.GetClose(_shift + 1); + double _high = GetChart().GetHigh(_shift + 1); + double _low = GetChart().GetLow(_shift + 1); + double _close = GetChart().GetClose(_shift + 1); if (_close > 0 && _low != _high) { float _pp = (float)(_high + _low + _close) / 3; switch (_cmd) { case ORDER_TYPE_BUY: - _result = chart.GetOpenOffer(_cmd) > _pp; + _result = GetChart().GetOpenOffer(_cmd) > _pp; break; case ORDER_TYPE_SELL: - _result = chart.GetOpenOffer(_cmd) < _pp; + _result = GetChart().GetOpenOffer(_cmd) < _pp; break; } } @@ -246,7 +247,7 @@ class Trade { /** * Check if trading instance is valid. */ - bool IsValid() { return chart.IsValidTf(); } + bool IsValid() { return GetChart().IsValidTf(); } /** * Check if this trade instance has active orders. @@ -261,14 +262,14 @@ class Trade { Ref _order = order_last; if (_order.IsSet() && _order.Ptr().Get(ORDER_TYPE) == _cmd && - _order.Ptr().Get(ORDER_TIME_SETUP) > chart.GetBarTime()) { + _order.Ptr().Get(ORDER_TIME_SETUP) > GetChart().GetBarTime()) { _result = true; } if (!_result) { for (DictStructIterator> iter = orders_active.Begin(); iter.IsValid(); ++iter) { _order = iter.Value(); - if (_order.Ptr().Get(ORDER_TYPE) == _cmd && _order.Ptr().Get(ORDER_TIME_SETUP) > chart.GetBarTime()) { + if (_order.Ptr().Get(ORDER_TYPE) == _cmd && _order.Ptr().Get(ORDER_TIME_SETUP) > GetChart().GetBarTime()) { _result = true; break; } @@ -284,7 +285,7 @@ class Trade { bool _result = false; Ref _order = order_last; OrderData _odata; - double _price_curr = chart.GetOpenOffer(_cmd); + double _price_curr = GetChart().GetOpenOffer(_cmd); if (_order.IsSet()) { _odata = _order.Ptr().GetData(); @@ -382,7 +383,9 @@ class Trade { return _result ? (float)_margin_req : 0; #endif } - float GetMarginRequired(ENUM_ORDER_TYPE _cmd = ORDER_TYPE_BUY) { return GetMarginRequired(chart.GetSymbol(), _cmd); } + float GetMarginRequired(ENUM_ORDER_TYPE _cmd = ORDER_TYPE_BUY) { + return GetMarginRequired(GetChart().GetSymbol(), _cmd); + } /* Lot size methods */ @@ -402,10 +405,10 @@ class Trade { double GetMaxLotSize(double _sl, ENUM_ORDER_TYPE _cmd = NULL) { _cmd = _cmd == NULL ? Order::OrderType() : _cmd; double risk_amount = account.GetTotalBalance() / 100 * tparams.risk_margin; - double _ticks = fabs(_sl - chart.GetOpenOffer(_cmd)) / chart.GetTickSize(); + double _ticks = fabs(_sl - GetChart().GetOpenOffer(_cmd)) / GetChart().GetTickSize(); double lot_size1 = fmin(_sl, _ticks) > 0 ? risk_amount / (_sl * (_ticks / 100.0)) : 1; - lot_size1 *= chart.GetVolumeMin(); - // double lot_size2 = 1 / (chart.GetTickValue() * sl / risk_margin); + lot_size1 *= GetChart().GetVolumeMin(); + // double lot_size2 = 1 / (GetChart().GetTickValue() * sl / risk_margin); // PrintFormat("SL=%g: 1 = %g, 2 = %g", sl, lot_size1, lot_size2); return NormalizeLots(lot_size1); } @@ -452,7 +455,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. Print(__FUNCTION__, ": Error in history!"); break; } - if (deal.Symbol() != chart.GetSymbol()) continue; + if (deal.Symbol() != GetChart().GetSymbol()) continue; double profit = deal.Profit(); */ double profit = 0; @@ -495,7 +498,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. uint _method = 0 // Method of calculation (0-3). ) { float _avail_amount = _method % 2 == 0 ? account.GetMarginAvail() : account.GetTotalBalance(); - float _lot_size_min = (float)chart.GetVolumeMin(); + float _lot_size_min = (float)GetChart().GetVolumeMin(); float _lot_size = _lot_size_min; float _risk_value = (float)account.GetLeverage(); if (_method == 0 || _method == 1) { @@ -506,12 +509,12 @@ HistorySelect(0, TimeCurrent()); // Select history for access. } } else { float _risk_amount = _avail_amount / 100 * _risk_margin; - float _money_value = Convert::MoneyToValue(_risk_amount, _lot_size_min, chart.GetSymbol()); - float _tick_value = chart.GetTickSize(); + float _money_value = Convert::MoneyToValue(_risk_amount, _lot_size_min, GetChart().GetSymbol()); + float _tick_value = GetChart().GetTickSize(); // @todo: Improves calculation logic. _lot_size = _money_value * _tick_value * _risk_ratio / _risk_value / 100; } - _lot_size = (float)fmin(_lot_size, chart.GetVolumeMax()); + _lot_size = (float)fmin(_lot_size, GetChart().GetVolumeMax()); return (float)NormalizeLots(_lot_size); } @@ -523,12 +526,12 @@ HistorySelect(0, TimeCurrent()); // Select history for access. bool OrderAdd(Order *_order) { bool _result = false; unsigned int _last_error = _order.GetData().last_error; - logger.Link(_order.Logger()); + GetLogger().Link(_order.Logger()); Ref _ref_order = _order; switch (_last_error) { case 69539: - logger.Error("Error while opening an order!", __FUNCTION_LINE__, - StringFormat("Code: %d, Msg: %s", _last_error, Terminal::GetErrorText(_last_error))); + GetLogger().Error("Error while opening an order!", __FUNCTION_LINE__, + StringFormat("Code: %d, Msg: %s", _last_error, Terminal::GetErrorText(_last_error))); tstats.Add(TRADE_STAT_ORDERS_ERRORS); // Pass-through. case ERR_NO_ERROR: @@ -540,12 +543,12 @@ HistorySelect(0, TimeCurrent()); // Select history for access. _result = true; break; case TRADE_RETCODE_INVALID: - logger.Error("Cannot process order!", __FUNCTION_LINE__, - StringFormat("Code: %d, Msg: %s", _last_error, Terminal::GetErrorText(_last_error))); + GetLogger().Error("Cannot process order!", __FUNCTION_LINE__, + StringFormat("Code: %d, Msg: %s", _last_error, Terminal::GetErrorText(_last_error))); break; default: - logger.Error("Cannot add order!", __FUNCTION_LINE__, - StringFormat("Code: %d, Msg: %s", _last_error, Terminal::GetErrorText(_last_error))); + GetLogger().Error("Cannot add order!", __FUNCTION_LINE__, + StringFormat("Code: %d, Msg: %s", _last_error, Terminal::GetErrorText(_last_error))); tstats.Add(TRADE_STAT_ORDERS_ERRORS); _result = false; break; @@ -585,7 +588,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. bool OrderOpen(ENUM_ORDER_TYPE _cmd, double _lot_size = 0, string _comment = "") { bool _result = false; if (!IsOrderAllowed()) { - logger.Error("Limit of open and pending orders has reached the limit!", __FUNCTION_LINE__); + GetLogger().Error("Limit of open and pending orders has reached the limit!", __FUNCTION_LINE__); return _result; } // Prepare order request. @@ -594,11 +597,11 @@ HistorySelect(0, TimeCurrent()); // Select history for access. _request.comment = _comment != "" ? _comment : tparams.order_comment; _request.deviation = 10; _request.magic = tparams.GetMagicNo(); - _request.price = chart.GetOpenOffer(_cmd); - _request.symbol = chart.GetSymbol(); + _request.price = GetChart().GetOpenOffer(_cmd); + _request.symbol = GetChart().GetSymbol(); _request.type = _cmd; _request.type_filling = Order::GetOrderFilling(_request.symbol); - _request.volume = _lot_size > 0 ? _lot_size : fmax(tparams.lot_size, chart.GetVolumeMin()); + _request.volume = _lot_size > 0 ? _lot_size : fmax(tparams.lot_size, GetChart().GetVolumeMin()); _request.volume = NormalizeLots(_request.volume); ResetLastError(); if (account.GetAccountFreeMarginCheck(_request.type, _request.volume) > 0) { @@ -611,7 +614,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. OnOrderOpen(_order); } } else { - logger.Error("No free margin to open more orders!", __FUNCTION_LINE__); + GetLogger().Error("No free margin to open more orders!", __FUNCTION_LINE__); } return _result; } @@ -668,7 +671,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. _order = iter.Value(); if (_order.Ptr().IsOpen()) { if (!_order.Ptr().OrderClose(_reason, _comment)) { - logger.AddLastError(__FUNCTION_LINE__, _order.Ptr().GetData().last_error); + GetLogger().AddLastError(__FUNCTION_LINE__, _order.Ptr().GetData().last_error); return -1; } order_last = _order; @@ -696,8 +699,8 @@ HistorySelect(0, TimeCurrent()); // Select history for access. if (_order.Ptr().IsOpen()) { if (_order.Ptr().GetRequest().type == _cmd) { if (!_order.Ptr().OrderClose(_reason, _comment)) { - logger.Error("Error while closing order!", __FUNCTION_LINE__, - StringFormat("Code: %d", _order.Ptr().GetData().last_error)); + GetLogger().Error("Error while closing order!", __FUNCTION_LINE__, + StringFormat("Code: %d", _order.Ptr().GetData().last_error)); return -1; } order_last = _order; @@ -729,7 +732,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. if (_order.Ptr().IsOpen()) { if (_order.Ptr().Get(_prop) == _value) { if (!_order.Ptr().OrderClose(_reason, _comment)) { - logger.AddLastError(__FUNCTION_LINE__, _order.Ptr().GetData().last_error); + GetLogger().AddLastError(__FUNCTION_LINE__, _order.Ptr().GetData().last_error); return -1; } order_last = _order; @@ -788,7 +791,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. ) { float _max_value1 = _max_pips > 0 ? CalcOrderSLTP(_max_pips, _cmd, _mode) : 0; float _max_value2 = tparams.risk_margin > 0 ? GetMaxSLTP(_cmd, _lot_size, _mode) : 0; - float _res = (float)chart.NormalizePrice(GetSaferSLTP(_value, _max_value1, _max_value2, _cmd, _mode)); + float _res = (float)GetChart().NormalizePrice(GetSaferSLTP(_value, _max_value1, _max_value2, _cmd, _mode)); // PrintFormat("%s/%s: Value: %g", EnumToString(_cmd), EnumToString(_mode), _value); // PrintFormat("%s/%s: Max value 1: %g", EnumToString(_cmd), EnumToString(_mode), _max_value1); // PrintFormat("%s/%s: Max value 2: %g", EnumToString(_cmd), EnumToString(_mode), _max_value2); @@ -803,12 +806,12 @@ HistorySelect(0, TimeCurrent()); // Select history for access. ENUM_ORDER_TYPE _cmd, // Order type (e.g. buy or sell). ENUM_ORDER_TYPE_VALUE _mode // Type of value (stop loss or take profit). ) { - double _price = _cmd == NULL ? Order::OrderOpenPrice() : chart.GetOpenOffer(_cmd); + double _price = _cmd == NULL ? Order::OrderOpenPrice() : GetChart().GetOpenOffer(_cmd); _cmd = _cmd == NULL ? Order::OrderType() : _cmd; // PrintFormat("#%d: %s/%s: %g (%g/%g) + %g * %g * %g = %g", Order::OrderTicket(), EnumToString(_cmd), - // EnumToString(_mode), _price, Bid, Ask, _value, chart.GetPipSize(), Order::OrderDirection(_cmd, _mode), - // chart.GetOpenOffer(_cmd) + _value * chart.GetPipSize() * Order::OrderDirection(_cmd, _mode)); - return _value > 0 ? float(_price + _value * chart.GetPipSize() * Order::OrderDirection(_cmd, _mode)) : 0; + // EnumToString(_mode), _price, Bid, Ask, _value, GetChart().GetPipSize(), Order::OrderDirection(_cmd, _mode), + // GetChart().GetOpenOffer(_cmd) + _value * GetChart().GetPipSize() * Order::OrderDirection(_cmd, _mode)); + return _value > 0 ? float(_price + _value * GetChart().GetPipSize() * Order::OrderDirection(_cmd, _mode)) : 0; } float CalcOrderSL(float _value, ENUM_ORDER_TYPE _cmd) { return CalcOrderSLTP(_value, _cmd, ORDER_TYPE_SL); } float CalcOrderTP(float _value, ENUM_ORDER_TYPE _cmd) { return CalcOrderSLTP(_value, _cmd, ORDER_TYPE_TP); } @@ -827,14 +830,14 @@ HistorySelect(0, TimeCurrent()); // Select history for access. */ float GetMaxSLTP(ENUM_ORDER_TYPE _cmd = NULL, float _lot_size = 0, ENUM_ORDER_TYPE_VALUE _mode = ORDER_TYPE_SL, float _risk_margin = 1.0) { - double _price = _cmd == NULL ? Order::OrderOpenPrice() : chart.GetOpenOffer(_cmd); + double _price = _cmd == NULL ? Order::OrderOpenPrice() : GetChart().GetOpenOffer(_cmd); // For the new orders, use the available margin for calculation, otherwise use the account balance. float _margin = Convert::MoneyToValue( (_cmd == NULL ? account.GetMarginAvail() : account.GetTotalBalance()) / 100 * _risk_margin, _lot_size, - chart.GetSymbol()); + GetChart().GetSymbol()); _cmd = _cmd == NULL ? Order::OrderType() : _cmd; // @fixme - // _lot_size = _lot_size <= 0 ? fmax(Order::OrderLots(), chart.GetVolumeMin()) : _lot_size; + // _lot_size = _lot_size <= 0 ? fmax(Order::OrderLots(), GetChart().GetVolumeMin()) : _lot_size; return (float)_price + GetTradeDistanceInValue() // + Convert::MoneyToValue(account.GetTotalBalance() / 100 * _risk_margin, _lot_size) @@ -864,7 +867,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. case ORDER_TYPE_TP: return NormalizeSLTP(_value1 < _value2 ? _value1 : _value2, _cmd, _mode); default: - logger.Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); + GetLogger().Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); } break; case ORDER_TYPE_SELL_LIMIT: @@ -875,11 +878,11 @@ HistorySelect(0, TimeCurrent()); // Select history for access. case ORDER_TYPE_TP: return NormalizeSLTP(_value1 > _value2 ? _value1 : _value2, _cmd, _mode); default: - logger.Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); + GetLogger().Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); } break; default: - logger.Error(StringFormat("Invalid order type: %s!", EnumToString(_cmd), __FUNCTION__)); + GetLogger().Error(StringFormat("Invalid order type: %s!", EnumToString(_cmd), __FUNCTION__)); } return EMPTY_VALUE; } @@ -917,7 +920,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. static long GetTradeDistanceInPts(string _symbol) { return fmax(SymbolInfoStatic::GetTradeStopsLevel(_symbol), SymbolInfoStatic::GetFreezeLevel(_symbol)); } - long GetTradeDistanceInPts() { return GetTradeDistanceInPts(chart.GetSymbol()); } + long GetTradeDistanceInPts() { return GetTradeDistanceInPts(GetChart().GetSymbol()); } /** * Get a market distance in pips. @@ -930,7 +933,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. unsigned int _pts_per_pip = SymbolInfoStatic::GetPointsPerPip(_symbol); return (double)(_pts_per_pip > 0 ? (GetTradeDistanceInPts(_symbol) / _pts_per_pip) : 0); } - double GetTradeDistanceInPips() { return GetTradeDistanceInPips(chart.GetSymbol()); } + double GetTradeDistanceInPips() { return GetTradeDistanceInPips(GetChart().GetSymbol()); } /** * Get a market gap in value. @@ -942,7 +945,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. static double GetTradeDistanceInValue(string _symbol) { return Trade::GetTradeDistanceInPts(_symbol) * SymbolInfoStatic::GetPointSize(_symbol); } - float GetTradeDistanceInValue() { return (float)GetTradeDistanceInValue(chart.GetSymbol()); } + float GetTradeDistanceInValue() { return (float)GetTradeDistanceInValue(GetChart().GetSymbol()); } /* Trend methods */ @@ -967,7 +970,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. double GetTrend(int method, ENUM_TIMEFRAMES _tf = NULL, bool simple = false) { static datetime _last_trend_check = 0; static double _last_trend = 0; - string symbol = chart.GetSymbol(); + string symbol = GetChart().GetSymbol(); if (_last_trend_check == Chart().GetBarTime(_tf)) { return _last_trend; } @@ -1083,7 +1086,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. } _last_trend = (bull - bear); _last_trend_check = Chart().GetBarTime(_tf, 0); - logger.Debug(StringFormat("%s: %g", __FUNCTION__, _last_trend)); + GetLogger().Debug(StringFormat("%s: %g", __FUNCTION__, _last_trend)); return _last_trend; } @@ -1144,7 +1147,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. // Check if auto trading is enabled. && (Terminal::IsRealtime() && !Terminal::IsExpertEnabled())); /* Chart checks */ - tstates.SetState(TRADE_STATE_BARS_NOT_ENOUGH, chart.GetBars() < tparams.GetBarsMin()); + tstates.SetState(TRADE_STATE_BARS_NOT_ENOUGH, GetChart().GetBars() < tparams.GetBarsMin()); /* Terminal checks */ tstates.SetState(TRADE_STATE_TRADE_NOT_ALLOWED, // Check if real trading is allowed. @@ -1163,7 +1166,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. for (int _bi = 0; _bi < sizeof(int) * 8; _bi++) { bool _enabled = tstates.CheckState(1 << _bi) > TradeStates::CheckState(1 << _bi, _states_prev); if (_enabled && (ENUM_TRADE_STATE)(1 << _bi) != TRADE_STATE_ORDERS_ACTIVE) { - logger.Warning(TradeStates::GetStateMessage((ENUM_TRADE_STATE)(1 << _bi)), GetName()); + GetLogger().Warning(TradeStates::GetStateMessage((ENUM_TRADE_STATE)(1 << _bi)), GetName()); } } _states_prev = tstates.GetStates(); @@ -1178,15 +1181,15 @@ HistorySelect(0, TimeCurrent()); // Select history for access. */ double NormalizeLots(double _lots, bool _ceil = false) { double _lot_size = _lots; - double _vol_min = chart.GetVolumeMin(); - double _vol_step = chart.GetVolumeStep() > 0.0 ? chart.GetVolumeStep() : _vol_min; + double _vol_min = GetChart().GetVolumeMin(); + double _vol_step = GetChart().GetVolumeStep() > 0.0 ? GetChart().GetVolumeStep() : _vol_min; if (_vol_step > 0) { // Related: http://forum.mql4.com/47988 double _precision = 1 / _vol_step; // Edge case when step is higher than minimum. _lot_size = _ceil ? ceil(_lots * _precision) / _precision : floor(_lots * _precision) / _precision; - double _min_lot = fmax(chart.GetVolumeMin(), chart.GetVolumeStep()); - _lot_size = fmin(fmax(_lot_size, _min_lot), chart.GetVolumeMax()); + double _min_lot = fmax(GetChart().GetVolumeMin(), GetChart().GetVolumeStep()); + _lot_size = fmin(fmax(_lot_size, _min_lot), GetChart().GetVolumeMax()); } return NormalizeDouble(_lot_size, Math::FloatDigits(_vol_min)); } @@ -1203,12 +1206,12 @@ HistorySelect(0, TimeCurrent()); // Select history for access. switch (_mode) { // Bid - StopLoss >= SYMBOL_TRADE_STOPS_LEVEL (minimum trade distance) case ORDER_TYPE_SL: - return fmin(_value, chart.GetBid() - GetTradeDistanceInValue()); + return fmin(_value, GetChart().GetBid() - GetTradeDistanceInValue()); // TakeProfit - Bid >= SYMBOL_TRADE_STOPS_LEVEL (minimum trade distance) case ORDER_TYPE_TP: - return fmax(_value, chart.GetBid() + GetTradeDistanceInValue()); + return fmax(_value, GetChart().GetBid() + GetTradeDistanceInValue()); default: - logger.Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); + GetLogger().Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); } break; // Selling is done at the Bid price. @@ -1218,24 +1221,24 @@ HistorySelect(0, TimeCurrent()); // Select history for access. switch (_mode) { // StopLoss - Ask >= SYMBOL_TRADE_STOPS_LEVEL (minimum trade distance) case ORDER_TYPE_SL: - return fmax(_value, chart.GetAsk() + GetTradeDistanceInValue()); + return fmax(_value, GetChart().GetAsk() + GetTradeDistanceInValue()); // Ask - TakeProfit >= SYMBOL_TRADE_STOPS_LEVEL (minimum trade distance) case ORDER_TYPE_TP: - return fmin(_value, chart.GetAsk() - GetTradeDistanceInValue()); + return fmin(_value, GetChart().GetAsk() - GetTradeDistanceInValue()); default: - logger.Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); + GetLogger().Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); } break; default: - logger.Error(StringFormat("Invalid order type: %s!", EnumToString(_cmd), __FUNCTION__)); + GetLogger().Error(StringFormat("Invalid order type: %s!", EnumToString(_cmd), __FUNCTION__)); } return NULL; } double NormalizeSL(double _value, ENUM_ORDER_TYPE _cmd) { - return chart.NormalizePrice(NormalizeSLTP(_value, _cmd, ORDER_TYPE_SL)); + return GetChart().NormalizePrice(NormalizeSLTP(_value, _cmd, ORDER_TYPE_SL)); } double NormalizeTP(double _value, ENUM_ORDER_TYPE _cmd) { - return chart.NormalizePrice(NormalizeSLTP(_value, _cmd, ORDER_TYPE_TP)); + return GetChart().NormalizePrice(NormalizeSLTP(_value, _cmd, ORDER_TYPE_TP)); } /* Validation methods */ @@ -1271,7 +1274,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. // SL-Ask >= StopLevel && Ask-TP >= StopLevel // OpenPrice-Ask >= StopLevel && OpenPrice-SL >= StopLevel && TP-OpenPrice >= StopLevel // PrintFormat("%g > %g", fmin(fabs(GetBid() - price), fabs(GetAsk() - price)), distance); - return price > 0 && fmin(fabs(chart.GetBid() - price), fabs(chart.GetAsk() - price)) > distance; + return price > 0 && fmin(fabs(GetChart().GetBid() - price), fabs(GetChart().GetAsk() - price)) > distance; default: return (true); } @@ -1283,8 +1286,8 @@ HistorySelect(0, TimeCurrent()); // Select history for access. bool IsValidOrderSL(double _value, ENUM_ORDER_TYPE _cmd, double _value_prev = WRONG_VALUE, bool _locked = false) { bool _is_valid = _value >= 0 && _value != _value_prev; double _min_distance = GetTradeDistanceInPips(); - double _price = chart.GetOpenOffer(_cmd); - unsigned int _digits = chart.GetDigits(); + double _price = GetChart().GetOpenOffer(_cmd); + unsigned int _digits = GetChart().GetDigits(); switch (_cmd) { case ORDER_TYPE_BUY: _is_valid &= _value < _price && Convert::GetValueDiffInPips(_price, _value, true, _digits) > _min_distance; @@ -1329,10 +1332,10 @@ HistorySelect(0, TimeCurrent()); // Select history for access. * @see: https://www.mql5.com/en/articles/2555#invalid_SL_TP_for_position */ double IsValidOrderSLTP(ENUM_ORDER_TYPE _cmd, double sl, double tp) { - double ask = chart.GetAsk(); - double bid = chart.GetBid(); - double openprice = chart.GetOpenOffer(_cmd); - double closeprice = chart.GetCloseOffer(_cmd); + double ask = GetChart().GetAsk(); + double bid = GetChart().GetBid(); + double openprice = GetChart().GetOpenOffer(_cmd); + double closeprice = GetChart().GetCloseOffer(_cmd); // The minimum distance of SYMBOL_TRADE_STOPS_LEVEL taken into account. double distance = GetTradeDistanceInValue(); // bool result; @@ -1400,8 +1403,8 @@ HistorySelect(0, TimeCurrent()); // Select history for access. bool IsValidOrderTP(double _value, ENUM_ORDER_TYPE _cmd, double _value_prev = WRONG_VALUE, bool _locked = false) { bool _is_valid = _value >= 0 && _value != _value_prev; double _min_distance = GetTradeDistanceInPips(); - double _price = chart.GetOpenOffer(_cmd); - unsigned int _digits = chart.GetDigits(); + double _price = GetChart().GetOpenOffer(_cmd); + unsigned int _digits = GetChart().GetDigits(); switch (_cmd) { case ORDER_TYPE_BUY: _is_valid &= _value > _price && Convert::GetValueDiffInPips(_value, _price, true, _digits) > _min_distance; @@ -1437,7 +1440,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. * _order Order instance of order which got opened. */ virtual void OnOrderOpen(const Order &_order) { - if (logger.GetLevel() >= V_INFO) { + if (GetLogger().GetLevel() >= V_INFO) { // logger.Info(_order.ToString(), (string)_order.GetTicket()); // @fixme ResetLastError(); // @fixme: Error 69539 } @@ -1477,7 +1480,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. // case TRADE_ORDER_CONDS_IN_TREND: // case TRADE_ORDER_CONDS_IN_TREND_NOT: default: - logger.Error(StringFormat("Invalid trade condition: %s!", EnumToString(_cond), __FUNCTION_LINE__)); + GetLogger().Error(StringFormat("Invalid trade condition: %s!", EnumToString(_cond), __FUNCTION_LINE__)); return false; } } @@ -1530,7 +1533,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. tstates.AddState((unsigned int)_args[0].integer_value); return GetLastError() == ERR_NO_ERROR; default: - logger.Error(StringFormat("Invalid trade action: %s!", EnumToString(_action), __FUNCTION_LINE__)); + GetLogger().Error(StringFormat("Invalid trade action: %s!", EnumToString(_action), __FUNCTION_LINE__)); return false; } } @@ -1549,14 +1552,14 @@ HistorySelect(0, TimeCurrent()); // Select history for access. /* Class handlers */ /** - * Returns pointer to Log class. + * Returns pointer to Chart class. */ - Chart *GetChart() { return GetPointer(chart); } + Chart *GetChart() { return chart.Ptr(); } /** * Returns pointer to Log class. */ - Log *GetLogger() { return GetPointer(logger); } + Log *GetLogger() { return logger.Ptr(); } /* Serializers */