diff --git a/Convert.mqh b/Convert.mqh index ad687fdcb..77b45b687 100644 --- a/Convert.mqh +++ b/Convert.mqh @@ -111,7 +111,7 @@ class Convert { /** * Returns number of points per pip. */ - static unsigned int PointsPerPip(string _symbol = NULL) { + static unsigned int PointsPerPip(string _symbol = NULL_STRING) { return PointsPerPip((unsigned int)SymbolInfoStatic::SymbolInfoInteger(_symbol, SYMBOL_DIGITS)); } @@ -137,7 +137,7 @@ class Convert { /** * Convert pips into price value. */ - static double PipsToValue(double pips, string _symbol = NULL) { + static double PipsToValue(double pips, string _symbol = NULL_STRING) { return PipsToValue(pips, (unsigned int)SymbolInfoStatic::SymbolInfoInteger(_symbol, SYMBOL_DIGITS)); } @@ -149,7 +149,7 @@ class Convert { /** * Convert value into pips. */ - static double ValueToPips(double value, string _symbol = NULL) { + static double ValueToPips(double value, string _symbol = NULL_STRING) { return ValueToPips(value, (unsigned int)SymbolInfoStatic::SymbolInfoInteger(_symbol, SYMBOL_DIGITS)); } @@ -161,7 +161,7 @@ class Convert { /** * Convert pips into points. */ - static unsigned int PipsToPoints(double pips, string _symbol = NULL) { + static unsigned int PipsToPoints(double pips, string _symbol = NULL_STRING) { return PipsToPoints(pips, (unsigned int)SymbolInfoStatic::SymbolInfoInteger(_symbol, SYMBOL_DIGITS)); } @@ -173,7 +173,7 @@ class Convert { /** * Convert points into pips. */ - static double PointsToPips(int64 pts, string _symbol = NULL) { + static double PointsToPips(int64 pts, string _symbol = NULL_STRING) { return PointsToPips(pts, (unsigned int)SymbolInfoStatic::SymbolInfoInteger(_symbol, SYMBOL_DIGITS)); } @@ -181,7 +181,7 @@ class Convert { * Convert points into price value. * */ - static double PointsToValue(int64 pts, int mode, string _symbol = NULL) { + static double PointsToValue(int64 pts, int mode, string _symbol = NULL_STRING) { switch (mode) { case 0: // Forex. // In currencies a tick is a point. @@ -226,7 +226,7 @@ class Convert { /** * Convert points into price value. */ - static double PointsToValue(int64 pts, string _symbol = NULL) { + static double PointsToValue(int64 pts, string _symbol = NULL_STRING) { return PointsToValue(pts, (int)SymbolInfoStatic::SymbolInfoInteger(_symbol, SYMBOL_TRADE_CALC_MODE)); } @@ -236,7 +236,7 @@ class Convert { * @return * Returns amount in a base currency based on the given the value. */ - static double ValueToMoney(double value, string _symbol = NULL) { + static double ValueToMoney(double value, string _symbol = NULL_STRING) { double _tick_value = SymbolInfoStatic::GetTickValue(_symbol) > 0 ? SymbolInfoStatic::GetTickValue(_symbol) : 1; return value * _tick_value / SymbolInfoStatic::GetPointSize(_symbol); } @@ -247,7 +247,7 @@ class Convert { * @return * Returns value in points equivalent to the amount in a base currency. */ - static float MoneyToValue(float money, float lot_size, string _symbol = NULL) { + static float MoneyToValue(float money, float lot_size, string _symbol = NULL_STRING) { double _tick_value = SymbolInfoStatic::GetTickValue(_symbol) > 0 ? SymbolInfoStatic::GetTickValue(_symbol) : 1; return money > 0 && lot_size > 0 ? float(money / _tick_value * SymbolInfoStatic::GetPointSize(_symbol) / lot_size) : 0; @@ -257,7 +257,7 @@ class Convert { * Get the difference between two price values (in pips). */ static double GetValueDiffInPips(double price1, double price2, bool abs = false, int digits = 0, - string _symbol = NULL) { + string _symbol = NULL_STRING) { digits = digits ? digits : (int)SymbolInfoStatic::SymbolInfoInteger(_symbol, SYMBOL_DIGITS); return ValueToPips(abs ? fabs(price1 - price2) : (price1 - price2), digits); } diff --git a/Exchange/Account/AccountMt.h b/Exchange/Account/AccountMt.h index 19b957e7f..feca84dda 100644 --- a/Exchange/Account/AccountMt.h +++ b/Exchange/Account/AccountMt.h @@ -470,7 +470,7 @@ class AccountMt : public AccountBase { * @return * Returns true, when free margin is sufficient, false when insufficient or on error. */ - bool IsFreeMargin(ENUM_ORDER_TYPE _cmd, double size_of_lot, string _symbol = NULL) { + bool IsFreeMargin(ENUM_ORDER_TYPE _cmd, double size_of_lot, string _symbol = NULL_STRING) { bool _res = true; // double margin = AccountFreeMarginCheck(_symbol, _cmd, size_of_lot); if (GetLastError() == 134 /* NOT_ENOUGH_MONEY */) _res = false; diff --git a/Exchange/SymbolInfo/SymbolInfo.h b/Exchange/SymbolInfo/SymbolInfo.h index 7b94f8e50..81e0e85ab 100644 --- a/Exchange/SymbolInfo/SymbolInfo.h +++ b/Exchange/SymbolInfo/SymbolInfo.h @@ -66,7 +66,7 @@ class SymbolInfo : public Object { /** * Class constructor given a symbol string. */ - SymbolInfo(string _symbol = NULL) : symbol(_symbol), pip_size(GetPipSize()), symbol_digits(GetDigits()) { + SymbolInfo(string _symbol = NULL_STRING) : symbol(_symbol), pip_size(GetPipSize()), symbol_digits(GetDigits()) { Select(); last_tick = GetTick(); // @todo: Test symbol with SymbolExists(_symbol) diff --git a/Exchange/SymbolInfo/SymbolInfo.struct.static.h b/Exchange/SymbolInfo/SymbolInfo.struct.static.h index ff9092244..f6c8f6aa2 100644 --- a/Exchange/SymbolInfo/SymbolInfo.struct.static.h +++ b/Exchange/SymbolInfo/SymbolInfo.struct.static.h @@ -528,7 +528,7 @@ struct SymbolInfoStatic { return ::SymbolInfoString(name, prop_id); #else printf("@fixme: %s\n", "SymbolInfoStatic::SymbolInfoString()"); - return 0; + return NULL_STRING; #endif } }; diff --git a/File.extern.h b/File.extern.h index efa21a23c..79db77ea4 100644 --- a/File.extern.h +++ b/File.extern.h @@ -58,4 +58,9 @@ unsigned int FileWrite(int file_handle, Arg&& arg, Args&&... args) { return _memfs.FileWrite(file_handle, arg, args...); } +// Currently we only support writting char arrays. +unsigned int FileWriteArray(int file_handle, ARRAY_REF(unsigned char, arr), int start_index, int count = INT_MAX) { + return _memfs.FileWriteArray(file_handle, arr, start_index, count); +} + #endif diff --git a/File.mqh b/File.mqh index 4edc59dcb..f2a98823e 100644 --- a/File.mqh +++ b/File.mqh @@ -96,7 +96,7 @@ class File { Print("Cannot open file \"", path, "\" for reading. Error code: ", GetLastError(), ". Consider using path relative to \"" + terminalDataPath + "\\" + terminalSubfolder + "\\Files\\\" as absolute paths may not work."); - return NULL; + return NULL_STRING; } string data = ""; @@ -116,7 +116,7 @@ class File { static bool SaveFile(string path, string data, bool binary = false) { ResetLastError(); - int handle = FileOpen(path, FILE_WRITE | (binary ? FILE_BIN : FILE_TXT), "", CP_UTF8); + int handle = FileOpen(path, FILE_WRITE | (binary ? FILE_BIN : FILE_TXT), 0, CP_UTF8); if (handle == INVALID_HANDLE) { string terminalDataPath = TerminalInfoString(TERMINAL_DATA_PATH); @@ -132,7 +132,7 @@ class File { } if (binary) { - uchar buffer[]; + ARRAY(unsigned char, buffer); StringToCharArray(data, buffer, 0, WHOLE_ARRAY, CP_UTF8); FileWriteArray(handle, buffer, 0, ArraySize(buffer)); } diff --git a/Indicator/Indicator.h b/Indicator/Indicator.h index 795fd7e74..5af452be9 100644 --- a/Indicator/Indicator.h +++ b/Indicator/Indicator.h @@ -482,7 +482,7 @@ class Indicator : public IndicatorData { /** * Sets indicator's params. */ - void SetParams(IndicatorParams& _iparams) { iparams = _iparams; } + void SetParams(IndicatorParams& _iparams) { REF_TYPE(IndicatorParams)iparams = _iparams; } /* Conditions */ diff --git a/Indicator/IndicatorBase.h b/Indicator/IndicatorBase.h index a2bfbe26c..1005eb796 100644 --- a/Indicator/IndicatorBase.h +++ b/Indicator/IndicatorBase.h @@ -236,6 +236,25 @@ class IndicatorBase : public Object { */ virtual IndicatorData* GetCandle(bool _warn_if_not_found = true, IndicatorData* _originator = nullptr) = 0; + /** + * Returns time of the current tick. Updated by EmitEntry() from the Tick indicator and stored in the Tick indicator + * in the hierarchy. + */ + virtual datetime GetTimeCurrent() { + // We shouldn't be here. It should be implemented in the IndicatorData class and overridden in the Tick indicator. + DebugBreak(); + return datetime(0); + } + + /** + * Updates time of the last tick. Called by EmitEntry() from the Tick indicator. + * @param _time_ms Timestamp in milliseconds. + */ + virtual void UpdateLastTickTimeMs(int64 _time_ms) { + // We shouldn't be here. It should be implemented in the IndicatorData class and overridden in the Tick indicator. + DebugBreak(); + } + /** * Returns the number of bars on the chart decremented by iparams.shift. */ @@ -424,7 +443,7 @@ class IndicatorBase : public Object { } }; -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include #include diff --git a/Indicator/IndicatorData.h b/Indicator/IndicatorData.h index 3bf08dc2e..062066519 100644 --- a/Indicator/IndicatorData.h +++ b/Indicator/IndicatorData.h @@ -64,7 +64,6 @@ class IndicatorData : public IndicatorBase { int calc_start_bar; // Index of the first valid bar (from 0). int flags; // Flags such as INDI_FLAG_INDEXABLE_BY_SHIFT. int last_tick_index; // Index of the last tick. - int64 first_tick_time_ms; // Time of the first ask/bid tick. void* mydata; bool last_tick_result; // Result of the last Tick() invocation. ENUM_INDI_DATA_VS_TYPE retarget_ap_av; // Value storage type to be used as applied price/volume. @@ -150,7 +149,7 @@ class IndicatorData : public IndicatorBase { : do_draw(false), idparams(_idparams), indi_src(_indi_src) { Init(); } - IndicatorData(const IndicatorDataParams& _idparams, ENUM_TIMEFRAMES _tf, string _symbol = NULL) + IndicatorData(const IndicatorDataParams& _idparams, ENUM_TIMEFRAMES _tf, string _symbol = NULL_STRING) : do_draw(false), idparams(_idparams) { Init(); } @@ -211,12 +210,6 @@ class IndicatorData : public IndicatorBase { */ int GetFlags() { return flags; } - /** - * Returns time of the first ask/bid tick (time of first global OnTick()). - * Time is compatible with time generated by IndicatorTick, e.g., Indi_TickMt. - */ - int64 GetFirstTickTimeMs() { return first_tick_time_ms; } - /** * Get full name of the indicator (with "over ..." part). */ @@ -892,6 +885,22 @@ class IndicatorData : public IndicatorBase { HasSpecificValueStorage(INDI_DATA_VS_TYPE_VOLUME) && HasSpecificValueStorage(INDI_DATA_VS_TYPE_TICK_VOLUME); } + /** + * Returns time of the current tick. Updated by EmitEntry() from the Tick indicator and stored in the Tick indicator + * in the hierarchy. + */ + datetime GetTimeCurrent() override { + return GetTick() PTR_DEREF GetTimeCurrent(); + } + + /** + * Updates time of the last tick. Called by EmitEntry() from the Tick indicator. + * @param _time_ms Timestamp in milliseconds. + */ + void UpdateLastTickTimeMs(int64 _time_ms) override { + GetTick() PTR_DEREF UpdateLastTickTimeMs(_time_ms); + } + bool Tick(int _global_tick_index) { if (last_tick_index == _global_tick_index) { #ifdef __debug_indicator__ @@ -901,10 +910,11 @@ class IndicatorData : public IndicatorBase { return last_tick_result; } - if (_global_tick_index == 0) { - // Time of the first tick must be compatible with time generated by IndicatorTick, e.g., Indi_TickMt. - first_tick_time_ms = TimeCurrent() * 1000; - } + // Will allow indicator to update time of the current tick to be used by current indicator. + // It is required to update time before ticking data source and indicators in hierarchy because they could use time + // of the current tick for their calculations, e.g., to check if new tick belongs to new bar or not. + // @todo OnUpdatePlatformTime(GetTimeCurrent()); + last_tick_index = _global_tick_index; @@ -2000,6 +2010,13 @@ class IndicatorData : public IndicatorBase { * Sends entry to listening indicators. */ void EmitEntry(IndicatorDataEntry& _entry, ENUM_INDI_EMITTED_ENTRY_TYPE _type = INDI_EMITTED_ENTRY_TYPE_PARENT) { + if (_type == INDI_EMITTED_ENTRY_TYPE_TICK) { + // Updating time of the last emitted tick entry in the Tick indicator in the hierarchy to be later retrieved by + // GetTimeCurrent() method of indicators or global TimeCurrent()/Platform::TimeCurrent() when indicator is ran + // with Tester class. + GetTick() PTR_DEREF UpdateLastTickTimeMs(_entry.timestamp * 1000); + } + for (int i = 0; i < ArraySize(listeners); ++i) { if (listeners[i].ObjectExists()) { listeners[i].Ptr() PTR_DEREF OnDataSourceEntry(_entry, _type); @@ -2219,7 +2236,7 @@ IValueStorage* ExternInstantiateIndicatorBufferValueStorageDouble::InstantiateIn int GetBarsFromStart(IndicatorBase* _indi) { return _indi PTR_DEREF GetBars(); } #endif -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include #include @@ -2229,7 +2246,13 @@ EMSCRIPTEN_BINDINGS(IndicatorData) { .function("SetSource", emscripten::optional_override([](IndicatorData& self, IndicatorData* base) { self.SetDataSource(base); }), - emscripten::allow_raw_pointer>()); + emscripten::allow_raw_pointer>()); } #endif + +// Provide inline definitions for PlatformTime methods that need complete IndicatorBase/IndicatorData types. +// This must come after the IndicatorData class body so both types are complete. +#ifndef __MQL__ +#include "../Platform/PlatformTime.inline.h" +#endif diff --git a/Indicator/IndicatorTf.h b/Indicator/IndicatorTf.h index 30f038823..79782b933 100644 --- a/Indicator/IndicatorTf.h +++ b/Indicator/IndicatorTf.h @@ -73,7 +73,10 @@ class IndicatorTf : public IndicatorCandle>(_icparams, _idparams) { + Init(); + } /** * Gets indicator's time-frame. diff --git a/Indicator/IndicatorTf.struct.h b/Indicator/IndicatorTf.struct.h index 5a4d80f92..bff9bb406 100644 --- a/Indicator/IndicatorTf.struct.h +++ b/Indicator/IndicatorTf.struct.h @@ -37,7 +37,7 @@ struct IndicatorTfParams : IndicatorParams { ChartTf tf; // Struct constructor. - IndicatorTfParams(string _name, ENUM_TIMEFRAMES _tf) : IndicatorParams(_name) { tf.SetTf(_tf); } + IndicatorTfParams(string _name, ENUM_TIMEFRAMES _tf) : IndicatorParams(_name), tf(_tf) { } // Copy constructor. IndicatorTfParams(const IndicatorTfParams &_params, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) { THIS_REF = _params; diff --git a/Indicator/IndicatorTick.h b/Indicator/IndicatorTick.h index eb1cf1e12..f3f08deb2 100644 --- a/Indicator/IndicatorTick.h +++ b/Indicator/IndicatorTick.h @@ -55,6 +55,9 @@ class IndicatorTick : public Indicator { SymbolInfoProp symbol_props; TickBarCounter counter; + // Time of the last tick (with passed periods functionality). + DateTime last_tick_time; + protected: /* Protected methods */ @@ -83,7 +86,7 @@ class IndicatorTick : public Indicator { */ IndicatorTick(string _symbol, const TS& _itparams, const IndicatorDataParams& _idparams, IndicatorData* _indi_src = NULL, int _indi_mode = 0) - : Indicator(_itparams, _idparams, _indi_src, _indi_mode), history(THIS_PTR) { + : Indicator(_itparams, _idparams, _indi_src, _indi_mode), history(THIS_PTR), last_tick_time(false) { itparams = _itparams; if (_indi_src != NULL) { THIS_ATTR SetDataSource(_indi_src, _indi_mode); @@ -92,7 +95,7 @@ class IndicatorTick : public Indicator { Init(); } IndicatorTick(string _symbol, ENUM_INDICATOR_TYPE _itype = INDI_CANDLE, int _shift = 0, string _name = "") - : Indicator(_itype, _shift, _name), history(THIS_PTR) { + : Indicator(_itype, _shift, _name), history(THIS_PTR), last_tick_time(false) { symbol = _symbol; Init(); } @@ -112,6 +115,22 @@ class IndicatorTick : public Indicator { */ datetime GetBarTime(int _rel_shift = 0) override { return history.GetItemTimeByShift(_rel_shift); } + /** + * Updates time of the last tick. Called by EmitEntry() from the Tick indicator. + */ + virtual void UpdateLastTickTimeMs(int64 _time_ms) override { + Print("Updating last tick time (sec): ", _time_ms / 1000, " for indicator ", this->GetFullName()); + last_tick_time.Update(_time_ms / 1000); + } + + /** + * Returns time of the current tick. Updated by EmitEntry() from the Tick indicator and stored in the Tick indicator + * in the hierarchy. + */ + datetime GetTimeCurrent() override { + return last_tick_time.dt_curr.GetTimestamp(); + } + /** * Gets ask price for a given date and time. Return current ask price if _dt wasn't passed or is 0. */ diff --git a/Indicator/tests/classes/IndicatorTfDummy.h b/Indicator/tests/classes/IndicatorTfDummy.h index baab51c03..c26b5cdd0 100644 --- a/Indicator/tests/classes/IndicatorTfDummy.h +++ b/Indicator/tests/classes/IndicatorTfDummy.h @@ -71,12 +71,12 @@ class IndicatorTfDummy : public IndicatorTf { #ifdef __debug_indicator__ Print(GetFullName(), " got new tick at ", entry.timestamp, - " (" + TimeToString(entry.timestamp) + "): ", entry.ToString()); + " (" + TimeToString(entry.timestamp, TIME_DATE | TIME_MINUTES | TIME_SECONDS) + "): ", entry.ToString()); #endif } }; -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include EMSCRIPTEN_BINDINGS(IndicatorTfDummyParams) { emscripten::value_object("indicators.TfParams"); } diff --git a/Indicators/Oscillator/Indi_RSI.h b/Indicators/Oscillator/Indi_RSI.h index 8c494df7e..5622f3c16 100644 --- a/Indicators/Oscillator/Indi_RSI.h +++ b/Indicators/Oscillator/Indi_RSI.h @@ -118,7 +118,7 @@ class Indi_RSI : public Indicator { * - https://docs.mql4.com/indicators/irsi * - https://www.mql5.com/en/docs/indicators/irsi */ - static double iRSI(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _period = 14, + static double iRSI(string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _period = 14, ENUM_APPLIED_PRICE _applied_price = PRICE_CLOSE, int _shift = 0, IndicatorData *_obj = NULL) { #ifdef __MQL__ #ifdef __MQL4__ @@ -139,7 +139,7 @@ class Indi_RSI : public Indicator { * Calculates non-SMMA version of RSI on another indicator (uses iRSIOnArray). */ template - static double iRSIOnArrayOnIndicator(IndicatorData *_indi, string _symbol = NULL, + static double iRSIOnArrayOnIndicator(IndicatorData *_indi, string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _period = 14, ENUM_APPLIED_PRICE _applied_price = PRICE_CLOSE, int _shift = 0, Indi_RSI *_obj = NULL) { @@ -174,7 +174,7 @@ class Indi_RSI : public Indicator { * RSI values. To exactly replicate our RSI numbers, a formula will need at * least 250 data points." */ - static double iRSIOnIndicator(Indi_RSI *_target, IndicatorData *_source, string _symbol = NULL, + static double iRSIOnIndicator(Indi_RSI *_target, IndicatorData *_source, string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _period = 14, ENUM_APPLIED_PRICE _ap = PRICE_CLOSE, int _shift = 0) { INDI_REQUIRE_BARS_OR_RETURN_EMPTY(_target, _period + _shift + 1); // +1 because of _bar_time_prev. @@ -327,6 +327,8 @@ class Indi_RSI : public Indicator { break; case IDATA_ONCALCULATE: // @todo Modify iRSIOnIndicator() to operate on single IndicatorData pointer. + Print("Indi_RSI doesn't support IDATA_ONCALCULATE mode yet!"); + DebugBreak(); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /* [ */ iparams.GetPeriod(), @@ -335,7 +337,7 @@ class Indi_RSI : public Indicator { case IDATA_INDICATOR: _value = Indi_RSI::iRSIOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), iparams.GetPeriod(), iparams.GetAppliedPrice(), ToRelShift(_abs_shift)); - break; + break; default: RUNTIME_ERROR("Invalid indicator IDATA_* type!"); } @@ -355,7 +357,7 @@ double iRSIOnArray(ARRAY_REF(double, _arr), int _total, int _period, int _abs_sh } #endif -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include EMSCRIPTEN_BINDINGS(Indi_RSI_Params) { diff --git a/Indicators/Tick/Indi_TickProvider.h b/Indicators/Tick/Indi_TickProvider.h index c592eb39b..813f9a0b7 100644 --- a/Indicators/Tick/Indi_TickProvider.h +++ b/Indicators/Tick/Indi_TickProvider.h @@ -137,7 +137,7 @@ class Indi_TickProvider : public IndicatorTick EMSCRIPTEN_BINDINGS(Indi_TickProviderParams) { @@ -160,7 +160,7 @@ EMSCRIPTEN_BINDINGS(Indi_TickProviderBaseBase) { EMSCRIPTEN_BINDINGS(Indi_TickProviderBase) { emscripten::class_>, - emscripten::base>>("IndiTickProviderBas e") + emscripten::base>>("IndiTickProviderBase") .smart_ptr>>>( "Ref>"); } diff --git a/Platform/Chart/Bar.struct.h b/Platform/Chart/Bar.struct.h index 6b372d08f..5f39e03ad 100644 --- a/Platform/Chart/Bar.struct.h +++ b/Platform/Chart/Bar.struct.h @@ -245,7 +245,7 @@ struct BarOHLC string ToCSV() { return StringFormat("%d,%g,%g,%g,%g", time, open, high, low, close); } // Operators. bool operator==(const BarOHLC &_r) { - return time == _r.time && open == _r.time && high == _r.high && low == _r.low && close == _r.close; + return time == _r.time && open == (double)_r.time && high == _r.high && low == _r.low && close == _r.close; } bool operator!=(const BarOHLC &_r) { return !(THIS_REF == _r); } }; diff --git a/Platform/Chart/Chart.enum.h b/Platform/Chart/Chart.enum.h index 36ef19255..600e69678 100644 --- a/Platform/Chart/Chart.enum.h +++ b/Platform/Chart/Chart.enum.h @@ -128,7 +128,7 @@ enum ENUM_TIMEFRAMES { PERIOD_MN1 = 43200 // 1 month. }; - #ifdef EMSCRIPTEN + #ifdef __EMSCRIPTEN__ #include #include diff --git a/Platform/Chart/Chart.struct.static.h b/Platform/Chart/Chart.struct.static.h index 6c17ba9cc..f4ec125c6 100644 --- a/Platform/Chart/Chart.struct.static.h +++ b/Platform/Chart/Chart.struct.static.h @@ -41,7 +41,7 @@ struct ChartStatic { /** * Returns the number of bars on the specified chart. */ - static int iBars(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) { + static int iBars(string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) { #ifdef __MQL4__ // In MQL4, for the current chart, the information about the amount of bars is in the Bars predefined variable. int _bars = ::iBars(_symbol, _tf); @@ -115,7 +115,7 @@ struct ChartStatic { * * @see http://docs.mql4.com/series/iclose */ - static double iClose(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0) { + static double iClose(string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0) { #ifdef __MQL4__ return ::iClose(_symbol, _tf, _shift); // Same as: Close[_shift] #else // __MQL5__ @@ -130,7 +130,7 @@ struct ChartStatic { * * If local history is empty (not loaded), function returns 0. */ - static double iHigh(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _shift = 0) { + static double iHigh(string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _shift = 0) { #ifdef __MQL4__ return ::iHigh(_symbol, _tf, _shift); // Same as: High[_shift] #else // __MQL5__ @@ -187,7 +187,7 @@ struct ChartStatic { * * If local history is empty (not loaded), function returns 0. */ - static double iLow(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _shift = 0) { + static double iLow(string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _shift = 0) { #ifdef __MQL4__ return ::iLow(_symbol, _tf, _shift); // Same as: Low[_shift] #else // __MQL5__ @@ -200,7 +200,7 @@ struct ChartStatic { /** * Returns the shift of the lowest value over a specific number of periods depending on type. */ - static int iLowest(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _type = MODE_LOW, + static int iLowest(string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _type = MODE_LOW, unsigned int _count = WHOLE_ARRAY, int _start = 0) { #ifdef __MQL4__ return ::iLowest(_symbol, _tf, _type, _count, _start); @@ -244,7 +244,7 @@ struct ChartStatic { * * If local history is empty (not loaded), function returns 0. */ - static double iOpen(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _shift = 0) { + static double iOpen(string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _shift = 0) { #ifdef __MQL4__ return ::iOpen(_symbol, _tf, _shift); // Same as: Open[_shift] #else // __MQL5__ @@ -257,7 +257,7 @@ struct ChartStatic { /** * Returns the current price value given applied price type. */ - static double iPrice(ENUM_APPLIED_PRICE _ap, string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, + static double iPrice(ENUM_APPLIED_PRICE _ap, string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0) { double _result = EMPTY_VALUE; switch (_ap) { @@ -304,7 +304,7 @@ struct ChartStatic { * * If local history is empty (not loaded), function returns 0. */ - static datetime iTime(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _shift = 0) { + static datetime iTime(string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, unsigned int _shift = 0) { #ifdef __MQL4__ return ::iTime(_symbol, _tf, _shift); // Same as: Time[_shift] #else // __MQL5__ @@ -320,7 +320,7 @@ struct ChartStatic { * * If local history is empty (not loaded), function returns 0. */ - static int64 iVolume(string _symbol = NULL, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0) { + static int64 iVolume(string _symbol = NULL_STRING, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0) { #ifdef __MQL4__ ResetLastError(); int64 _volume = ::iVolume(_symbol, _tf, _shift); // Same as: Volume[_shift] diff --git a/Platform/Order.h b/Platform/Order.h index e2f232d2e..f75af6fdb 100644 --- a/Platform/Order.h +++ b/Platform/Order.h @@ -2508,7 +2508,7 @@ class Order : public SymbolInfo { string _string; switch (selected_ticket_type) { case ORDER_SELECT_TYPE_NONE: - return (X)NULL_VALUE; + return NULL_VALUE; case ORDER_SELECT_TYPE_ACTIVE: case ORDER_SELECT_TYPE_HISTORY: @@ -2527,7 +2527,7 @@ class Order : public SymbolInfo { case DEAL_TYPE_SELL: return ConvertBasic::LongTo(ORDER_TYPE_SELL); default: - return (X)NULL_VALUE; + return NULL_VALUE; } break; case ORDER_STATE: @@ -2536,23 +2536,23 @@ class Order : public SymbolInfo { case ORDER_TIME_EXPIRATION: case ORDER_TIME_DONE: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; case ORDER_TIME_SETUP_MSC: return OrderGetValue(DEAL_TIME_MSC, _type, _out); case ORDER_TIME_DONE_MSC: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; case ORDER_TYPE_FILLING: case ORDER_TYPE_TIME: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; case ORDER_MAGIC: return OrderGetValue(DEAL_MAGIC, _type, _out); case ORDER_POSITION_ID: return OrderGetValue(DEAL_POSITION_ID, _type, _out); case ORDER_POSITION_BY_ID: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; default: if ((int)_prop_id == (int)ORDER_REASON) { switch ((int)OrderGetValue(DEAL_REASON, _type, _long)) { @@ -2571,7 +2571,7 @@ class Order : public SymbolInfo { case DEAL_REASON_SO: return ConvertBasic::LongTo(ORDER_REASON_SO); default: - return (X)NULL_VALUE; + return NULL_VALUE; } } } @@ -2582,29 +2582,29 @@ class Order : public SymbolInfo { return OrderGetValue(DEAL_VOLUME, _type, _out); case ORDER_VOLUME_CURRENT: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; case ORDER_PRICE_OPEN: return OrderGetValue(DEAL_PRICE, _type, _out); case ORDER_SL: case ORDER_TP: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; case ORDER_PRICE_CURRENT: return OrderGetValue(DEAL_PRICE, _type, _out); case ORDER_PRICE_STOPLIMIT: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; } break; case ORDER_SELECT_DATA_TYPE_STRING: switch (_prop_id) { case ORDER_SYMBOL: case ORDER_COMMENT: - return (X)NULL_VALUE; + return NULL_VALUE; default: #ifdef ORDER_EXTERNAL_ID if ((int)_prop_id == (int)ORDER_EXTERNAL_ID) { - return (X)NULL_VALUE; + return NULL_VALUE; } #endif } @@ -2625,7 +2625,7 @@ class Order : public SymbolInfo { case POSITION_TYPE_SELL: return ConvertBasic::LongTo(ORDER_TYPE_SELL); default: - return (X)NULL_VALUE; + return NULL_VALUE; } break; case ORDER_STATE: @@ -2634,23 +2634,23 @@ class Order : public SymbolInfo { case ORDER_TIME_EXPIRATION: case ORDER_TIME_DONE: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; case ORDER_TIME_SETUP_MSC: return OrderGetValue(POSITION_TIME_MSC, _type, _out); case ORDER_TIME_DONE_MSC: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; case ORDER_TYPE_FILLING: case ORDER_TYPE_TIME: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; case ORDER_MAGIC: return OrderGetValue(POSITION_MAGIC, _type, _out); case ORDER_POSITION_ID: return OrderGetValue(POSITION_IDENTIFIER, _type, _out); case ORDER_POSITION_BY_ID: SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; default: if ((int)_prop_id == (int)ORDER_REASON) { switch ((int)OrderGetValue(POSITION_REASON, _type, _long)) { @@ -2663,7 +2663,7 @@ class Order : public SymbolInfo { case POSITION_REASON_EXPERT: return ConvertBasic::LongTo(ORDER_REASON_EXPERT); default: - return (X)NULL_VALUE; + return NULL_VALUE; } } } @@ -2685,7 +2685,7 @@ class Order : public SymbolInfo { case ORDER_PRICE_STOPLIMIT: // @fixme SetUserError(ERR_INVALID_PARAMETER); - return (X)NULL_VALUE; + return NULL_VALUE; } break; case ORDER_SELECT_DATA_TYPE_STRING: @@ -2706,7 +2706,7 @@ class Order : public SymbolInfo { break; } - return (X)NULL_VALUE; + return NULL_VALUE; #else return OrderGetValue(_prop_id, _type, _out); #endif @@ -2953,3 +2953,5 @@ class Order : public SymbolInfo { ENUM_ORDER_SELECT_TYPE Order::selected_ticket_type = ORDER_SELECT_TYPE_NONE; uint64 Order::selected_ticket_id = 0; #endif + + diff --git a/Platform/Order.struct.h b/Platform/Order.struct.h index db37432dd..c52628994 100644 --- a/Platform/Order.struct.h +++ b/Platform/Order.struct.h @@ -296,7 +296,7 @@ struct OrderData { tp(0), close_tries(0), last_error(ERR_NO_ERROR), - symbol(NULL), + symbol(NULL_STRING), volume_curr(0), volume_init(0) {} // Copy constructor. @@ -1021,7 +1021,7 @@ struct OrderStatic { * Usage: SerializerConverter::FromObject(MqlTradeRequestProxy(_request)).ToString()); */ struct MqlTradeRequestProxy : MqlTradeRequest { - MqlTradeRequestProxy(MqlTradeRequest &r) { THIS_REF = r; } + MqlTradeRequestProxy(MqlTradeRequest &r) { REF_TYPE(MqlTradeRequest)THIS_REF = r; } SerializerNodeType Serialize(Serializer &s) { s.PassEnum(THIS_REF, "action", action); @@ -1051,7 +1051,7 @@ struct MqlTradeRequestProxy : MqlTradeRequest { * Usage: SerializerConverter::FromObject(MqlTradeResultProxy(_request)).ToString()); */ struct MqlTradeResultProxy : MqlTradeResult { - MqlTradeResultProxy(MqlTradeResult &r) { THIS_REF = r; } + MqlTradeResultProxy(MqlTradeResult &r) { REF_TYPE(MqlTradeResult)THIS_REF = r; } SerializerNodeType Serialize(Serializer &s) { s.Pass(THIS_REF, "retcode", retcode); diff --git a/Platform/Orders.h b/Platform/Orders.h index 3b95b2b17..645af3663 100644 --- a/Platform/Orders.h +++ b/Platform/Orders.h @@ -322,7 +322,7 @@ class Orders { * @return * Returns true on success. */ - bool OrdersCloseAll(const string _symbol = NULL, const ENUM_POSITION_TYPE _type = (ENUM_POSITION_TYPE)-1, + bool OrdersCloseAll(const string _symbol = NULL_STRING, const ENUM_POSITION_TYPE _type = (ENUM_POSITION_TYPE)-1, const int _magic = -1) { #ifdef __MQL4__ @@ -544,7 +544,7 @@ class Orders { /** * Count open positions by order type. */ - static unsigned int GetOrdersByType(ENUM_ORDER_TYPE _cmd, string _symbol = NULL) { + static unsigned int GetOrdersByType(ENUM_ORDER_TYPE _cmd, string _symbol = NULL_STRING) { unsigned int _counter = 0; _symbol = _symbol != NULL_STRING ? _symbol : _Symbol; for (int i = 0; i < OrdersTotal(); i++) { diff --git a/Platform/Platform.extern.h b/Platform/Platform.extern.h index f5b62bd7f..8b405f366 100644 --- a/Platform/Platform.extern.h +++ b/Platform/Platform.extern.h @@ -29,8 +29,9 @@ #include "../Exchange/Account/Account.enum.h" #include "../Storage/Data.define.h" -#include "../Storage/DateTime.h" #include "../Storage/Object.extern.h" +#include "../Storage/String.extern.h" +#include "../Storage/DateTime.extern.h" #include "Deal.enum.h" #include "Order.define.h" #include "Order.enum.h" diff --git a/Platform/Platform.h b/Platform/Platform.h index 2ec428f3b..35a8c0e67 100644 --- a/Platform/Platform.h +++ b/Platform/Platform.h @@ -78,15 +78,6 @@ class Platform : public Taskable { // Global tick index. static int global_tick_index; - // Date and time used to determine periods that passed. - static DateTime time; - - // Merged flags from previous Platform::UpdateTime(); - static unsigned int time_flags; - - // Whether to clear passed periods on consecutive Platform::UpdateTime(). - static bool time_clear_flags; - // Whether history for all the indicators was emitted. static bool emitted_history; @@ -105,6 +96,9 @@ class Platform : public Taskable { // Timeframe of the currently ticking indicator. static ENUM_TIMEFRAMES period; + // Currently ticking indicator. + static IndicatorData* indi_current; + private: /** * Sets symbol of the currently ticking indicator. @@ -186,17 +180,15 @@ class Platform : public Taskable { */ static int GetGlobalTickIndex() { return global_tick_index; } + /** + * Returns number of seconds passed from the Unix epoch. + */ + static datetime Timestamp() { return TimeCurrent(); } + /** * Performs tick on every added indicator. */ static void Tick() { - // @todo Should update time for each ticking indicator and only when it signal a tick. - PlatformTime::Tick(); - time.Update(); - - // Checking starting periods and updating time to current one. - time_flags = time.GetStartedPeriods(); - DictStructIterator> _iter; last_tick_result = false; @@ -207,6 +199,8 @@ class Platform : public Taskable { symbol = _iter.Value() REF_DEREF GetSymbol(); period = _iter.Value() REF_DEREF GetTf(); + PlatformTime::SetCurrentIndicator(_iter.Value().Ptr()); + #ifdef __debug__ PrintFormat("Tick #%d for %s for symbol %s and period %s", global_tick_index, C_STR(_iter.Value() REF_DEREF GetFullName()), C_STR(symbol), C_STR(ChartTf::TfToString(period))); @@ -234,9 +228,6 @@ class Platform : public Taskable { symbol = PLATFORM_WRONG_SYMBOL; period = PLATFORM_WRONG_TIMEFRAME; - // Will check for new time periods in consecutive Platform::UpdateTime(). - time_clear_flags = true; - // Started from 0. Will be incremented after each finished tick. ++global_tick_index; } @@ -295,51 +286,6 @@ class Platform : public Taskable { */ static void Remove(IndicatorData *_indi) { indis.Unset(_indi PTR_DEREF GetId()); } - /** - * Returns date and time used to determine periods that passed. - */ - static DateTime Time() { return time; } - - /** - * Returns number of seconds passed from the Unix epoch. - */ - static datetime Timestamp() { return TimeCurrent(); } - - /** - * Checks whether it's a new second. - */ - static bool IsNewSecond() { return (time_flags & DATETIME_SECOND) != 0; } - - /** - * Checks whether it's a new minute. - */ - static bool IsNewMinute() { return (time_flags & DATETIME_MINUTE) != 0; } - - /** - * Checks whether it's a new hour. - */ - static bool IsNewHour() { return (time_flags & DATETIME_HOUR) != 0; } - - /** - * Checks whether it's a new day. - */ - static bool IsNewDay() { return (time_flags & DATETIME_DAY) != 0; } - - /** - * Checks whether it's a new week. - */ - static bool IsNewWeek() { return (time_flags & DATETIME_WEEK) != 0; } - - /** - * Checks whether it's a new month. - */ - static bool IsNewMonth() { return (time_flags & DATETIME_MONTH) != 0; } - - /** - * Checks whether it's a new year. - */ - static bool IsNewYear() { return (time_flags & DATETIME_YEAR) != 0; } - /** * Returns number of candles for a given symbol and time-frame. */ @@ -641,9 +587,6 @@ class Platform : public Taskable { bool Platform::initialized = false; bool Platform::last_tick_result = false; -DateTime Platform::time = (datetime)0; -unsigned int Platform::time_flags = 0; -bool Platform::time_clear_flags = true; int Platform::global_tick_index = 0; string Platform::symbol = PLATFORM_WRONG_SYMBOL; ENUM_TIMEFRAMES Platform::period = PLATFORM_WRONG_TIMEFRAME; @@ -714,7 +657,7 @@ double HistoryDealGetDouble(uint64 ticket_number, ENUM_DEAL_PROPERTY_DOUBLE prop string HistoryDealGetString(uint64 ticket_number, ENUM_DEAL_PROPERTY_STRING property_id) { Print("Not yet implemented: ", __FUNCTION__, " returns empty string."); - return 0; + return ""; } bool OrderSelect(int index, int select, int pool = MODE_TRADES) { @@ -784,12 +727,12 @@ double HistoryOrderGetDouble(uint64 ticket_number, ENUM_ORDER_PROPERTY_DOUBLE pr string OrderGetString(ENUM_ORDER_PROPERTY_STRING property_id) { Print("Not yet implemented: ", __FUNCTION__, " returns empty string."); - return 0; + return ""; } string HistoryOrderGetString(uint64 ticket_number, ENUM_ORDER_PROPERTY_STRING property_id) { Print("Not yet implemented: ", __FUNCTION__, " returns empty string."); - return 0; + return ""; } int PositionsTotal() { @@ -990,23 +933,6 @@ string TimeToString(datetime value, int mode) { return ss.str(); } -bool TimeToStruct(datetime dt, MqlDateTime &dt_struct) { - time_t now = (time_t)dt; - - tm *ltm = localtime(&now); - - dt_struct.day = ltm->tm_mday; - dt_struct.day_of_week = ltm->tm_wday; - dt_struct.day_of_year = ltm->tm_yday; - dt_struct.hour = ltm->tm_hour; - dt_struct.min = ltm->tm_min; - dt_struct.mon = ltm->tm_mon; - dt_struct.sec = ltm->tm_sec; - dt_struct.year = ltm->tm_year; - - return true; -} - SymbolGetter::operator string() const { return Platform::GetSymbol(); } ENUM_TIMEFRAMES Period() { return Platform::GetPeriod(); } diff --git a/Platform/PlatformTime.h b/Platform/PlatformTime.h index 73e16008c..cb3563404 100644 --- a/Platform/PlatformTime.h +++ b/Platform/PlatformTime.h @@ -28,6 +28,7 @@ // Includes. #include "../Storage/DateTime.enum.h" #include "../Storage/DateTime.struct.h" +#include "../Storage/DateTime.extern.h" /** * @file @@ -43,51 +44,39 @@ #include "../Std.h" +// Forward declarations to avoid circular include via Serializer.h -> Convert.basic.h -> DateTime.h -> PlatformTime.h. +class IndicatorBase; +class IndicatorData; + class PlatformTime { - static MqlDateTime current_time; - static int64 current_timestamp_s; - static int64 current_timestamp_ms; + // Current tick indicator. + static IndicatorBase* current_tick_indicator; public: - static int64 CurrentTimestamp() { return current_timestamp_s; } - static int64 CurrentTimestampMs() { return current_timestamp_ms; } - static MqlDateTime CurrentTime() { return current_time; } - - void static Tick() { -#ifdef __MQL__ - static int64 _last_timestamp_ms = 0; - - current_timestamp_s = ::TimeCurrent(current_time); - - current_timestamp_ms = (int64)GetTickCount(); - if (_last_timestamp_ms != 0 && current_timestamp_ms < _last_timestamp_ms) { - // Overflow occured (49.7 days passed). - // More info: https://docs.mql4.com/common/gettickcount - current_timestamp_ms += _last_timestamp_ms; - } - - _last_timestamp_ms = current_timestamp_ms; -#else - using namespace std::chrono; - current_timestamp_s = (int64)duration_cast(system_clock::now().time_since_epoch()).count(); - current_timestamp_ms = (int64)duration_cast(system_clock::now().time_since_epoch()).count(); - - using namespace std::chrono; - std::time_t t = current_timestamp_s; - std::tm* now = std::localtime(&t); - current_time.day = now->tm_mday; - current_time.day_of_week = now->tm_wday; - current_time.day_of_year = now->tm_yday; - current_time.hour = now->tm_hour; - current_time.min = now->tm_min; - current_time.mon = now->tm_mon; - current_time.sec = now->tm_sec; - current_time.year = now->tm_year; -#endif - } + /** + * Returns current time in seconds since epoch. + */ + static int64 TimeCurrent(); + + /** + * Returns current time in milliseconds since epoch. + */ + static int64 CurrentTimestampMs(); + + /** + * Returns current time as MqlDateTime structure. + */ + static MqlDateTime CurrentTime(); + + /** + * Sets the current indicator. Used to provide current tick time to indicators when they call TimeCurrent() method. + */ + static void SetCurrentIndicator(IndicatorData* _indi); + + /** + * Updates current tick time. Should be called on every tick with the tick time in milliseconds since epoch. In MQL, + * it can be called without parameters as tick time can be retrieved with TimeCurrent() and GetTickCount() functions. + */ + static void UpdateLastTickTimeMs(int64 tick_time_ms, IndicatorData* _source_indi); }; - -MqlDateTime PlatformTime::current_time = {0, 0, 0, 0, 0, 0, 0, 0}; -int64 PlatformTime::current_timestamp_s = 0; -int64 PlatformTime::current_timestamp_ms = 0; diff --git a/Platform/PlatformTime.inline.h b/Platform/PlatformTime.inline.h new file mode 100644 index 000000000..bcaddd90f --- /dev/null +++ b/Platform/PlatformTime.inline.h @@ -0,0 +1,87 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2023, EA31337 Ltd | +//| https://ea31337.github.io | +//+------------------------------------------------------------------+ + +/* + * 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 + * Inline method bodies for PlatformTime that require complete IndicatorBase/IndicatorData types. + * + * This file must be included AFTER both IndicatorBase and IndicatorData class definitions are + * available (i.e. from the end of IndicatorData.h). It must NOT be included from PlatformTime.h + * itself, to avoid a circular include through Serializer.h -> Convert.basic.h -> DateTime.h. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes the full indicator types (safe here because IndicatorData.h is already processed). +#include "../Indicator/IndicatorBase.h" +#include "../Indicator/IndicatorData.h" + +// Static member definition (must appear in exactly one translation unit). +// Since IndicatorData.h is designed to be included from a single TU in WASM builds, this is safe here. +IndicatorBase* PlatformTime::current_tick_indicator = nullptr; + +inline int64 PlatformTime::TimeCurrent() { + if (current_tick_indicator == nullptr) { + Print("Error: Current tick indicator is not set. TimeCurrent() will return 0 and is unusable. You should use IndicatorTest/Platform::Tick() method to run ticks."); + DebugBreak(); + return 0; + } + + Print("Retrieving current time from current tick indicator: ", current_tick_indicator PTR_DEREF GetFullName(), " with time ", current_tick_indicator PTR_DEREF GetTimeCurrent()); + + return current_tick_indicator PTR_DEREF GetTimeCurrent(); +} + +inline int64 PlatformTime::CurrentTimestampMs() { + if (current_tick_indicator == nullptr) { + return 0; + } + return current_tick_indicator PTR_DEREF GetTimeCurrent() * 1000; +} + +inline MqlDateTime PlatformTime::CurrentTime() { + if (current_tick_indicator == nullptr) { + Print("Warning: Current tick indicator is not set. Returning 0 as current time."); + DebugBreak(); + return MqlDateTime{0, 0, 0, 0, 0, 0, 0, 0}; + } + + MqlDateTime dt; + TimeToStruct(current_tick_indicator PTR_DEREF GetTimeCurrent(), dt); + return dt; +} + +inline void PlatformTime::SetCurrentIndicator(IndicatorData* _indi) { + current_tick_indicator = _indi PTR_DEREF GetTick(); +} + +inline void PlatformTime::UpdateLastTickTimeMs(int64 tick_time_ms, IndicatorData* _source_indi) { + if (current_tick_indicator != _source_indi) { + Print("Warning: Currently ticking indicator is not the same as indicator calling PlatformTime::Update()! Returning without updating time."); + DebugBreak(); + return; + } + current_tick_indicator PTR_DEREF UpdateLastTickTimeMs(tick_time_ms); +} diff --git a/Platform/Terminal.h b/Platform/Terminal.h index e78d1f125..b8096e39a 100644 --- a/Platform/Terminal.h +++ b/Platform/Terminal.h @@ -978,7 +978,7 @@ class Terminal : public Object { return ::TerminalInfoString(property_id); #else printf("@fixme: %s\n", "Terminal::TerminalInfoString()"); - return 0; + return NULL_STRING; #endif } diff --git a/Refs.struct.h b/Refs.struct.h index 3f421fb03..0550f82c2 100644 --- a/Refs.struct.h +++ b/Refs.struct.h @@ -34,7 +34,7 @@ #include "Serializer/SerializerNode.enum.h" #include "Std.h" -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include #endif @@ -102,7 +102,7 @@ struct Ref { */ X* ptr_object; -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ typedef X element_type; #endif @@ -144,7 +144,7 @@ struct Ref { */ X* Ptr() const { return ptr_object; } -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ X* get() const { return ptr_object; } #endif @@ -274,7 +274,7 @@ struct Ref { /** * Equality operator. */ - bool operator==(const Ref& r) { return ptr_object != NULL && ptr_object == r.ptr_object; } + bool operator==(const Ref& r) const { return ptr_object != NULL && ptr_object == r.ptr_object; } /** * Returns information about object references counter. diff --git a/Serializer/SerializerCsv.h b/Serializer/SerializerCsv.h index d82746379..b2fae97c5 100644 --- a/Serializer/SerializerCsv.h +++ b/Serializer/SerializerCsv.h @@ -58,13 +58,13 @@ class SerializerCsv { if (CheckPointer(_root) == POINTER_INVALID) { Alert("SerializerCsv: Invalid root node pointer!"); DebugBreak(); - return NULL; + return NULL_STRING; } if (_stub == NULL || _stub PTR_DEREF Node() == NULL) { Alert("SerializerCsv: Cannot convert to CSV without stub object!"); DebugBreak(); - return NULL; + return NULL_STRING; } bool _include_titles = bool(serializer_flags & SERIALIZER_CSV_INCLUDE_TITLES); diff --git a/Serializer/SerializerJson.h b/Serializer/SerializerJson.h index 776a6818d..2f4a9f510 100644 --- a/Serializer/SerializerJson.h +++ b/Serializer/SerializerJson.h @@ -397,6 +397,6 @@ class SerializerJson { } } - return NULL; + return NULL_STRING; } }; diff --git a/Std.h b/Std.h index c4a5d6ce1..49a9a685d 100644 --- a/Std.h +++ b/Std.h @@ -85,6 +85,13 @@ #define REF_CPP & #endif +// Reference to type. +#ifdef __MQL4__ + #define REF_TYPE(X) (X) +#else + #define REF_TYPE(X) (X&) +#endif + // Reference to simple type like bool, int, double, string. #ifdef __MQL__ #define REF(X) X& @@ -304,7 +311,7 @@ class _cpp_array { void setIsSeries(bool _isSeries) { m_isSeries = _isSeries; } }; - #ifdef EMSCRIPTEN + #ifdef __EMSCRIPTEN__ #include #define REGISTER_ARRAY_OF(N, T, D) \ @@ -407,13 +414,38 @@ const char* _empty_string_c = ""; const string _empty_string = ""; // Converter of NULL_VALUE into expected type. e.g., "int x = NULL_VALUE" will end up with "x = 0". -struct _NULL_VALUE { +class _NULL_VALUE { + public: + + // Explicit conversion operator for string + operator std::string() const { + return _empty_string; + } + template operator T() const { return std::numeric_limits::max(); } + + // Helper method to get value as type T - needed for template contexts + template + typename std::enable_if::value, T>::type as() const { + return std::numeric_limits::max(); + } + + template + typename std::enable_if::value, T>::type as() const { + return _empty_string; + } + + template T as() const { + return 0; + } + } NULL_VALUE; +extern _NULL_VALUE NULL_VALUE; + /** * Converting an enumeration value of any type to a text form. * @@ -427,11 +459,6 @@ string EnumToString(int _value) { ss << _value; return ss.str(); } - -template <> -_NULL_VALUE::operator string() const { - return _empty_string; -} #define NULL_STRING "" #else #define NULL_VALUE NULL diff --git a/Storage/Array.h b/Storage/Array.h index a8d96a6af..24a6b5e05 100644 --- a/Storage/Array.h +++ b/Storage/Array.h @@ -557,10 +557,10 @@ static int GetLowestArrDoubleValue(double& arr[][], int key) { template void ArrayPrint(ARRAY_REF(T, _arr), // Printed array. int _digits = 0, // Number of decimal places. - const string _dlm = NULL, // Separator of the structure field values. + const string _dlm = NULL_STRING, // Separator of the structure field values. int64 _start = 0, // First printed element index. int64 _count = WHOLE_ARRAY, // Number of printed elements. - int64 _flags = NULL) { + int64 _flags = 0) { #ifdef __MQL5__ ::ArrayPrint(_arr, _digits, _dlm, _start, _count, _flags); #else diff --git a/Storage/DateTime.entry.h b/Storage/DateTime.entry.h index 176c43417..2a1d36aa7 100644 --- a/Storage/DateTime.entry.h +++ b/Storage/DateTime.entry.h @@ -38,7 +38,7 @@ struct DateTimeEntry : MqlDateTime { int week_of_year; // Struct constructors. - DateTimeEntry() { Set(); } + DateTimeEntry(bool _init_with_curr_time = true) { if (_init_with_curr_time) Set(); } DateTimeEntry(datetime _dt) { Set(_dt); } DateTimeEntry(MqlDateTime& _dt) { Set(_dt); @@ -95,7 +95,7 @@ struct DateTimeEntry : MqlDateTime { datetime GetTimestamp() { return StructToTime(THIS_REF); } // Setters. void Set() { - TimeToStruct(PlatformTime::CurrentTimestamp(), THIS_REF); + TimeToStruct(PlatformTime::TimeCurrent(), THIS_REF); // @fixit Should also set day of week. } void SetGMT() { @@ -109,7 +109,7 @@ struct DateTimeEntry : MqlDateTime { } // Set date and time. void Set(MqlDateTime& _time) { - THIS_REF = _time; + REF_TYPE(MqlDateTime)THIS_REF = _time; // @fixit Should also set day of week. } void SetDayOfMonth(int _value) { @@ -169,4 +169,5 @@ struct DateTimeEntry : MqlDateTime { } } void SetYear(int _value) { year = _value; } + datetime GetTime() { return StructToTime(THIS_REF); } }; diff --git a/Storage/DateTime.extern.h b/Storage/DateTime.extern.h index 1c7b14554..ffcbbfac4 100644 --- a/Storage/DateTime.extern.h +++ b/Storage/DateTime.extern.h @@ -31,6 +31,7 @@ #include #include "DateTime.enum.h" +#include "DateTime.struct.h" #include "String.h" // Forward declarations. @@ -47,7 +48,7 @@ class datetime { datetime(const int64& _time) { dt = _time; } // datetime(const int& _time); bool operator==(const int _time) const = delete; - bool operator==(const datetime& _time) const { return dt == _time; } + bool operator==(const datetime& _time) const { return dt == (int64)_time; } bool operator<(const int _time) const = delete; bool operator>(const int _time) const = delete; bool operator<(const datetime& _time) const { return dt < _time; } @@ -73,7 +74,6 @@ extern int CopyTime(string symbol_name, ENUM_TIMEFRAMES timeframe, datetime star } extern datetime StructToTime(MqlDateTime& dt_struct); -extern bool TimeToStruct(datetime dt, MqlDateTime& dt_struct); extern datetime TimeGMT(); extern datetime TimeGMT(MqlDateTime& dt_struct); extern datetime TimeTradeServer(); @@ -81,8 +81,25 @@ extern datetime TimeTradeServer(MqlDateTime& dt_struct); extern datetime StringToTime(const string& value); extern string TimeToString(datetime value, int mode = TIME_DATE | TIME_MINUTES); +bool TimeToStruct(datetime dt, MqlDateTime &dt_struct) { + time_t now = (time_t)dt; + + tm *ltm = localtime(&now); + + dt_struct.day = ltm->tm_mday; + dt_struct.day_of_week = ltm->tm_wday; + dt_struct.day_of_year = ltm->tm_yday; + dt_struct.hour = ltm->tm_hour; + dt_struct.min = ltm->tm_min; + dt_struct.mon = ltm->tm_mon; + dt_struct.sec = ltm->tm_sec; + dt_struct.year = ltm->tm_year; + + return true; +} + template -datetime operator"" _D(); +datetime operator""_D(); #define DATETIME_LITERAL(STR) _D " ## STR ## " diff --git a/Storage/DateTime.h b/Storage/DateTime.h index be8647c64..8c619f9be 100644 --- a/Storage/DateTime.h +++ b/Storage/DateTime.h @@ -34,8 +34,15 @@ #pragma once #endif +#ifndef __MQL__ // Forward declarations. struct DataParamEntry; +class datetime; +struct MqlDateTime; + +datetime TimeCurrent(); +datetime TimeCurrent(MqlDateTime &dt_struct); +#endif // Includes class enum and structs. #include "../Platform/PlatformTime.h" @@ -65,11 +72,15 @@ class DateTime { /** * Class constructor. */ - DateTime() { TimeToStruct(PlatformTime::CurrentTimestamp(), dt_curr); } + DateTime(bool _init_with_curr_time = true) : dt_curr(_init_with_curr_time), dt_last(_init_with_curr_time) { + if (_init_with_curr_time) { + TimeToStruct(PlatformTime::TimeCurrent(), dt_curr); + } + } DateTime(DateTime &r) : dt_curr(r.dt_curr), dt_last(r.dt_last) {} - DateTime(DateTimeEntry &_dt) { dt_curr = _dt; } - DateTime(MqlDateTime &_dt) { dt_curr = _dt; } - DateTime(datetime _dt) { dt_curr.Set(_dt); } + DateTime(DateTimeEntry &_dt) : dt_curr(_dt), dt_last(_dt) {} + DateTime(MqlDateTime &_dt) : dt_curr(_dt), dt_last(_dt) {} + DateTime(datetime _dt) : dt_curr(_dt), dt_last(_dt) {} /** * Class deconstructor. @@ -89,14 +100,16 @@ class DateTime { * @param * _unit - given periods to check * _update - whether to update datetime before check + * _update_last - whether to update last datetime after check + * _time - optional timestamp to update datetime (dt_curr) to (if _update is true) * * @return int * Returns bitwise flag of started periods. */ - unsigned int GetStartedPeriods(bool _update = true, bool _update_last = true) { + unsigned int GetStartedPeriods(bool _update = true, bool _update_last = true, int64 _time = 0) { unsigned int _result = DATETIME_NONE; if (_update) { - Update(); + Update(_time); } if (dt_curr.GetValue(DATETIME_YEAR) != dt_last.GetValue(DATETIME_YEAR)) { @@ -200,9 +213,17 @@ class DateTime { bool IsNewHour() { return (GetStartedPeriods(false, false) & DATETIME_HOUR) != 0; } /** - * Updates datetime to the current one. + * Updates datetime to the given one. */ - void Update() { dt_curr.Set(PlatformTime::CurrentTimestamp()); } + void Update(int64 time = 0) { + if (time == dt_last.GetTime()) { + // No time change, no need to update. + return; + } + + dt_last = dt_curr; + dt_curr.Set(time != 0 ? time : PlatformTime::TimeCurrent()); + } /* Conditions */ @@ -245,11 +266,15 @@ class DateTime { #ifndef __MQL__ -datetime TimeCurrent() { return PlatformTime::CurrentTimestamp(); } +datetime TimeCurrent() { return PlatformTime::TimeCurrent(); } +/** + * Returns current tick's time and fill the dt_struct with the corresponding values. + */ datetime TimeCurrent(MqlDateTime &dt_struct) { - dt_struct = PlatformTime::CurrentTime(); - return PlatformTime::CurrentTimestamp(); + datetime current_time = PlatformTime::TimeCurrent(); + TimeToStruct(current_time, dt_struct); + return current_time; } #endif diff --git a/Storage/DateTime.static.h b/Storage/DateTime.static.h index cb932ce6c..258f5efac 100644 --- a/Storage/DateTime.static.h +++ b/Storage/DateTime.static.h @@ -42,7 +42,7 @@ struct DateTimeStatic { */ static int Day(datetime dt = 0) { if (dt == (datetime)0) { - dt = (datetime)PlatformTime::CurrentTimestamp(); + dt = (datetime)PlatformTime::TimeCurrent(); } #ifdef __MQL4__ return ::TimeDay(dt); @@ -58,7 +58,7 @@ struct DateTimeStatic { */ static int DayOfWeek(datetime dt = 0) { if (dt == (datetime)0) { - dt = (datetime)PlatformTime::CurrentTimestamp(); + dt = (datetime)PlatformTime::TimeCurrent(); } #ifdef __MQL4__ return ::DayOfWeek(); @@ -74,7 +74,7 @@ struct DateTimeStatic { */ static int DayOfYear(datetime dt = 0) { if (dt == (datetime)0) { - dt = (datetime)PlatformTime::CurrentTimestamp(); + dt = (datetime)PlatformTime::TimeCurrent(); } #ifdef __MQL4__ return ::DayOfYear(); @@ -90,7 +90,7 @@ struct DateTimeStatic { */ static int Hour(datetime dt = 0) { if (dt == (datetime)0) { - dt = (datetime)PlatformTime::CurrentTimestamp(); + dt = (datetime)PlatformTime::TimeCurrent(); } #ifdef __MQL4__ return ::Hour(); @@ -115,7 +115,7 @@ struct DateTimeStatic { */ static int Minute(datetime dt = 0) { if (dt == (datetime)0) { - dt = (datetime)PlatformTime::CurrentTimestamp(); + dt = (datetime)PlatformTime::TimeCurrent(); } #ifdef __MQL4__ return ::Minute(); @@ -131,7 +131,7 @@ struct DateTimeStatic { */ static int Month(datetime dt = 0) { if (dt == (datetime)0) { - dt = (datetime)PlatformTime::CurrentTimestamp(); + dt = (datetime)PlatformTime::TimeCurrent(); } #ifdef __MQL4__ return ::Month(); @@ -147,7 +147,7 @@ struct DateTimeStatic { */ static int Seconds(datetime dt = 0) { if (dt == (datetime)0) { - dt = (datetime)PlatformTime::CurrentTimestamp(); + dt = (datetime)PlatformTime::TimeCurrent(); } #ifdef __MQL4__ return ::Seconds(); @@ -170,7 +170,7 @@ struct DateTimeStatic { #endif } static string TimeToStr(int mode = TIME_DATE | TIME_MINUTES | TIME_SECONDS) { - return TimeToStr(PlatformTime::CurrentTimestamp(), mode); + return TimeToStr(PlatformTime::TimeCurrent(), mode); } /** @@ -193,7 +193,7 @@ struct DateTimeStatic { */ static int Year(datetime dt = 0) { if (dt == (datetime)0) { - dt = (datetime)PlatformTime::CurrentTimestamp(); + dt = (datetime)PlatformTime::TimeCurrent(); } #ifdef __MQL4__ return ::Year(); diff --git a/Storage/Dict/Dict.h b/Storage/Dict/Dict.h index 438d2334a..19fed4181 100644 --- a/Storage/Dict/Dict.h +++ b/Storage/Dict/Dict.h @@ -125,7 +125,7 @@ class Dict : public DictBase { V operator[](K key) { if (THIS_ATTR _mode == DictModeList) return THIS_ATTR GetSlot((unsigned int)key).value; - int position; + unsigned int position; DictSlot* slot = GetSlotByKey(THIS_ATTR _DictSlots_ref, key, position); if (!slot) return (V)NULL; @@ -201,7 +201,7 @@ class Dict : public DictBase { * Checks whether dictionary contains given value. */ bool Contains(const V value) { - for (DictIterator i = THIS_ATTR Begin(); i.IsValid(); ++i) { + for (DictIteratorBase i = THIS_ATTR Begin(); i.IsValid(); ++i) { if (i.Value() == value) { return true; } diff --git a/Storage/ItemsHistory.h b/Storage/ItemsHistory.h index 85b16383c..526d1479d 100644 --- a/Storage/ItemsHistory.h +++ b/Storage/ItemsHistory.h @@ -34,7 +34,9 @@ */ enum ENUM_ITEMS_HISTORY_DIRECTION { ITEMS_HISTORY_DIRECTION_FORWARD, ITEMS_HISTORY_DIRECTION_BACKWARD }; -#include "../Indicator/IndicatorData.h" +// Forward declaration to avoid circular include with IndicatorData.h. +class IndicatorData; + #include "../Refs.mqh" #include "../Storage/Dict/DictStruct.h" diff --git a/Storage/MemoryFileSystem.h b/Storage/MemoryFileSystem.h index dd8272fd7..a02251566 100644 --- a/Storage/MemoryFileSystem.h +++ b/Storage/MemoryFileSystem.h @@ -144,6 +144,21 @@ class MemoryFileSystem { _file REF_DEREF buffer += data; return data.size(); } + + // Similar to FileWrite, but for char arrays. Currently we only support writting char arrays. + unsigned int FileWriteArray(int file_handle, ARRAY_REF(unsigned char, arr), int start_index, int count = INT_MAX) { + if (!files_by_handle.KeyExists(file_handle)) { + Print("Error: MemoryFileSystemFile handle ", file_handle, " is not opened!"); + DebugBreak(); + return 0; + } + + string data(((char*)arr.str().data()) + start_index, MathMin(count, arr.size())); + + Ref _file = files_by_handle.GetByKey(file_handle); + _file REF_DEREF buffer += data; + return data.size(); + } }; #endif diff --git a/Storage/ValueStorage.indicator.h b/Storage/ValueStorage.indicator.h index c6e4d6346..ba0b25950 100644 --- a/Storage/ValueStorage.indicator.h +++ b/Storage/ValueStorage.indicator.h @@ -73,6 +73,10 @@ class IndicatorBufferValueStorage : public HistoryValueStorage { template C IndicatorBufferValueStorage::Fetch(int _rel_shift) { IndicatorBase* _indi = THIS_ATTR indi_candle.Ptr(); - return _indi PTR_DEREF template GetValue(mode, THIS_ATTR RealShift(_rel_shift)); + // GetValue() already uses descending shift semantics (0 = newest bar). + // Do NOT call RealShift() here — that would convert to ascending order + // which GetValue/GetItemByShift would then misinterpret, always resolving + // to the oldest bars instead of the requested ones. + return _indi PTR_DEREF template GetValue(mode, _rel_shift); } #endif diff --git a/Task/Task.h b/Task/Task.h index 7f21b9665..2c20724f0 100644 --- a/Task/Task.h +++ b/Task/Task.h @@ -315,7 +315,7 @@ class Task : public Taskable { /* Other methods */ }; -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include #include diff --git a/Task/Task.struct.h b/Task/Task.struct.h index 7adfcde5e..7f8cbcb7c 100644 --- a/Task/Task.struct.h +++ b/Task/Task.struct.h @@ -154,7 +154,7 @@ struct TaskEntry { SERIALIZER_EMPTY_STUB; }; -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include EMSCRIPTEN_BINDINGS(TaskEntry) { emscripten::class_("TaskEntry").constructor(); } diff --git a/Task/TaskManager.h b/Task/TaskManager.h index 704c4af59..94d8048fa 100644 --- a/Task/TaskManager.h +++ b/Task/TaskManager.h @@ -138,7 +138,7 @@ class TaskManager { } }; -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include EMSCRIPTEN_BINDINGS(TaskManager) { diff --git a/Tick/Tick.struct.h b/Tick/Tick.struct.h index f01bd662a..c30802a6e 100644 --- a/Tick/Tick.struct.h +++ b/Tick/Tick.struct.h @@ -119,7 +119,7 @@ struct TickTAB : TickAB { struct DoubleTickTAB : TickTAB {}; -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include EMSCRIPTEN_BINDINGS(TickAB) { diff --git a/Trade.mqh b/Trade.mqh index cc38d285f..7c4d361ec 100644 --- a/Trade.mqh +++ b/Trade.mqh @@ -583,7 +583,7 @@ class Trade : public Taskable { * Optional symbol name if different than current. */ double OptimizeLotSize(double lots, double win_factor = 1.0, double loss_factor = 1.0, int ols_orders = 100, - string _symbol = NULL) { + string _symbol = NULL_STRING) { double lotsize = lots; int wins = 0, losses = 0; // Number of consequent losing orders. int twins = 0, tlosses = 0; // Total number of consequent losing orders. diff --git a/Trade/TradeSignal.h b/Trade/TradeSignal.h index 1d130ddd1..d81e5e39f 100644 --- a/Trade/TradeSignal.h +++ b/Trade/TradeSignal.h @@ -185,7 +185,7 @@ class TradeSignal { string ToString() { return signal.ToString(); } }; -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include EMSCRIPTEN_BINDINGS(TradeSignal) { diff --git a/Trade/TradeSignalManager.h b/Trade/TradeSignalManager.h index 20cc09be6..389431702 100644 --- a/Trade/TradeSignalManager.h +++ b/Trade/TradeSignalManager.h @@ -246,7 +246,7 @@ class TradeSignalManager : Dynamic { } }; -#ifdef EMSCRIPTEN +#ifdef __EMSCRIPTEN__ #include EMSCRIPTEN_BINDINGS(TradeSignalManager) { diff --git a/Trade/tests/TradeSignalManager.test.cpp.fixme b/Trade/tests/TradeSignalManager.test.cpp similarity index 97% rename from Trade/tests/TradeSignalManager.test.cpp.fixme rename to Trade/tests/TradeSignalManager.test.cpp index cc8149f0e..e9a56883d 100644 --- a/Trade/tests/TradeSignalManager.test.cpp.fixme +++ b/Trade/tests/TradeSignalManager.test.cpp @@ -28,10 +28,8 @@ #include "../../Common.define.h" #include "../../Common.extern.h" #include "../../Std.h" -#include "../../String.extern.h" -#include "../TradeSignalManager.h" - #include "../../Platform/Platform.h" +#include "../TradeSignalManager.h" int main(int argc, char **argv) { // @todo