Skip to content

C++/WASM support for EA31337 code#792

Open
nseam wants to merge 3 commits into
EA31337:v4.000-devfrom
nseam:v3.009-dev-new--wasm-support
Open

C++/WASM support for EA31337 code#792
nseam wants to merge 3 commits into
EA31337:v4.000-devfrom
nseam:v3.009-dev-new--wasm-support

Conversation

@nseam
Copy link
Copy Markdown
Contributor

@nseam nseam commented Apr 28, 2026

No description provided.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (47)
  • Convert.mqh is excluded by none and included by none
  • Exchange/Account/AccountMt.h is excluded by none and included by none
  • Exchange/SymbolInfo/SymbolInfo.h is excluded by none and included by none
  • Exchange/SymbolInfo/SymbolInfo.struct.static.h is excluded by none and included by none
  • File.extern.h is excluded by none and included by none
  • File.mqh is excluded by none and included by none
  • Indicator/Indicator.h is excluded by none and included by none
  • Indicator/IndicatorBase.h is excluded by none and included by none
  • Indicator/IndicatorData.h is excluded by none and included by none
  • Indicator/IndicatorTf.h is excluded by none and included by none
  • Indicator/IndicatorTf.struct.h is excluded by none and included by none
  • Indicator/IndicatorTick.h is excluded by none and included by none
  • Indicator/tests/classes/IndicatorTfDummy.h is excluded by none and included by none
  • Indicators/Oscillator/Indi_RSI.h is excluded by none and included by none
  • Indicators/Tick/Indi_TickProvider.h is excluded by none and included by none
  • Platform/Chart/Bar.struct.h is excluded by none and included by none
  • Platform/Chart/Chart.enum.h is excluded by none and included by none
  • Platform/Chart/Chart.struct.static.h is excluded by none and included by none
  • Platform/Order.h is excluded by none and included by none
  • Platform/Order.struct.h is excluded by none and included by none
  • Platform/Orders.h is excluded by none and included by none
  • Platform/Platform.extern.h is excluded by none and included by none
  • Platform/Platform.h is excluded by none and included by none
  • Platform/PlatformTime.h is excluded by none and included by none
  • Platform/PlatformTime.inline.h is excluded by none and included by none
  • Platform/Terminal.h is excluded by none and included by none
  • Refs.struct.h is excluded by none and included by none
  • Serializer/SerializerCsv.h is excluded by none and included by none
  • Serializer/SerializerJson.h is excluded by none and included by none
  • Std.h is excluded by none and included by none
  • Storage/Array.h is excluded by none and included by none
  • Storage/DateTime.entry.h is excluded by none and included by none
  • Storage/DateTime.extern.h is excluded by none and included by none
  • Storage/DateTime.h is excluded by none and included by none
  • Storage/DateTime.static.h is excluded by none and included by none
  • Storage/Dict/Dict.h is excluded by none and included by none
  • Storage/ItemsHistory.h is excluded by none and included by none
  • Storage/MemoryFileSystem.h is excluded by none and included by none
  • Storage/ValueStorage.indicator.h is excluded by none and included by none
  • Task/Task.h is excluded by none and included by none
  • Task/Task.struct.h is excluded by none and included by none
  • Task/TaskManager.h is excluded by none and included by none
  • Tick/Tick.struct.h is excluded by none and included by none
  • Trade.mqh is excluded by none and included by none
  • Trade/TradeSignal.h is excluded by none and included by none
  • Trade/TradeSignalManager.h is excluded by none and included by none
  • Trade/tests/TradeSignalManager.test.cpp is excluded by none and included by none

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b8ca19d8-9d92-441d-b8f7-49f0f3e9f2c1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'version'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 28, 2026

Reviewer's Guide

Introduces C++/WASM-compatible time handling and null/string semantics across the EA31337 codebase, routing time queries through PlatformTime and tick indicators, updating various utilities and bindings to use NULL_STRING and EMSCRIPTEN guards, and adding inline PlatformTime implementations plus WASM-specific filesystem and serialization fixes.

File-Level Changes

Change Details Files
Refactor platform time handling to be driven by tick indicators and usable in C++/WASM builds.
  • Remove Platform::time, time_flags, and related period-check helpers from Platform and instead expose Timestamp() as a thin wrapper over TimeCurrent().
  • Redesign PlatformTime to provide TimeCurrent/CurrentTimestampMs/CurrentTime APIs that delegate to a current_tick_indicator, set via SetCurrentIndicator and updated by UpdateLastTickTimeMs.
  • Add PlatformTime.inline.h with inline implementations that integrate with IndicatorBase/IndicatorData, including static current_tick_indicator and logic to pull tick time from indicators.
Platform/Platform.h
Platform/PlatformTime.h
Platform/PlatformTime.inline.h
Propagate tick time through indicators so TimeCurrent() reflects the current tick when running under the tester/WASM.
  • Extend IndicatorBase and IndicatorData with GetTimeCurrent and UpdateLastTickTimeMs virtuals, implemented in IndicatorData and overridden in IndicatorTick to manage a DateTime _last_tick_time.
  • Update IndicatorData::EmitEntry to tag tick entries (INDI_EMITTED_ENTRY_TYPE_TICK) and push tick timestamps into the tick indicator, and wire Indi_TickProvider to emit tick-type entries.
  • Ensure IndicatorTf and related indicator parameter/constructor logic are correctly initialized for C++ builds.
Indicator/IndicatorBase.h
Indicator/IndicatorData.h
Indicator/IndicatorTick.h
Indicators/Tick/Indi_TickProvider.h
Indicator/IndicatorTf.h
Indicator/IndicatorTf.struct.h
Indicator/tests/classes/IndicatorTfDummy.h
Adjust DateTime utilities to consume PlatformTime and support external (C++) datetime struct/helpers.
  • Make DateTime and DateTimeEntry use PlatformTime::TimeCurrent and TimeToStruct(DateTime.extern) instead of internal PlatformTime timestamps; add GetTime(), and allow GetStartedPeriods/Update to accept explicit timestamps.
  • Move TimeToStruct implementation into DateTime.extern.h and wire new non-MQL TimeCurrent overloads returning datetime and filling MqlDateTime.
  • Update DateTimeStatic helpers to call PlatformTime::TimeCurrent instead of PlatformTime::CurrentTimestamp.
Storage/DateTime.h
Storage/DateTime.extern.h
Storage/DateTime.static.h
Storage/DateTime.entry.h
Introduce C++-friendly NULL_VALUE/NULL_STRING semantics and fix string defaults/returns for non-MQL builds.
  • Redefine _NULL_VALUE as a class with template conversion and string-specialized behavior; add extern declaration, and ensure functions that should return empty strings use NULL_STRING/"" instead of 0.
  • Change many function parameters and struct fields from default NULL to NULL_STRING to avoid pointer-vs-string ambiguity in C++ (e.g., Convert, ChartStatic, SymbolInfo, Orders, AccountMt, Trade).
  • Update Order::OrderGetValue and various serializer/File helpers to return NULL_STRING instead of NULL/0 where appropriate.
Std.h
Platform/Order.h
Indicator/IndicatorData.h
Convert.mqh
Platform/Chart/Chart.struct.static.h
Platform/Order.struct.h
Platform/Orders.h
Exchange/Account/AccountMt.h
Exchange/SymbolInfo/SymbolInfo.h
Exchange/SymbolInfo/SymbolInfo.struct.static.h
Serializer/SerializerCsv.h
Serializer/SerializerJson.h
File.mqh
Add/adjust WASM (EMSCRIPTEN) bindings and memory-based filesystem support needed for C++/WASM.
  • Standardize on EMSCRIPTEN macro for Emscripten bindings across indicators, tasks, refs, tick structs, and enums, and fix a class name typo in Indi_TickProvider WASM binding.
  • Add MemoryFileSystem::FileWriteArray and corresponding FileWriteArray extern wrapper to support binary writes from C++ using uchar arrays.
  • Update Platform.extern.h and related headers to use extern string/date types suitable for C++ and avoid circular includes by forward-declaring IndicatorData where needed.
Indicator/IndicatorData.h
Indicator/IndicatorBase.h
Indicators/Oscillator/Indi_RSI.h
Indicators/Tick/Indi_TickProvider.h
Refs.struct.h
Task/Task.h
Task/Task.struct.h
Task/TaskManager.h
Tick/Tick.struct.h
Platform/Chart/Chart.enum.h
Storage/MemoryFileSystem.h
File.extern.h
Storage/ItemsHistory.h
Platform/Platform.extern.h
Bug fixes and small correctness tweaks discovered during C++/WASM enabling.
  • Fix HistoryDealGetString/OrderGetString/etc. to return empty strings instead of 0, and adjust File::SaveFile to pass flags correctly to FileOpen and use ARRAY(uchar) for binary buffer.
  • Correct BarOHLC::operator== comparison (open vs _r.open) and Dict/Array utilities (index type, iterator type, default flags).
  • Repair various small API issues: MqlTradeRequestProxy/MqlTradeResultProxy copy construction via REF_TYPE macro, Indicator::SetParams and DateTimeEntry::Set use of REF_TYPE, ChartStatic/SymbolInfoStatic fallbacks, and change EMSCRIPTEN to EMSCRIPTEN where required.
Platform/Platform.h
Platform/Chart/Bar.struct.h
Storage/Dict/Dict.h
Storage/Array.h
Platform/Order.struct.h
Indicator/Indicator.h
Storage/DateTime.entry.h
File.mqh
Exchange/SymbolInfo/SymbolInfo.struct.static.h
Platform/Terminal.h
Trade.mqh
Trade/TradeSignal.h
Trade/TradeSignalManager.h
Trade/tests/TradeSignalManager.test.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 8 issues, and left some high level feedback:

  • In BarOHLC::operator== the comparison open == (double)_r.time looks like a copy‑paste error and should likely be open == _r.open, otherwise equality checks on bars will behave incorrectly.
  • The _NULL_VALUE class in Std.h now has conflicting/overlapping APIs (an operator std::string, a templated operator T, two as<T> specializations plus a generic template <typename T> T as() const { return 0; }) and is also both defined and declared extern; this is prone to ambiguity and ODR issues and should be simplified so there is a single, consistent implementation and a single definition.
  • PlatformTime::current_tick_indicator is defined in PlatformTime.inline.h, which is included as a header; this will emit a non‑inline static definition in every translation unit that includes it—consider marking it inline or moving the definition to a single .cpp to avoid ODR violations, and review the heavy Print/DebugBreak usage in these inline functions to avoid noisy logging in normal flows.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `BarOHLC::operator==` the comparison `open == (double)_r.time` looks like a copy‑paste error and should likely be `open == _r.open`, otherwise equality checks on bars will behave incorrectly.
- The `_NULL_VALUE` class in `Std.h` now has conflicting/overlapping APIs (an `operator std::string`, a templated `operator T`, two `as<T>` specializations plus a generic `template <typename T> T as() const { return 0; }`) and is also both defined and declared `extern`; this is prone to ambiguity and ODR issues and should be simplified so there is a single, consistent implementation and a single definition.
- `PlatformTime::current_tick_indicator` is defined in `PlatformTime.inline.h`, which is included as a header; this will emit a non‑inline static definition in every translation unit that includes it—consider marking it `inline` or moving the definition to a single .cpp to avoid ODR violations, and review the heavy `Print`/`DebugBreak` usage in these inline functions to avoid noisy logging in normal flows.

## Individual Comments

### Comment 1
<location path="Std.h" line_range="417-426" />
<code_context>

 // 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 <typename T>
   operator T() const {
     return std::numeric_limits<T>::max();
   }
+
+  // Helper method to get value as type T - needed for template contexts
+  template <typename T>
+  typename std::enable_if<!std::is_same<T, string>::value, T>::type as() const {
+    return std::numeric_limits<T>::max();
+  }
+
+  template <typename T>
+  typename std::enable_if<std::is_same<T, string>::value, T>::type as() const {
+    return _empty_string;
+  }
+
+  template <typename T> T as() const {
+    return 0;
+  }
</code_context>
<issue_to_address>
**issue (bug_risk):** The _NULL_VALUE class has conflicting template overloads and a duplicated global definition, which is likely to break compilation and violate the one-definition rule.

The third `template <typename T> T as() const` conflicts with the two SFINAE `as` overloads (same signature) and will cause redefinition/overload ambiguity, as well as contradicting their behavior.

You also both define and declare `NULL_VALUE`:
```cpp
} NULL_VALUE;

extern _NULL_VALUE NULL_VALUE;
```
This violates the one-definition rule and can cause multiple-definition link errors or UB.

Please:
- Remove the generic `as<T>()` and rely on the SFINAE overloads, or replace all three with a single templated `as<T>()` using `if constexpr`.
- Keep only one definition of `NULL_VALUE`: an `extern` declaration in a header and a single non-`extern` definition in one translation unit (or adjust accordingly).
</issue_to_address>

### Comment 2
<location path="Platform/PlatformTime.inline.h" line_range="43" />
<code_context>
+
+// 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() {
</code_context>
<issue_to_address>
**issue (bug_risk):** Defining the static member in this header will cause multiple definition errors when included in multiple translation units.

To fix this, either:
- Mark the definition as `inline` (C++17+): `inline IndicatorBase* PlatformTime::current_tick_indicator = nullptr;`, ensuring all TUs see the same definition; or
- Move the definition to a `.cpp` file and leave only `static IndicatorBase* current_tick_indicator;` declared in the header.

Relying on a single-TU convention for WASM is fragile; the second option is safer if this code may be reused in other builds.
</issue_to_address>

### Comment 3
<location path="Platform/Chart/Bar.struct.h" line_range="247-248" />
<code_context>
   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); }
</code_context>
<issue_to_address>
**issue (bug_risk):** BarOHLC equality comparison uses the right-hand bar's time instead of its open value, so equality checks are incorrect.

`operator==` currently reads:
```cpp
bool operator==(const BarOHLC &_r) {
  return time == _r.time && open == (double)_r.time && high == _r.high && low == _r.low && close == _r.close;
}
```
It should compare `open` fields directly, e.g.:
```cpp
return time == _r.time && open == _r.open && high == _r.high && low == _r.low && close == _r.close;
```
As written, any code using this for equality (e.g., deduplication or lookups) will behave incorrectly.
</issue_to_address>

### Comment 4
<location path="Platform/PlatformTime.inline.h" line_range="45-52" />
<code_context>
+// 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();
</code_context>
<issue_to_address>
**suggestion (performance):** Using Print/DebugBreak in these time-accessors for non-error paths can be noisy and expensive in tight tick loops.

`TimeCurrent()` currently logs every call and uses `DebugBreak()` when `current_tick_indicator` is null. Given these are per‑tick calls and may be invoked by many indicators, this will flood logs and degrade performance, especially in backtests.

Please gate these prints behind a debug flag or remove them from non‑debug builds, and replace `DebugBreak()` in normal code paths with assertions or a dedicated error‑reporting mechanism so diagnostics remain useful without impacting runtime behavior.

Suggested implementation:

```c
#endif

#include <cassert>

// Includes the full indicator types (safe here because IndicatorData.h is already processed).
#include "../Indicator/IndicatorBase.h"
#include "../Indicator/IndicatorData.h"

```

```c
inline int64 PlatformTime::TimeCurrent() {
  if (current_tick_indicator == nullptr) {
#ifndef NDEBUG
    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.");
    assert(false && "PlatformTime::TimeCurrent called with null current_tick_indicator");
#endif
    return 0;
  }

#ifndef NDEBUG
  Print("Retrieving current time from current tick indicator: ", current_tick_indicator PTR_DEREF GetFullName(), " with time ", current_tick_indicator PTR_DEREF GetTimeCurrent());
#endif

  return current_tick_indicator PTR_DEREF GetTimeCurrent();
}

```
</issue_to_address>

### Comment 5
<location path="Indicator/IndicatorTick.h" line_range="121-122" />
<code_context>
+  /**
+   * 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);
+  }
</code_context>
<issue_to_address>
**suggestion (performance):** Per-tick Print in IndicatorTick::UpdateLastTickTimeMs will severely impact performance and spam logs.

Because `UpdateLastTickTimeMs` is called for every tick, this log line will generate extremely large log volumes and noticeably slow both live and backtest runs.

Please either guard this behind a compile-time debug flag / debug-only build, or remove it once the feature is validated. If you still need detailed tracing, consider a rate-limited logger instead of unconditional per-tick printing.

Suggested implementation:

```c
  /**
   * Updates time of the last tick. Called by EmitEntry() from the Tick indicator.
   * Logging is guarded by DEBUG_INDICATOR_TICK_LOG to avoid per-tick log spam in normal builds.
   */
  virtual void UpdateLastTickTimeMs(int64 _time_ms) override {
  #ifdef DEBUG_INDICATOR_TICK_LOG
    Print("Updating last tick time (sec): ", _time_ms / 1000, " for indicator ", this->GetFullName());
  #endif
    _last_tick_time.Update(_time_ms / 1000);
  }

```

1. Define `DEBUG_INDICATOR_TICK_LOG` only in debug builds or when you explicitly need detailed per-tick tracing. For example, in a central config/header used only for debugging, or via your build system’s preprocessor definitions.
2. Once the feature is fully validated, you may want to remove the guarded `Print` entirely if you no longer need per-tick tracing.
</issue_to_address>

### Comment 6
<location path="Indicator/tests/classes/IndicatorTfDummy.h" line_range="79" />
<code_context>
   }
 };

-#ifdef EMSCRIPTEN
+#ifdef __EMSCRIPTEN__
 #include <emscripten.h>
 #include <emscripten/bind.h>
</code_context>
<issue_to_address>
**suggestion (testing):** Add tests for the new WASM/EMSCRIPTEN bindings and time-handling changes (PlatformTime/Indicator time plumbing).

These changes alter how time is sourced and propagated for the WASM build (e.g., PlatformTime depending on the current tick indicator, new GetTimeCurrent/UpdateLastTickTimeMs behavior, INDI_EMITTED_ENTRY_TYPE_TICK in EmitEntry), but there are no new tests validating this, particularly in the C++/WASM path.

Please add a dedicated C++/WASM test (e.g., `Indicator/tests/PlatformTime.test.cpp`) that:
- Runs a Tick provider plus one or more indicators under the WASM-aware test harness.
- Asserts that `TimeCurrent()`, `PlatformTime::TimeCurrent()`, and `IndicatorBase::GetTimeCurrent()` all return the tick time from the Tick indicator.
- Covers edge cases such as: no current tick indicator set, indicator hierarchies (time propagates from the Tick indicator), and multiple ticks with increasing timestamps to confirm DateTime/period-start behavior.

This will ensure the new WASM time plumbing matches MQL runtime expectations and catches regressions early.
</issue_to_address>

### Comment 7
<location path="Platform/PlatformTime.h" line_range="51" />
<code_context>
+class IndicatorBase;
+class IndicatorData;
+
 class PlatformTime {
-  static MqlDateTime current_time;
-  static int64 current_timestamp_s;
</code_context>
<issue_to_address>
**issue (complexity):** Consider keeping `PlatformTime` indicator-agnostic and moving platform-specific logic back into a dedicated implementation unit to reduce coupling and indirection.

You’ve increased coupling and indirection without needing to for the new feature. You can keep all functionality (indicators providing tick time) and simplify by pushing indicator‑specific logic out of `PlatformTime` and back into the indicator layer.

### 1. Remove indicator coupling from `PlatformTime`

Instead of storing an `IndicatorBase*` and accepting `IndicatorData*`, just expose primitive‑based APIs. Let indicators call these APIs, rather than `PlatformTime` knowing about indicators.

```cpp
// PlatformTime.h
class PlatformTime {
  static MqlDateTime current_time;
  static int64 current_timestamp_s;
  static int64 current_timestamp_ms;

public:
  static int64 TimeCurrent()      { return current_timestamp_s; }
  static int64 CurrentTimestampMs(){ return current_timestamp_ms; }
  static MqlDateTime CurrentTime(){ return current_time; }

  // Called by the indicator layer when it has a new tick time (ms since epoch).
  static void UpdateLastTickTimeMs(int64 tick_time_ms);
};
```

Then in the indicator code (where you *do* know about `IndicatorData`):

```cpp
// Indicator code (example)
void OnTick(IndicatorData* indi) {
  // however you derive ms since epoch from the indicator:
  int64 tick_ms = indi->GetTickTimeMs();
  PlatformTime::UpdateLastTickTimeMs(tick_ms);
}
```

This removes:

- `IndicatorBase* current_tick_indicator`
- `SetCurrentIndicator(IndicatorData* _indi)`
- `UpdateLastTickTimeMs(int64 tick_time_ms, IndicatorData* _source_indi)`

and the forward declarations `class IndicatorBase; class IndicatorData;` from `PlatformTime.h`, while preserving “tick time comes from indicators”.

### 2. Keep platform logic in one TU (avoid special inline header)

You can move the non‑MQL implementation back into a `.cpp` (or a single TU guarded by `#ifndef __MQL__`) to avoid the extra inline header indirection:

```cpp
// PlatformTime.cpp (non-MQL build)
#include "PlatformTime.h"
#include <chrono>
#include <ctime>

MqlDateTime PlatformTime::current_time = {0};
int64 PlatformTime::current_timestamp_s = 0;
int64 PlatformTime::current_timestamp_ms = 0;

void PlatformTime::UpdateLastTickTimeMs(int64 tick_time_ms) {
  using namespace std::chrono;
  current_timestamp_ms = tick_time_ms;
  current_timestamp_s = current_timestamp_ms / 1000;

  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;
}
```

For MQL, you can keep a thin wrapper that calls `UpdateLastTickTimeMs` with tick time derived from `TimeCurrent()` / `GetTickCount()`:

```cpp
// PlatformTime.mqh (MQL build, if needed)
class PlatformTime {
  // ... same static members ...
public:
  static void Tick() {
    // derive tick_time_ms using MQL APIs
    int64 tick_time_ms = /* TimeCurrent/GetTickCount combo */;
    UpdateLastTickTimeMs(tick_time_ms);
  }
};
```

This keeps `PlatformTime`:

- Indicator‑agnostic.
- Free of circular includes and forward declarations.
- Still capable of using indicator‑provided tick time via a simple, primitive API.
</issue_to_address>

### Comment 8
<location path="Indicator/IndicatorData.h" line_range="895" />
<code_context>
   /**
    * Returns number of points per pip.
    */
</code_context>
<issue_to_address>
**issue (complexity):** Consider letting the tick indicator store and update the current tick time directly instead of delegating through GetTick() and PlatformTime, simplifying ownership and data flow.

You can simplify this without changing behavior by making the tick indicator itself the clear owner of the “current tick time” and removing the delegation chain through `GetTick()`.

Right now:

- `IndicatorData::GetTimeCurrent()` → `GetTick()->GetTimeCurrent()`
- `IndicatorData::UpdateLastTickTimeMs()` → `GetTick()->UpdateLastTickTimeMs(...)`
- `EmitEntry()` → `GetTick()->UpdateLastTickTimeMs(_entry.timestamp * 1000)`

Combined with `PlatformTime` calling back into the indicator, this creates the circular indirection the other reviewer highlighted.

### 1. Store the tick time directly in `IndicatorData`

Give `IndicatorData` (specifically the tick indicator instance) a simple field to own the last tick time and implement the two methods directly, instead of delegating to `GetTick()`:

```cpp
// in IndicatorData (private or protected)
int64 last_tick_time_ms = 0;

// ...

// Returns time of the current tick.
datetime GetTimeCurrent() override {
  // If you need datetime in seconds:
  return (datetime)(last_tick_time_ms / 1000);
}

// Updates time of the last tick (ms).
void UpdateLastTickTimeMs(int64 _time_ms) override {
  last_tick_time_ms = _time_ms;
}
```

This removes the back-and-forth between different indicators for a value that is conceptually owned by the tick indicator.

### 2. Update `EmitEntry` to use the local setter

When emitting a tick entry, you can update the time via the local API, not via `GetTick()`:

```cpp
void EmitEntry(IndicatorDataEntry& _entry,
               ENUM_INDI_EMITTED_ENTRY_TYPE _type = INDI_EMITTED_ENTRY_TYPE_PARENT) {
  if (_type == INDI_EMITTED_ENTRY_TYPE_TICK) {
    // Tick indicator updates its own tick time
    UpdateLastTickTimeMs(_entry.timestamp * 1000);
  }

  for (int i = 0; i < ArraySize(listeners); ++i) {
    if (listeners[i].ObjectExists()) {
      listeners[i].Ptr() PTR_DEREF OnDataSourceEntry(_entry, _type);
    }
  }
}
```

If you still need a top-level “tick indicator in the hierarchy” abstraction, `GetTick()` can remain, but it no longer needs to be involved in the time plumbing.

### 3. Simplify `PlatformTime` usage (unidirectional flow)

With the above, `PlatformTime` can work with a primitive and avoid knowing about the indicator hierarchy:

- When a tick is emitted, push the time into `PlatformTime` directly:

```cpp
// wherever you integrate with PlatformTime:
if (_type == INDI_EMITTED_ENTRY_TYPE_TICK) {
  const int64 tick_time_ms = _entry.timestamp * 1000;
  UpdateLastTickTimeMs(tick_time_ms);       // indicator field
  PlatformTime::UpdateLastTickTimeMs(tick_time_ms); // platform field
}
```

- `PlatformTime` no longer needs to call back into the indicator to fetch/set `tick_time_ms`; it just stores the primitive. That allows you to:

  * remove the need for `PlatformTime` to know about `IndicatorData` internals
  * likely remove or at least reduce the need to include `PlatformTime.inline.h` at the end of `IndicatorData` to resolve circular dependencies

If you still need inline definitions, you can keep `PlatformTime` APIs purely in terms of primitive types:

```cpp
// PlatformTime.inline.h
inline void PlatformTime::UpdateLastTickTimeMs(int64 tick_time_ms) {
  last_tick_time_ms = tick_time_ms;
}

inline datetime PlatformTime::TimeCurrent() const {
  return (datetime)(last_tick_time_ms / 1000);
}
```

This keeps all functionality (tick indicators still drive both indicator hierarchy and `PlatformTime`), but the flow is now unidirectional and the coupling/indirection are significantly reduced.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread Std.h
Comment on lines +417 to 426
class _NULL_VALUE {
public:

// Explicit conversion operator for string
operator std::string() const {
return _empty_string;
}

template <typename T>
operator T() const {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The _NULL_VALUE class has conflicting template overloads and a duplicated global definition, which is likely to break compilation and violate the one-definition rule.

The third template <typename T> T as() const conflicts with the two SFINAE as overloads (same signature) and will cause redefinition/overload ambiguity, as well as contradicting their behavior.

You also both define and declare NULL_VALUE:

} NULL_VALUE;

extern _NULL_VALUE NULL_VALUE;

This violates the one-definition rule and can cause multiple-definition link errors or UB.

Please:

  • Remove the generic as<T>() and rely on the SFINAE overloads, or replace all three with a single templated as<T>() using if constexpr.
  • Keep only one definition of NULL_VALUE: an extern declaration in a header and a single non-extern definition in one translation unit (or adjust accordingly).


// 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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Defining the static member in this header will cause multiple definition errors when included in multiple translation units.

To fix this, either:

  • Mark the definition as inline (C++17+): inline IndicatorBase* PlatformTime::current_tick_indicator = nullptr;, ensuring all TUs see the same definition; or
  • Move the definition to a .cpp file and leave only static IndicatorBase* current_tick_indicator; declared in the header.

Relying on a single-TU convention for WASM is fragile; the second option is safer if this code may be reused in other builds.

Comment on lines 247 to +248
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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): BarOHLC equality comparison uses the right-hand bar's time instead of its open value, so equality checks are incorrect.

operator== currently reads:

bool operator==(const BarOHLC &_r) {
  return time == _r.time && open == (double)_r.time && high == _r.high && low == _r.low && close == _r.close;
}

It should compare open fields directly, e.g.:

return time == _r.time && open == _r.open && high == _r.high && low == _r.low && close == _r.close;

As written, any code using this for equality (e.g., deduplication or lookups) will behave incorrectly.

Comment on lines +45 to +52
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());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Using Print/DebugBreak in these time-accessors for non-error paths can be noisy and expensive in tight tick loops.

TimeCurrent() currently logs every call and uses DebugBreak() when current_tick_indicator is null. Given these are per‑tick calls and may be invoked by many indicators, this will flood logs and degrade performance, especially in backtests.

Please gate these prints behind a debug flag or remove them from non‑debug builds, and replace DebugBreak() in normal code paths with assertions or a dedicated error‑reporting mechanism so diagnostics remain useful without impacting runtime behavior.

Suggested implementation:

#endif

#include <cassert>

// Includes the full indicator types (safe here because IndicatorData.h is already processed).
#include "../Indicator/IndicatorBase.h"
#include "../Indicator/IndicatorData.h"
inline int64 PlatformTime::TimeCurrent() {
  if (current_tick_indicator == nullptr) {
#ifndef NDEBUG
    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.");
    assert(false && "PlatformTime::TimeCurrent called with null current_tick_indicator");
#endif
    return 0;
  }

#ifndef NDEBUG
  Print("Retrieving current time from current tick indicator: ", current_tick_indicator PTR_DEREF GetFullName(), " with time ", current_tick_indicator PTR_DEREF GetTimeCurrent());
#endif

  return current_tick_indicator PTR_DEREF GetTimeCurrent();
}

Comment thread Indicator/IndicatorTick.h
Comment on lines +121 to +122
virtual void UpdateLastTickTimeMs(int64 _time_ms) override {
Print("Updating last tick time (sec): ", _time_ms / 1000, " for indicator ", this->GetFullName());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Per-tick Print in IndicatorTick::UpdateLastTickTimeMs will severely impact performance and spam logs.

Because UpdateLastTickTimeMs is called for every tick, this log line will generate extremely large log volumes and noticeably slow both live and backtest runs.

Please either guard this behind a compile-time debug flag / debug-only build, or remove it once the feature is validated. If you still need detailed tracing, consider a rate-limited logger instead of unconditional per-tick printing.

Suggested implementation:

  /**
   * Updates time of the last tick. Called by EmitEntry() from the Tick indicator.
   * Logging is guarded by DEBUG_INDICATOR_TICK_LOG to avoid per-tick log spam in normal builds.
   */
  virtual void UpdateLastTickTimeMs(int64 _time_ms) override {
  #ifdef DEBUG_INDICATOR_TICK_LOG
    Print("Updating last tick time (sec): ", _time_ms / 1000, " for indicator ", this->GetFullName());
  #endif
    _last_tick_time.Update(_time_ms / 1000);
  }
  1. Define DEBUG_INDICATOR_TICK_LOG only in debug builds or when you explicitly need detailed per-tick tracing. For example, in a central config/header used only for debugging, or via your build system’s preprocessor definitions.
  2. Once the feature is fully validated, you may want to remove the guarded Print entirely if you no longer need per-tick tracing.

}
};

#ifdef EMSCRIPTEN
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Add tests for the new WASM/EMSCRIPTEN bindings and time-handling changes (PlatformTime/Indicator time plumbing).

These changes alter how time is sourced and propagated for the WASM build (e.g., PlatformTime depending on the current tick indicator, new GetTimeCurrent/UpdateLastTickTimeMs behavior, INDI_EMITTED_ENTRY_TYPE_TICK in EmitEntry), but there are no new tests validating this, particularly in the C++/WASM path.

Please add a dedicated C++/WASM test (e.g., Indicator/tests/PlatformTime.test.cpp) that:

  • Runs a Tick provider plus one or more indicators under the WASM-aware test harness.
  • Asserts that TimeCurrent(), PlatformTime::TimeCurrent(), and IndicatorBase::GetTimeCurrent() all return the tick time from the Tick indicator.
  • Covers edge cases such as: no current tick indicator set, indicator hierarchies (time propagates from the Tick indicator), and multiple ticks with increasing timestamps to confirm DateTime/period-start behavior.

This will ensure the new WASM time plumbing matches MQL runtime expectations and catches regressions early.

Comment thread Platform/PlatformTime.h
class IndicatorBase;
class IndicatorData;

class PlatformTime {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider keeping PlatformTime indicator-agnostic and moving platform-specific logic back into a dedicated implementation unit to reduce coupling and indirection.

You’ve increased coupling and indirection without needing to for the new feature. You can keep all functionality (indicators providing tick time) and simplify by pushing indicator‑specific logic out of PlatformTime and back into the indicator layer.

1. Remove indicator coupling from PlatformTime

Instead of storing an IndicatorBase* and accepting IndicatorData*, just expose primitive‑based APIs. Let indicators call these APIs, rather than PlatformTime knowing about indicators.

// PlatformTime.h
class PlatformTime {
  static MqlDateTime current_time;
  static int64 current_timestamp_s;
  static int64 current_timestamp_ms;

public:
  static int64 TimeCurrent()      { return current_timestamp_s; }
  static int64 CurrentTimestampMs(){ return current_timestamp_ms; }
  static MqlDateTime CurrentTime(){ return current_time; }

  // Called by the indicator layer when it has a new tick time (ms since epoch).
  static void UpdateLastTickTimeMs(int64 tick_time_ms);
};

Then in the indicator code (where you do know about IndicatorData):

// Indicator code (example)
void OnTick(IndicatorData* indi) {
  // however you derive ms since epoch from the indicator:
  int64 tick_ms = indi->GetTickTimeMs();
  PlatformTime::UpdateLastTickTimeMs(tick_ms);
}

This removes:

  • IndicatorBase* current_tick_indicator
  • SetCurrentIndicator(IndicatorData* _indi)
  • UpdateLastTickTimeMs(int64 tick_time_ms, IndicatorData* _source_indi)

and the forward declarations class IndicatorBase; class IndicatorData; from PlatformTime.h, while preserving “tick time comes from indicators”.

2. Keep platform logic in one TU (avoid special inline header)

You can move the non‑MQL implementation back into a .cpp (or a single TU guarded by #ifndef __MQL__) to avoid the extra inline header indirection:

// PlatformTime.cpp (non-MQL build)
#include "PlatformTime.h"
#include <chrono>
#include <ctime>

MqlDateTime PlatformTime::current_time = {0};
int64 PlatformTime::current_timestamp_s = 0;
int64 PlatformTime::current_timestamp_ms = 0;

void PlatformTime::UpdateLastTickTimeMs(int64 tick_time_ms) {
  using namespace std::chrono;
  current_timestamp_ms = tick_time_ms;
  current_timestamp_s = current_timestamp_ms / 1000;

  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;
}

For MQL, you can keep a thin wrapper that calls UpdateLastTickTimeMs with tick time derived from TimeCurrent() / GetTickCount():

// PlatformTime.mqh (MQL build, if needed)
class PlatformTime {
  // ... same static members ...
public:
  static void Tick() {
    // derive tick_time_ms using MQL APIs
    int64 tick_time_ms = /* TimeCurrent/GetTickCount combo */;
    UpdateLastTickTimeMs(tick_time_ms);
  }
};

This keeps PlatformTime:

  • Indicator‑agnostic.
  • Free of circular includes and forward declarations.
  • Still capable of using indicator‑provided tick time via a simple, primitive API.

Comment thread Indicator/IndicatorData.h
HasSpecificValueStorage(INDI_DATA_VS_TYPE_VOLUME) && HasSpecificValueStorage(INDI_DATA_VS_TYPE_TICK_VOLUME);
}

/**
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider letting the tick indicator store and update the current tick time directly instead of delegating through GetTick() and PlatformTime, simplifying ownership and data flow.

You can simplify this without changing behavior by making the tick indicator itself the clear owner of the “current tick time” and removing the delegation chain through GetTick().

Right now:

  • IndicatorData::GetTimeCurrent()GetTick()->GetTimeCurrent()
  • IndicatorData::UpdateLastTickTimeMs()GetTick()->UpdateLastTickTimeMs(...)
  • EmitEntry()GetTick()->UpdateLastTickTimeMs(_entry.timestamp * 1000)

Combined with PlatformTime calling back into the indicator, this creates the circular indirection the other reviewer highlighted.

1. Store the tick time directly in IndicatorData

Give IndicatorData (specifically the tick indicator instance) a simple field to own the last tick time and implement the two methods directly, instead of delegating to GetTick():

// in IndicatorData (private or protected)
int64 last_tick_time_ms = 0;

// ...

// Returns time of the current tick.
datetime GetTimeCurrent() override {
  // If you need datetime in seconds:
  return (datetime)(last_tick_time_ms / 1000);
}

// Updates time of the last tick (ms).
void UpdateLastTickTimeMs(int64 _time_ms) override {
  last_tick_time_ms = _time_ms;
}

This removes the back-and-forth between different indicators for a value that is conceptually owned by the tick indicator.

2. Update EmitEntry to use the local setter

When emitting a tick entry, you can update the time via the local API, not via GetTick():

void EmitEntry(IndicatorDataEntry& _entry,
               ENUM_INDI_EMITTED_ENTRY_TYPE _type = INDI_EMITTED_ENTRY_TYPE_PARENT) {
  if (_type == INDI_EMITTED_ENTRY_TYPE_TICK) {
    // Tick indicator updates its own tick time
    UpdateLastTickTimeMs(_entry.timestamp * 1000);
  }

  for (int i = 0; i < ArraySize(listeners); ++i) {
    if (listeners[i].ObjectExists()) {
      listeners[i].Ptr() PTR_DEREF OnDataSourceEntry(_entry, _type);
    }
  }
}

If you still need a top-level “tick indicator in the hierarchy” abstraction, GetTick() can remain, but it no longer needs to be involved in the time plumbing.

3. Simplify PlatformTime usage (unidirectional flow)

With the above, PlatformTime can work with a primitive and avoid knowing about the indicator hierarchy:

  • When a tick is emitted, push the time into PlatformTime directly:
// wherever you integrate with PlatformTime:
if (_type == INDI_EMITTED_ENTRY_TYPE_TICK) {
  const int64 tick_time_ms = _entry.timestamp * 1000;
  UpdateLastTickTimeMs(tick_time_ms);       // indicator field
  PlatformTime::UpdateLastTickTimeMs(tick_time_ms); // platform field
}
  • PlatformTime no longer needs to call back into the indicator to fetch/set tick_time_ms; it just stores the primitive. That allows you to:

    • remove the need for PlatformTime to know about IndicatorData internals
    • likely remove or at least reduce the need to include PlatformTime.inline.h at the end of IndicatorData to resolve circular dependencies

If you still need inline definitions, you can keep PlatformTime APIs purely in terms of primitive types:

// PlatformTime.inline.h
inline void PlatformTime::UpdateLastTickTimeMs(int64 tick_time_ms) {
  last_tick_time_ms = tick_time_ms;
}

inline datetime PlatformTime::TimeCurrent() const {
  return (datetime)(last_tick_time_ms / 1000);
}

This keeps all functionality (tick indicators still drive both indicator hierarchy and PlatformTime), but the flow is now unidirectional and the coupling/indirection are significantly reduced.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates EA31337’s cross-platform layer to better support C++/WASM builds (Emscripten), including changes to default “null” string handling and a refactor of how “current time” is sourced during indicator ticks.

Changes:

  • Standardize Emscripten build guards (__EMSCRIPTEN__) and adjust several bindings-related sections for WASM builds.
  • Refactor time handling to source “current time” from the currently ticking indicator/tick-provider chain (PlatformTime::TimeCurrent()), and propagate tick timestamps via EmitEntry(..., INDI_EMITTED_ENTRY_TYPE_TICK).
  • Replace NULL/0 string returns and default params with NULL_STRING, plus add MemoryFileSystem support for FileWriteArray().

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
Trade/TradeSignalManager.h Switch Emscripten macro guard to __EMSCRIPTEN__ for bindings.
Trade/TradeSignal.h Switch Emscripten macro guard to __EMSCRIPTEN__ for bindings.
Trade/tests/TradeSignalManager.test.cpp Adjust includes for C++ compilation test.
Trade.mqh Use NULL_STRING instead of NULL for optional symbol parameter.
Tick/Tick.struct.h Switch Emscripten macro guard to __EMSCRIPTEN__ for bindings.
Task/TaskManager.h Switch Emscripten macro guard to __EMSCRIPTEN__ for bindings.
Task/Task.struct.h Switch Emscripten macro guard to __EMSCRIPTEN__ for bindings.
Task/Task.h Switch Emscripten macro guard to __EMSCRIPTEN__ for bindings.
Storage/ValueStorage.indicator.h Fix indicator buffer fetch shift semantics (avoid RealShift() here).
Storage/MemoryFileSystem.h Add FileWriteArray() support for byte arrays.
Storage/ItemsHistory.h Replace IndicatorData.h include with forward declaration to avoid circular deps.
Storage/Dict/Dict.h Adjust internal types (position unsigned) and iterator base type.
Storage/DateTime.static.h Route “now” through PlatformTime::TimeCurrent() instead of old timestamp API.
Storage/DateTime.h Refactor DateTime construction/update and add optional timestamp update path.
Storage/DateTime.extern.h Add struct include and provide C++ TimeToStruct() implementation.
Storage/DateTime.entry.h Change initialization behavior and add GetTime() helper.
Storage/Array.h Replace NULL defaults with NULL_STRING / 0 in ArrayPrint.
Std.h Add REF_TYPE macro and refactor NULL_VALUE conversion helpers; update Emscripten guard.
Serializer/SerializerJson.h Return NULL_STRING instead of NULL for string-returning API.
Serializer/SerializerCsv.h Return NULL_STRING instead of NULL on error paths.
Refs.struct.h Switch Emscripten macro guard to __EMSCRIPTEN__; make operator== const.
Platform/Terminal.h Return NULL_STRING instead of 0 for string-returning stub.
Platform/PlatformTime.inline.h New inline definitions for PlatformTime based on tick-indicator time.
Platform/PlatformTime.h Replace prior static time storage with tick-indicator-based time API; include DateTime externs.
Platform/Platform.h Remove old platform-level DateTime period tracking; set current indicator for PlatformTime.
Platform/Platform.extern.h Switch DateTime include to DateTime.extern.h; add missing String extern include.
Platform/Orders.h Use NULL_STRING defaults for optional symbol parameters.
Platform/Order.struct.h Use NULL_STRING for default symbol; apply REF_TYPE assignment in proxies.
Platform/Order.h Return NULL_VALUE without casts in templated param getter.
Platform/Chart/Chart.struct.static.h Use NULL_STRING defaults for optional symbol parameters.
Platform/Chart/Chart.enum.h Switch Emscripten macro guard to __EMSCRIPTEN__ for bindings.
Platform/Chart/Bar.struct.h Modify BarOHLC::operator== comparison logic.
Indicators/Tick/Indi_TickProvider.h Emit tick entries with explicit tick type; fix Emscripten binding name/guard.
Indicators/Oscillator/Indi_RSI.h Use NULL_STRING defaults; add debug break for unsupported mode; switch Emscripten guard.
Indicator/tests/classes/IndicatorTfDummy.h Improve TimeToString formatting; switch Emscripten guard.
Indicator/IndicatorTick.h Track last tick time via DateTime and expose tick-based GetTimeCurrent().
Indicator/IndicatorTf.struct.h Initialize ChartTf via ctor initializer.
Indicator/IndicatorTf.h Ensure base-class construction happens for TF indicators.
Indicator/IndicatorData.h Add time propagation hooks; fix Emscripten binding arg index; include PlatformTime.inline.h.
Indicator/IndicatorBase.h Add virtual tick-time APIs; switch Emscripten guard.
Indicator/Indicator.h Adjust params assignment via REF_TYPE.
File.mqh Return NULL_STRING on read failure; fix FileOpen delimiter param type; use ARRAY(unsigned char, ...).
File.extern.h Add FileWriteArray() wrapper for MemoryFileSystem.
Exchange/SymbolInfo/SymbolInfo.struct.static.h Return NULL_STRING instead of 0 for string-returning stub.
Exchange/SymbolInfo/SymbolInfo.h Use NULL_STRING default symbol in ctor.
Exchange/Account/AccountMt.h Use NULL_STRING default symbol for margin check.
Convert.mqh Use NULL_STRING defaults for optional symbol parameters.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Std.h
Comment on lines +430 to +443
// Helper method to get value as type T - needed for template contexts
template <typename T>
typename std::enable_if<!std::is_same<T, string>::value, T>::type as() const {
return std::numeric_limits<T>::max();
}

template <typename T>
typename std::enable_if<std::is_same<T, string>::value, T>::type as() const {
return _empty_string;
}

template <typename T> T as() const {
return 0;
}
Comment thread Storage/DateTime.entry.h
Comment on lines +41 to 42
DateTimeEntry(bool _init_with_curr_time = true) { if (_init_with_curr_time) Set(); }
DateTimeEntry(datetime _dt) { Set(_dt); }
Comment thread Storage/DateTime.h
Comment on lines +218 to +226
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());
}
Comment thread Indicator/IndicatorTick.h
Comment on lines 86 to 99
@@ -92,7 +95,7 @@ class IndicatorTick : public Indicator<TS> {
Init();
}
IndicatorTick(string _symbol, ENUM_INDICATOR_TYPE _itype = INDI_CANDLE, int _shift = 0, string _name = "")
: Indicator<TS>(_itype, _shift, _name), history(THIS_PTR) {
: Indicator<TS>(_itype, _shift, _name), history(THIS_PTR), last_tick_time(false) {
symbol = _symbol;
// 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;
Comment on lines +52 to +54
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();
Comment thread Storage/DateTime.extern.h
Comment on lines +84 to +99
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;
}
Comment thread Storage/DateTime.extern.h
Comment on lines +84 to +97
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;

Comment thread Storage/DateTime.h
Comment on lines 267 to 278
#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;
}
Comment thread Platform/Platform.h
static ENUM_TIMEFRAMES period;

// Currently ticking indicator.
static IndicatorData* indi_current;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants