diff --git a/Log.mqh b/Log.mqh index cf4cb64ab..28dff8671 100644 --- a/Log.mqh +++ b/Log.mqh @@ -256,7 +256,7 @@ class Log : public Object { } */ - virtual const string ToString() { + string ToString() override { string result; unsigned int lid; diff --git a/Market.mqh b/Market.mqh index c5945335b..99ef27de3 100644 --- a/Market.mqh +++ b/Market.mqh @@ -245,7 +245,7 @@ class Market : public SymbolInfo { /** * Returns Market data in textual representation. */ - string const ToString() { + string ToString() override { return StringFormat(string("Pip digits/value: %d/%g, Spread: %d pts (%g pips; %.4f%%), Pts/pip: %d, ") + "Volume digits: %d, " + "Delta: %g, Last change: %g pips", GetPipDigits(), GetPipValue(), GetSpreadInPts(), GetSpreadInPips(), GetSpreadInPct(), diff --git a/Object.mqh b/Object.mqh index d0dd1fadb..b3d471a31 100644 --- a/Object.mqh +++ b/Object.mqh @@ -100,12 +100,12 @@ class Object : public Dynamic { /** * Returns text representation of the object. */ - virtual const string ToString() { return StringFormat("[Object #%04x]", GetPointer(this)); } + virtual string ToString() { return StringFormat("[Object #%04x]", GetPointer(this)); } /** * Returns text representation of the object. */ - virtual const string ToJSON() { return StringFormat("{ \"type\": \"%s\" }", typename(this)); } + virtual string ToJSON() { return StringFormat("{ \"type\": \"%s\" }", typename(this)); } /** * Safely delete the object. diff --git a/Order.mqh b/Order.mqh index bdc16c441..53ac98f4f 100644 --- a/Order.mqh +++ b/Order.mqh @@ -2792,7 +2792,7 @@ class Order : public SymbolInfo { /** * Returns order details in text. */ - string const ToString() { + string ToString() override { SerializerConverter stub(SerializerConverter::MakeStubObject(SERIALIZER_FLAG_SKIP_HIDDEN)); return SerializerConverter::FromObject(THIS_REF, SERIALIZER_FLAG_SKIP_HIDDEN) .ToString(SERIALIZER_FLAG_SKIP_HIDDEN, &stub); diff --git a/Platform.h b/Platform.h index 6e3cbf395..d49172937 100644 --- a/Platform.h +++ b/Platform.h @@ -289,6 +289,8 @@ class Platform { props.vol_digits = SymbolInfoStatic::GetVolumeDigits(_symbol); props.vol_min = SymbolInfoStatic::GetVolumeMin(_symbol); props.vol_max = SymbolInfoStatic::GetVolumeMax(_symbol); + props.vol_step = SymbolInfoStatic::GetVolumeStep(_symbol); + props.point_size = SymbolInfoStatic::GetPointSize(_symbol); #endif return props; } diff --git a/SymbolInfo.mqh b/SymbolInfo.mqh index 130b97201..2139be76d 100644 --- a/SymbolInfo.mqh +++ b/SymbolInfo.mqh @@ -513,7 +513,7 @@ class SymbolInfo : public Object { /** * Returns symbol information in string format. */ - const string ToString() { + string ToString() override { return StringFormat( string("Symbol: %s, Last Ask/Bid: %g/%g, Last Price/Session Volume: %d/%g, Point size: %g, Pip size: %g, ") + "Tick size: %g (%g pts), Tick value: %g (%g/%g), " + "Digits: %d, Spread: %d pts, Trade stops level: %d, " + diff --git a/SymbolInfo.struct.h b/SymbolInfo.struct.h index 15e4d8a91..0a7f6e20d 100644 --- a/SymbolInfo.struct.h +++ b/SymbolInfo.struct.h @@ -79,6 +79,10 @@ struct SymbolInfoProp { unsigned int vol_digits; // Volume digits. double vol_min; // Minimum volume for a deal. double vol_max; // Maximum volume for a deal. + double vol_step; // Volume step. + double point_size; // Point size. + double tick_size; // Minimal price change. + // Constructors. SymbolInfoProp() : initialized(false) {} SymbolInfoProp(const SymbolInfoProp& _sip) { @@ -90,6 +94,9 @@ struct SymbolInfoProp { vol_digits = _sip.vol_digits; vol_min = _sip.vol_min; vol_max = _sip.vol_max; + vol_step = _sip.vol_step; + point_size = _sip.point_size; + tick_size = _sip.tick_size; } // Getters. double GetPipValue() { return pip_value; } @@ -99,6 +106,26 @@ struct SymbolInfoProp { unsigned int GetVolumeDigits() { return vol_digits; } double GetVolumeMin() { return vol_min; } double GetVolumeMax() { return vol_max; } + double GetVolumeStep() { return vol_step; } + double GetPointSize() { return point_size; } + double GetTickSize() { return tick_size; } + + /** + * Normalize price value. + * + * Make sure that the price is a multiple of ticksize. + */ + double NormalizePrice(double p) { + // See: http://forum.mql4.com/47988 + // http://forum.mql4.com/43064#515262 zzuegg reports for non-currency DE30: + // - MarketInfo(chart.symbol,MODE_TICKSIZE) returns 0.5 + // - MarketInfo(chart.symbol,MODE_DIGITS) return 1 + // - Point = 0.1 + // Rare fix when a change in tick size leads to a change in tick value. + double _result = round(p / GetPointSize()) * GetTickSize(); + _result = NormalizeDouble(_result, GetDigits()); + return _result; + } // Serializers. void SerializeStub(int _n1 = 1, int _n2 = 1, int _n3 = 1, int _n4 = 1, int _n5 = 1) {} @@ -123,5 +150,8 @@ SerializerNodeType SymbolInfoProp::Serialize(Serializer& _s) { _s.Pass(THIS_REF, "vol_digits", vol_digits); _s.Pass(THIS_REF, "vol_min", vol_min); _s.Pass(THIS_REF, "vol_max", vol_max); + _s.Pass(THIS_REF, "vol_step", vol_step); + _s.Pass(THIS_REF, "point_size", point_size); + _s.Pass(THIS_REF, "tick_size", tick_size); return SerializerNodeObject; } diff --git a/Timer.mqh b/Timer.mqh index 59f291ca3..ec86c1a7e 100644 --- a/Timer.mqh +++ b/Timer.mqh @@ -132,7 +132,7 @@ class Timer : public Object { /** * Print timer times. */ - virtual const string ToString() { + string ToString() override { return StringFormat("%s(%d)=%d-%dms,med=%dms,sum=%dms", GetName(), ArraySize(this PTR_DEREF data), GetMin(), GetMax(), GetMedian(), GetSum()); } diff --git a/Trade.mqh b/Trade.mqh index 422587479..b4da0093a 100644 --- a/Trade.mqh +++ b/Trade.mqh @@ -126,7 +126,7 @@ class Trade : public Taskable { */ template T Get(ENUM_CHART_PARAM _param) { - return GetSource().Get(_param); + return GetSource() PTR_DEREF Get(_param); } /** @@ -204,9 +204,8 @@ class Trade : public Taskable { _request.price = GetSource() PTR_DEREF GetOpenOffer(_type); _request.type = _type; _request.type_filling = Order::GetOrderFilling(_request.symbol); - // _request.volume = _volume > 0 ? _volume : tparams.Get(TRADE_PARAM_LOT_SIZE); - _request.volume = 0.01; - // @todo: NormalizeLots(). + _request.volume = _volume > 0 ? _volume : tparams.Get(TRADE_PARAM_LOT_SIZE); + // @fixit Revert previous code. // DebugBreak(); // _request.volume = NormalizeLots(fmax(_request.volume, SymbolInfoStatic::GetVolumeMin(_request.symbol))); // _request.volume = GetSource() PTR_DEREF GetTickVolume(); @@ -226,7 +225,9 @@ class Trade : public Taskable { /** * Sets default name of trade instance. */ - void SetName() { name = StringFormat("%s@%s", GetSource().GetSymbol(), ChartTf::TfToString(GetSource().GetTf())); } + void SetName() { + name = StringFormat("%s@%s", GetSource() PTR_DEREF GetSymbol(), ChartTf::TfToString(GetSource() PTR_DEREF GetTf())); + } /** * Sets name of trade instance. @@ -243,9 +244,9 @@ class Trade : public Taskable { /* bool IsPeak(ENUM_ORDER_TYPE _cmd, int _shift = 0) { bool _result = false; - double _high = GetSource().GetHigh(_shift + 1); - double _low = GetSource().GetLow(_shift + 1); - double _open = GetSource().GetOpenOffer(_cmd); + double _high = GetSource() PTR_DEREF GetHigh(_shift + 1); + double _low = GetSource() PTR_DEREF GetLow(_shift + 1); + double _open = GetSource() PTR_DEREF GetOpenOffer(_cmd); if (_low != _high) { switch (_cmd) { case ORDER_TYPE_BUY: @@ -266,17 +267,17 @@ class Trade : public Taskable { /* bool IsPivot(ENUM_ORDER_TYPE _cmd, int _shift = 0) { bool _result = false; - double _high = GetSource().GetHigh(_shift + 1); - double _low = GetSource().GetLow(_shift + 1); - double _close = GetSource().GetClose(_shift + 1); + double _high = GetSource() PTR_DEREF GetHigh(_shift + 1); + double _low = GetSource() PTR_DEREF GetLow(_shift + 1); + double _close = GetSource() PTR_DEREF GetClose(_shift + 1); if (_close > 0 && _low != _high) { float _pp = (float)(_high + _low + _close) / 3; switch (_cmd) { case ORDER_TYPE_BUY: - _result = GetSource().GetOpenOffer(_cmd) > _pp; + _result = GetSource() PTR_DEREF GetOpenOffer(_cmd) > _pp; break; case ORDER_TYPE_SELL: - _result = GetSource().GetOpenOffer(_cmd) < _pp; + _result = GetSource() PTR_DEREF GetOpenOffer(_cmd) < _pp; break; } } @@ -303,7 +304,7 @@ class Trade : public Taskable { /** * Check if trading instance is valid. */ - bool IsValid() { return GetSource().IsValid(); } + bool IsValid() { return GetSource() PTR_DEREF IsValid(); } /** * Check if this trade instance has active orders. @@ -318,7 +319,7 @@ class Trade : public Taskable { Ref _order = order_last; if (_order.IsSet() && _order.Ptr().Get(ORDER_TYPE) == _cmd && - _order.Ptr().Get(ORDER_TIME_SETUP) > GetSource().GetBarTime()) { + _order.Ptr().Get(ORDER_TIME_SETUP) > GetSource() PTR_DEREF GetBarTime()) { _result |= true; } @@ -327,8 +328,8 @@ class Trade : public Taskable { _order = iter.Value(); if (_order.Ptr().Get(ORDER_TYPE) == _cmd) { long _time_opened = _order.Ptr().Get(ORDER_TIME_SETUP); - _result |= _shift > 0 && _time_opened < GetSource().GetBarTime(_shift - 1); - _result |= _time_opened >= GetSource().GetBarTime(_shift); + _result |= _shift > 0 && _time_opened < GetSource() PTR_DEREF GetBarTime(_shift - 1); + _result |= _time_opened >= GetSource() PTR_DEREF GetBarTime(_shift); if (_result) { break; } @@ -346,7 +347,7 @@ class Trade : public Taskable { bool _result = false; Ref _order = order_last; OrderData _odata; - double _price_curr = GetSource().GetOpenOffer(_cmd); + double _price_curr = GetSource() PTR_DEREF GetOpenOffer(_cmd); if (_order.IsSet() && _order.Ptr().IsOpen()) { if (_odata.Get(ORDER_TYPE) == _cmd) { @@ -392,7 +393,7 @@ class Trade : public Taskable { bool _result = false; Ref _order = order_last; OrderData _odata; - double _price_curr = GetSource().GetOpenOffer(_cmd); + double _price_curr = GetSource() PTR_DEREF GetOpenOffer(_cmd); if (_order.IsSet()) { _result = _odata.Get(ORDER_TYPE) != _cmd; @@ -507,7 +508,7 @@ class Trade : public Taskable { #endif } float GetMarginRequired(ENUM_ORDER_TYPE _cmd = ORDER_TYPE_BUY) { - return (float)GetMarginRequired(GetSource().GetSymbol(), _cmd); + return (float)GetMarginRequired(GetSource() PTR_DEREF GetSymbol(), _cmd); } /* Lot size methods */ @@ -529,10 +530,10 @@ class Trade : public Taskable { 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 - GetSource().GetOpenOffer(_cmd)) / GetSource().GetTickSize(); + double _ticks = fabs(_sl - GetSource() PTR_DEREF GetOpenOffer(_cmd)) / GetSource() PTR_DEREF GetTickSize(); double lot_size1 = fmin(_sl, _ticks) > 0 ? risk_amount / (_sl * (_ticks / 100.0)) : 1; - lot_size1 *= GetSource().GetVolumeMin(); - // double lot_size2 = 1 / (GetSource().GetTickValue() * sl / risk_margin); + lot_size1 *= GetSource() PTR_DEREF GetVolumeMin(); + // double lot_size2 = 1 / (GetSource() PTR_DEREF GetTickValue() * sl / risk_margin); // PrintFormat("SL=%g: 1 = %g, 2 = %g", sl, lot_size1, lot_size2); return NormalizeLots(lot_size1); } @@ -582,7 +583,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. Print(__FUNCTION__, ": Error in history!"); break; } - if (deal.Symbol() != GetSource().GetSymbol()) continue; + if (deal.Symbol() != GetSource() PTR_DEREF GetSymbol()) continue; double profit = deal.Profit(); */ double profit = 0; @@ -629,7 +630,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. unsigned int _method = 0 // Method of calculation (0-3). ) { float _avail_amount = _method % 2 == 0 ? account.GetMarginAvail() : account.GetTotalBalance(); - float _lot_size_min = (float)GetSource().GetVolumeMin(); + float _lot_size_min = (float)GetSource() PTR_DEREF GetVolumeMin(); float _lot_size = _lot_size_min; float _risk_value = (float)account.GetLeverage(); if (_method == 0 || _method == 1) { @@ -640,12 +641,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, GetSource().GetSymbol()); - float _tick_value = GetSource().GetTickSize(); + float _money_value = Convert::MoneyToValue(_risk_amount, _lot_size_min, GetSource() PTR_DEREF GetSymbol()); + float _tick_value = GetSource() PTR_DEREF GetTickSize(); // @todo: Improves calculation logic. _lot_size = _money_value * _tick_value * _risk_ratio / _risk_value / 100; } - _lot_size = (float)fmin(_lot_size, GetSource().GetVolumeMax()); + _lot_size = (float)fmin(_lot_size, GetSource() PTR_DEREF GetVolumeMax()); return (float)NormalizeLots(_lot_size); } */ @@ -1037,7 +1038,8 @@ 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)GetSource().NormalizePrice(GetSaferSLTP(_value, _max_value1, _max_value2, _cmd, _mode)); + float _res = (float)GetSource() PTR_DEREF 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); @@ -1054,14 +1056,14 @@ 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() : GetSource().GetOpenOffer(_cmd); + double _price = _cmd == NULL ? Order::OrderOpenPrice() : GetSource() PTR_DEREF 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, GetSource().GetPipSize(), Order::OrderDirection(_cmd, + // EnumToString(_mode), _price, Bid, Ask, _value, GetSource() PTR_DEREF GetPipSize(), Order::OrderDirection(_cmd, _mode), - // GetSource().GetOpenOffer(_cmd) + _value * GetSource().GetPipSize() * Order::OrderDirection(_cmd, - _mode)); return _value > 0 ? float(_price + _value * GetSource().GetPipSize() * Order::OrderDirection(_cmd, - _mode)) : 0; + // GetSource() PTR_DEREF GetOpenOffer(_cmd) + _value * GetSource() PTR_DEREF GetPipSize() * + Order::OrderDirection(_cmd, _mode)); return _value > 0 ? float(_price + _value * GetSource() PTR_DEREF GetPipSize() * + Order::OrderDirection(_cmd, _mode)) : 0; } */ /* @@ -1086,14 +1088,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() : GetSource().GetOpenOffer(_cmd); + double _price = _cmd == NULL ? Order::OrderOpenPrice() : GetSource() PTR_DEREF 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, - GetSource().GetSymbol()); + GetSource() PTR_DEREF GetSymbol()); _cmd = _cmd == NULL ? Order::OrderType() : _cmd; // @fixme - // _lot_size = _lot_size <= 0 ? fmax(Order::OrderLots(), GetSource().GetVolumeMin()) : _lot_size; + // _lot_size = _lot_size <= 0 ? fmax(Order::OrderLots(), GetSource() PTR_DEREF GetVolumeMin()) : _lot_size; return (float)_price + GetTradeDistanceInValue() // + Convert::MoneyToValue(account.GetTotalBalance() / 100 * _risk_margin, _lot_size) @@ -1181,7 +1183,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(GetSource().GetSymbol()); } + long GetTradeDistanceInPts() { return GetTradeDistanceInPts(GetSource() PTR_DEREF GetSymbol()); } /** * Get a market distance in pips. @@ -1194,7 +1196,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(GetSource().GetSymbol()); } + double GetTradeDistanceInPips() { return GetTradeDistanceInPips(GetSource() PTR_DEREF GetSymbol()); } /** * Get a market gap in value. @@ -1206,7 +1208,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(GetSource().GetSymbol()); } + float GetTradeDistanceInValue() { return (float)GetTradeDistanceInValue(GetSource() PTR_DEREF GetSymbol()); } /* Trend methods */ @@ -1232,8 +1234,8 @@ HistorySelect(0, TimeCurrent()); // Select history for access. double GetTrend(int method, bool simple = false) { static datetime _last_trend_check = 0; static double _last_trend = 0; - string symbol = GetSource().GetSymbol(); - if (_last_trend_check == GetSource().GetTime()) { + string symbol = GetSource() PTR_DEREF GetSymbol(); + if (_last_trend_check == GetSource() PTR_DEREF GetTime()) { return _last_trend; } double bull = 0, bear = 0; @@ -1241,113 +1243,105 @@ HistorySelect(0, TimeCurrent()); // Select history for access. if (simple && method != 0) { if ((method & 1) != 0) { - if (GetSource().GetOpen(PERIOD_MN1, 0) > GetSource().GetClose(PERIOD_MN1, 1)) bull++; - if (GetSource().GetOpen(PERIOD_MN1, 0) < GetSource().GetClose(PERIOD_MN1, 1)) bear++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_MN1, 0) > GetSource() PTR_DEREF GetClose(PERIOD_MN1, 1)) bull++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_MN1, 0) < GetSource() PTR_DEREF GetClose(PERIOD_MN1, 1)) bear++; } if ((method & 2) != 0) { - if (GetSource().GetOpen(PERIOD_W1, 0) > GetSource().GetClose(PERIOD_W1, 1)) bull++; - if (GetSource().GetOpen(PERIOD_W1, 0) < GetSource().GetClose(PERIOD_W1, 1)) bear++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_W1, 0) > GetSource() PTR_DEREF GetClose(PERIOD_W1, 1)) bull++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_W1, 0) < GetSource() PTR_DEREF GetClose(PERIOD_W1, 1)) bear++; } if ((method & 4) != 0) { - if (GetSource().GetOpen(PERIOD_D1, 0) > GetSource().GetClose(PERIOD_D1, 1)) bull++; - if (GetSource().GetOpen(PERIOD_D1, 0) < GetSource().GetClose(PERIOD_D1, 1)) bear++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_D1, 0) > GetSource() PTR_DEREF GetClose(PERIOD_D1, 1)) bull++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_D1, 0) < GetSource() PTR_DEREF GetClose(PERIOD_D1, 1)) bear++; } if ((method & 8) != 0) { - if (GetSource().GetOpen(PERIOD_H4, 0) > GetSource().GetClose(PERIOD_H4, 1)) bull++; - if (GetSource().GetOpen(PERIOD_H4, 0) < GetSource().GetClose(PERIOD_H4, 1)) bear++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_H4, 0) > GetSource() PTR_DEREF GetClose(PERIOD_H4, 1)) bull++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_H4, 0) < GetSource() PTR_DEREF GetClose(PERIOD_H4, 1)) bear++; } if ((method & 16) != 0) { - if (GetSource().GetOpen(PERIOD_H1, 0) > GetSource().GetClose(PERIOD_H1, 1)) bull++; - if (GetSource().GetOpen(PERIOD_H1, 0) < GetSource().GetClose(PERIOD_H1, 1)) bear++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_H1, 0) > GetSource() PTR_DEREF GetClose(PERIOD_H1, 1)) bull++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_H1, 0) < GetSource() PTR_DEREF GetClose(PERIOD_H1, 1)) bear++; } if ((method & 32) != 0) { - if (GetSource().GetOpen(PERIOD_M30, 0) > GetSource().GetClose(PERIOD_M30, 1)) bull++; - if (GetSource().GetOpen(PERIOD_M30, 0) < GetSource().GetClose(PERIOD_M30, 1)) bear++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_M30, 0) > GetSource() PTR_DEREF GetClose(PERIOD_M30, 1)) bull++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_M30, 0) < GetSource() PTR_DEREF GetClose(PERIOD_M30, 1)) bear++; } if ((method & 64) != 0) { - if (GetSource().GetOpen(PERIOD_M15, 0) > GetSource().GetClose(PERIOD_M15, 1)) bull++; - if (GetSource().GetOpen(PERIOD_M15, 0) < GetSource().GetClose(PERIOD_M15, 1)) bear++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_M15, 0) > GetSource() PTR_DEREF GetClose(PERIOD_M15, 1)) bull++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_M15, 0) < GetSource() PTR_DEREF GetClose(PERIOD_M15, 1)) bear++; } if ((method & 128) != 0) { - if (GetSource().GetOpen(PERIOD_M5, 0) > GetSource().GetClose(PERIOD_M5, 1)) bull++; - if (GetSource().GetOpen(PERIOD_M5, 0) < GetSource().GetClose(PERIOD_M5, 1)) bear++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_M5, 0) > GetSource() PTR_DEREF GetClose(PERIOD_M5, 1)) bull++; + if (GetSource() PTR_DEREF GetOpen(PERIOD_M5, 0) < GetSource() PTR_DEREF GetClose(PERIOD_M5, 1)) bear++; } - // if (GetSource().GetOpen(PERIOD_H12, 0) > GetSource().GetClose(PERIOD_H12, 1)) bull++; - // if (GetSource().GetOpen(PERIOD_H12, 0) < GetSource().GetClose(PERIOD_H12, 1)) bear++; - // if (GetSource().GetOpen(PERIOD_H8, 0) > GetSource().GetClose(PERIOD_H8, 1)) bull++; - // if (GetSource().GetOpen(PERIOD_H8, 0) < GetSource().GetClose(PERIOD_H8, 1)) bear++; - // if (GetSource().GetOpen(PERIOD_H6, 0) > GetSource().GetClose(PERIOD_H6, 1)) bull++; - // if (GetSource().GetOpen(PERIOD_H6, 0) < GetSource().GetClose(PERIOD_H6, 1)) bear++; - // if (GetSource().GetOpen(PERIOD_H2, 0) > GetSource().GetClose(PERIOD_H2, 1)) bull++; - // if (GetSource().GetOpen(PERIOD_H2, 0) < GetSource().GetClose(PERIOD_H2, 1)) bear++; + // if (GetSource() PTR_DEREF GetOpen(PERIOD_H12, 0) > GetSource() PTR_DEREF GetClose(PERIOD_H12, 1)) bull++; + // if (GetSource() PTR_DEREF GetOpen(PERIOD_H12, 0) < GetSource() PTR_DEREF GetClose(PERIOD_H12, 1)) bear++; + // if (GetSource() PTR_DEREF GetOpen(PERIOD_H8, 0) > GetSource() PTR_DEREF GetClose(PERIOD_H8, 1)) bull++; + // if (GetSource() PTR_DEREF GetOpen(PERIOD_H8, 0) < GetSource() PTR_DEREF GetClose(PERIOD_H8, 1)) bear++; + // if (GetSource() PTR_DEREF GetOpen(PERIOD_H6, 0) > GetSource() PTR_DEREF GetClose(PERIOD_H6, 1)) bull++; + // if (GetSource() PTR_DEREF GetOpen(PERIOD_H6, 0) < GetSource() PTR_DEREF GetClose(PERIOD_H6, 1)) bear++; + // if (GetSource() PTR_DEREF GetOpen(PERIOD_H2, 0) > GetSource() PTR_DEREF GetClose(PERIOD_H2, 1)) bull++; + // if (GetSource() PTR_DEREF GetOpen(PERIOD_H2, 0) < GetSource() PTR_DEREF GetClose(PERIOD_H2, 1)) bear++; } else if (method != 0) { if ((method % 1) == 0) { for (_counter = 0; _counter < 3; _counter++) { - if (GetSource().GetOpen(PERIOD_MN1, _counter) > GetSource().GetClose(PERIOD_MN1, _counter + 1)) - bull += 30; - else if (GetSource().GetOpen(PERIOD_MN1, _counter) < GetSource().GetClose(PERIOD_MN1, _counter + - 1)) bear += 30; + if (GetSource() PTR_DEREF GetOpen(PERIOD_MN1, _counter) > GetSource() PTR_DEREF GetClose(PERIOD_MN1, _counter + + 1)) bull += 30; else if (GetSource() PTR_DEREF GetOpen(PERIOD_MN1, _counter) < GetSource() PTR_DEREF + GetClose(PERIOD_MN1, _counter + 1)) bear += 30; } } if ((method % 2) == 0) { for (_counter = 0; _counter < 8; _counter++) { - if (GetSource().GetOpen(PERIOD_W1, _counter) > GetSource().GetClose(PERIOD_W1, _counter + 1)) - bull += 7; - else if (GetSource().GetOpen(PERIOD_W1, _counter) < GetSource().GetClose(PERIOD_W1, _counter + 1)) - bear += 7; + if (GetSource() PTR_DEREF GetOpen(PERIOD_W1, _counter) > GetSource() PTR_DEREF GetClose(PERIOD_W1, _counter + + 1)) bull += 7; else if (GetSource() PTR_DEREF GetOpen(PERIOD_W1, _counter) < GetSource() PTR_DEREF GetClose(PERIOD_W1, + _counter + 1)) bear += 7; } } if ((method % 4) == 0) { for (_counter = 0; _counter < 7; _counter++) { - if (GetSource().GetOpen(PERIOD_D1, _counter) > GetSource().GetClose(PERIOD_D1, _counter + 1)) - bull += 1440 / 1440; - else if (GetSource().GetOpen(PERIOD_D1, _counter) < GetSource().GetClose(PERIOD_D1, _counter + 1)) - bear += 1440 / 1440; + if (GetSource() PTR_DEREF GetOpen(PERIOD_D1, _counter) > GetSource() PTR_DEREF GetClose(PERIOD_D1, _counter + + 1)) bull += 1440 / 1440; else if (GetSource() PTR_DEREF GetOpen(PERIOD_D1, _counter) < GetSource() PTR_DEREF + GetClose(PERIOD_D1, _counter + 1)) bear += 1440 / 1440; } } if ((method % 8) == 0) { for (_counter = 0; _counter < 24; _counter++) { - if (GetSource().GetOpen(PERIOD_H4, _counter) > GetSource().GetClose(PERIOD_H4, _counter + 1)) - bull += 240 / 1440; - else if (GetSource().GetOpen(PERIOD_H4, _counter) < GetSource().GetClose(PERIOD_H4, _counter + 1)) - bear += 240 / 1440; + if (GetSource() PTR_DEREF GetOpen(PERIOD_H4, _counter) > GetSource() PTR_DEREF GetClose(PERIOD_H4, _counter + + 1)) bull += 240 / 1440; else if (GetSource() PTR_DEREF GetOpen(PERIOD_H4, _counter) < GetSource() PTR_DEREF + GetClose(PERIOD_H4, _counter + 1)) bear += 240 / 1440; } } if ((method % 16) == 0) { for (_counter = 0; _counter < 24; _counter++) { - if (GetSource().GetOpen(PERIOD_H1, _counter) > GetSource().GetClose(PERIOD_H1, _counter + 1)) - bull += 60 / 1440; - else if (GetSource().GetOpen(PERIOD_H1, _counter) < GetSource().GetClose(PERIOD_H1, _counter + 1)) - bear += 60 / 1440; + if (GetSource() PTR_DEREF GetOpen(PERIOD_H1, _counter) > GetSource() PTR_DEREF GetClose(PERIOD_H1, _counter + + 1)) bull += 60 / 1440; else if (GetSource() PTR_DEREF GetOpen(PERIOD_H1, _counter) < GetSource() PTR_DEREF + GetClose(PERIOD_H1, _counter + 1)) bear += 60 / 1440; } } if ((method % 32) == 0) { for (_counter = 0; _counter < 48; _counter++) { - if (GetSource().GetOpen(PERIOD_M30, _counter) > GetSource().GetClose(PERIOD_M30, _counter + 1)) - bull += 30 / 1440; - else if (GetSource().GetOpen(PERIOD_M30, _counter) < GetSource().GetClose(PERIOD_M30, _counter + - 1)) bear += 30 / 1440; + if (GetSource() PTR_DEREF GetOpen(PERIOD_M30, _counter) > GetSource() PTR_DEREF GetClose(PERIOD_M30, _counter + + 1)) bull += 30 / 1440; else if (GetSource() PTR_DEREF GetOpen(PERIOD_M30, _counter) < GetSource() PTR_DEREF + GetClose(PERIOD_M30, _counter + 1)) bear += 30 / 1440; } } if ((method % 64) == 0) { for (_counter = 0; _counter < 96; _counter++) { - if (GetSource().GetOpen(PERIOD_M15, _counter) > GetSource().GetClose(PERIOD_M15, _counter + 1)) - bull += 15 / 1440; - else if (GetSource().GetOpen(PERIOD_M15, _counter) < GetSource().GetClose(PERIOD_M15, _counter + - 1)) bear += 15 / 1440; + if (GetSource() PTR_DEREF GetOpen(PERIOD_M15, _counter) > GetSource() PTR_DEREF GetClose(PERIOD_M15, _counter + + 1)) bull += 15 / 1440; else if (GetSource() PTR_DEREF GetOpen(PERIOD_M15, _counter) < GetSource() PTR_DEREF + GetClose(PERIOD_M15, _counter + 1)) bear += 15 / 1440; } } if ((method % 128) == 0) { for (_counter = 0; _counter < 288; _counter++) { - if (GetSource().GetOpen(PERIOD_M5, _counter) > GetSource().GetClose(PERIOD_M5, _counter + 1)) - bull += 5 / 1440; - else if (GetSource().GetOpen(PERIOD_M5, _counter) < GetSource().GetClose(PERIOD_M5, _counter + 1)) - bear += 5 / 1440; + if (GetSource() PTR_DEREF GetOpen(PERIOD_M5, _counter) > GetSource() PTR_DEREF GetClose(PERIOD_M5, _counter + + 1)) bull += 5 / 1440; else if (GetSource() PTR_DEREF GetOpen(PERIOD_M5, _counter) < GetSource() PTR_DEREF + GetClose(PERIOD_M5, _counter + 1)) bear += 5 / 1440; } } } _last_trend = (bull - bear); - _last_trend_check = GetSource().GetBarTime(_tf, 0); + _last_trend_check = GetSource() PTR_DEREF GetBarTime(_tf, 0); logger.Debug(StringFormat("%s: %g", __FUNCTION__, _last_trend)); return _last_trend; } @@ -1416,9 +1410,10 @@ HistorySelect(0, TimeCurrent()); // Select history for access. && (Terminal::IsRealtime() && !Terminal::IsExpertEnabled())); /* Chart checks */ #ifdef __debug__ - Print("Trade: Bars in data source: ", GetSource().GetBars(), ", minimum required bars: ", tparams.GetBarsMin()); + Print("Trade: Bars in data source: ", GetSource() PTR_DEREF GetBars(), + ", minimum required bars: ", tparams.GetBarsMin()); #endif - tstates.SetState(TRADE_STATE_BARS_NOT_ENOUGH, GetSource().GetBars() < tparams.GetBarsMin()); + tstates.SetState(TRADE_STATE_BARS_NOT_ENOUGH, GetSource() PTR_DEREF GetBars() < tparams.GetBarsMin()); /* Terminal checks */ tstates.SetState(TRADE_STATE_TRADE_NOT_ALLOWED, // Check if real trading is allowed. @@ -1450,22 +1445,23 @@ HistorySelect(0, TimeCurrent()); // Select history for access. /** * Normalize lot size. */ - /* double NormalizeLots(double _lots, bool _ceil = false) { double _lot_size = _lots; - double _vol_min = GetSource().GetVolumeMin(); - double _vol_step = GetSource().GetVolumeStep() > 0.0 ? GetSource().GetVolumeStep() : _vol_min; + double _vol_min = GetSource() PTR_DEREF GetSymbolProps().GetVolumeMin(); + double _vol_step = GetSource() PTR_DEREF GetSymbolProps().GetVolumeStep() > 0.0 + ? GetSource() PTR_DEREF GetSymbolProps().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(GetSource().GetVolumeMin(), GetSource().GetVolumeStep()); - _lot_size = fmin(fmax(_lot_size, _min_lot), GetSource().GetVolumeMax()); + double _min_lot = fmax(GetSource() PTR_DEREF GetSymbolProps().GetVolumeMin(), + GetSource() PTR_DEREF GetSymbolProps().GetVolumeStep()); + _lot_size = fmin(fmax(_lot_size, _min_lot), GetSource() PTR_DEREF GetSymbolProps().GetVolumeMax()); } return NormalizeDouble(_lot_size, Math::FloatDigits(_vol_min)); } - */ /** * Normalize SL/TP values. @@ -1483,10 +1479,10 @@ 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, GetSource().GetBid() - GetTradeDistanceInValue()); + return fmin(_value, GetSource() PTR_DEREF GetBid() - GetTradeDistanceInValue()); // TakeProfit - Bid >= SYMBOL_TRADE_STOPS_LEVEL (minimum trade distance) case ORDER_TYPE_TP: - return fmax(_value, GetSource().GetBid() + GetTradeDistanceInValue()); + return fmax(_value, GetSource() PTR_DEREF GetBid() + GetTradeDistanceInValue()); default: logger.Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); } @@ -1498,10 +1494,10 @@ 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, GetSource().GetAsk() + GetTradeDistanceInValue()); + return fmax(_value, GetSource() PTR_DEREF GetAsk() + GetTradeDistanceInValue()); // Ask - TakeProfit >= SYMBOL_TRADE_STOPS_LEVEL (minimum trade distance) case ORDER_TYPE_TP: - return fmin(_value, GetSource().GetAsk() - GetTradeDistanceInValue()); + return fmin(_value, GetSource() PTR_DEREF GetAsk() - GetTradeDistanceInValue()); default: logger.Error(StringFormat("Invalid mode: %s!", EnumToString(_mode), __FUNCTION__)); } @@ -1513,11 +1509,15 @@ HistorySelect(0, TimeCurrent()); // Select history for access. } double NormalizeSL(double _value, ENUM_ORDER_TYPE _cmd) { - return _value > 0 ? GetSource() PTR_DEREF NormalizePrice(NormalizeSLTP(_value, _cmd, ORDER_TYPE_SL)) : 0; + return _value > 0 + ? GetSource() PTR_DEREF GetSymbolProps().NormalizePrice(NormalizeSLTP(_value, _cmd, ORDER_TYPE_SL)) + : 0; } double NormalizeTP(double _value, ENUM_ORDER_TYPE _cmd) { - return _value > 0 ? GetSource() PTR_DEREF NormalizePrice(NormalizeSLTP(_value, _cmd, ORDER_TYPE_TP)) : 0; + return _value > 0 + ? GetSource() PTR_DEREF GetSymbolProps().NormalizePrice(NormalizeSLTP(_value, _cmd, ORDER_TYPE_TP)) + : 0; } /* Validation methods */ @@ -1553,7 +1553,8 @@ 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(GetSource().GetBid() - price), fabs(GetSource().GetAsk() - price)) > distance; + return price > 0 && fmin(fabs(GetSource() PTR_DEREF GetBid() - price), + fabs(GetSource() PTR_DEREF GetAsk() - price)) > distance; default: return (true); } @@ -1614,10 +1615,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 = GetSource().GetAsk(); - double bid = GetSource().GetBid(); - double openprice = GetSource().GetOpenOffer(_cmd); - double closeprice = GetSource().GetCloseOffer(_cmd); + double ask = GetSource() PTR_DEREF GetAsk(); + double bid = GetSource() PTR_DEREF GetBid(); + double openprice = GetSource() PTR_DEREF GetOpenOffer(_cmd); + double closeprice = GetSource() PTR_DEREF GetCloseOffer(_cmd); // The minimum distance of SYMBOL_TRADE_STOPS_LEVEL taken into account. double distance = GetTradeDistanceInValue(); // bool result; @@ -1688,8 +1689,8 @@ HistorySelect(0, TimeCurrent()); // Select history for access. return _is_valid; } double _min_distance = GetTradeDistanceInPips(); - double _price = GetSource().GetOpenOffer(_cmd); - unsigned int _digits = GetSource().GetSymbolProps().GetDigits(); + double _price = GetSource() PTR_DEREF GetOpenOffer(_cmd); + unsigned int _digits = GetSource() PTR_DEREF GetSymbolProps().GetDigits(); switch (_cmd) { case ORDER_TYPE_BUY: _is_valid &= _value > _price && Convert::GetValueDiffInPips(_value, _price, true, _digits) > _min_distance; @@ -2025,11 +2026,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. /** * Returns textual representation of the Trade class. */ - string ToString() const { - // @todo - // return StringFormat("Margin required: %g/lot", GetMarginRequired()); - return ""; - } + string ToString() { return SerializerConverter::FromObject(THIS_REF).ToString(); } /* Class handlers */ @@ -2059,12 +2056,16 @@ HistorySelect(0, TimeCurrent()); // Select history for access. /* Serializers */ + SERIALIZER_EMPTY_STUB + /** * Returns serialized representation of the object instance. */ SerializerNodeType Serialize(Serializer &_s) { - // ChartEntry _centry = GetEntry(); - // _s.PassStruct(THIS_REF, "chart-entry", _centry, SERIALIZER_FIELD_FLAG_DYNAMIC); + _s.PassStruct(THIS_REF, "trade-params", tparams); + _s.PassStruct(THIS_REF, "trade-states", tstates); + // @todo + // _s.PassStruct(THIS_REF, "trade-stats", tstats); return SerializerNodeObject; } }; diff --git a/tests/TradeTest.mq5 b/tests/TradeTest.mq5 index 0ac5366ec..a88f89d1b 100644 --- a/tests/TradeTest.mq5 +++ b/tests/TradeTest.mq5 @@ -24,6 +24,9 @@ * Test functionality of Trade class. */ +// #define __debug__ +// #define __debug_verbose__ + // Forward declaration. struct DataParamEntry; @@ -34,19 +37,37 @@ struct DataParamEntry; #include "../Test.mqh" #include "../Trade.mqh" +Ref _chart_m1; +Ref _chart_m5; + /** * Implements OnInit(). */ int OnInit() { Platform::Init(); - Ref _chart_m1 = Platform::FetchDefaultCandleIndicator(_Symbol, PERIOD_M1); - Ref _chart_m5 = Platform::FetchDefaultCandleIndicator(_Symbol, PERIOD_M5); + _chart_m1 = Platform::FetchDefaultCandleIndicator(_Symbol, PERIOD_M1); + _chart_m5 = Platform::FetchDefaultCandleIndicator(_Symbol, PERIOD_M5); Platform::Add(_chart_m1.Ptr()); Platform::Add(_chart_m5.Ptr()); + return INIT_SUCCEEDED; +} - // We're testing OHLCs in OnInit(), so we have to ensure that Tick() happened. +void OnTick() { Platform::Tick(); + // We need some bars in order to make trades. + if (_chart_m5 REF_DEREF GetBarIndex() > trade_params_defaults.GetBarsMin()) { + if (Test() == INIT_FAILED) { + Print("ERROR: Test failed!"); + } + // We only want to test on first tick. + ExpertRemove(); + } +} +/** + * Testing Trade class. Returns INIT_FAILED on failure. + */ +int Test() { // Initial market tests. assertTrueOrFail(SymbolInfoStatic::GetAsk(_Symbol) > 0, "Invalid Ask price!");