From 7c5c0b87f6f8db311261476c99c54c7ddbde0871 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Mon, 13 Oct 2025 21:20:59 +0300 Subject: [PATCH 01/48] remove obsolete input "EntryOrder.Distance" --- mql40/experts/ZigZag EA.mq4 | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 585eb3b8e..f1e192a77 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -103,7 +103,6 @@ extern string ___a__________________________ = "=== Signal settings ==="; extern int ZigZag.Periods = 30; extern string ___b__________________________ = "=== Trade settings ==="; -extern int EntryOrder.Distance = 0; // in punits: entry order distance from the signal level extern double Lots = 0.1; extern string ___c__________________________ = "=== Status ==="; @@ -1019,7 +1018,6 @@ bool SaveStatus() { WriteIniString(file, section, "Instance.StopAt", /*string */ Instance.StopAt); WriteIniString(file, section, "ZigZag.Periods", /*int */ ZigZag.Periods); WriteIniString(file, section, "Lots", /*double */ NumberToStr(Lots, ".+")); - WriteIniString(file, section, "EntryOrder.Distance", /*int */ EntryOrder.Distance); WriteIniString(file, section, "ShowProfitInPercent", /*bool */ ShowProfitInPercent); WriteIniString(file, section, "EA.Recorder", /*string */ EA.Recorder + separator); @@ -1088,7 +1086,6 @@ bool ReadStatus() { Instance.StopAt = GetIniStringA(file, section, "Instance.StopAt", ""); // string Instance.StopAt = @time(datetime|time) | @profit(numeric[%]) ZigZag.Periods = GetIniInt (file, section, "ZigZag.Periods" ); // int ZigZag.Periods = 40 Lots = GetIniDouble (file, section, "Lots" ); // double Lots = 0.1 - EntryOrder.Distance = GetIniInt (file, section, "EntryOrder.Distance" ); // int EntryOrder.Distance = 12 ShowProfitInPercent = GetIniBool (file, section, "ShowProfitInPercent" ); // bool ShowProfitInPercent = 1 EA.Recorder = GetIniStringA(file, section, "EA.Recorder", ""); // string EA.Recorder = 1,2,4 @@ -1295,7 +1292,6 @@ void BackupInputs() { prev.Instance.StopAt = StringConcatenate(Instance.StopAt, ""); prev.ZigZag.Periods = ZigZag.Periods; prev.Lots = Lots; - prev.EntryOrder.Distance = EntryOrder.Distance; prev.ShowProfitInPercent = ShowProfitInPercent; // affected runtime variables @@ -1330,7 +1326,6 @@ void RestoreInputs() { Instance.StopAt = prev.Instance.StopAt; ZigZag.Periods = prev.ZigZag.Periods; Lots = prev.Lots; - EntryOrder.Distance = prev.EntryOrder.Distance; ShowProfitInPercent = prev.ShowProfitInPercent; // affected runtime variables @@ -1497,16 +1492,11 @@ bool ValidateInputs() { if (LT(Lots, 0)) return(!onInputError("ValidateInputs(23) "+ instance.name +" invalid input parameter Lots: "+ NumberToStr(Lots, ".1+") +" (too small)")); if (NE(Lots, NormalizeLots(Lots))) return(!onInputError("ValidateInputs(24) "+ instance.name +" invalid input parameter Lots: "+ NumberToStr(Lots, ".1+") +" (not a multiple of MODE_LOTSTEP="+ NumberToStr(MarketInfo(Symbol(), MODE_LOTSTEP), ".+") +")")); - // EntryOrder.Distance - if (isInitParameters && EntryOrder.Distance!=prev.EntryOrder.Distance) { - if (instanceWasStarted) return(!onInputError("ValidateInputs(25) "+ instance.name +" cannot change input parameter EntryOrder.Distance of "+ StatusDescription(instance.status) +" instance")); - } - // EA.Recorder: on | off* | 1,2,3=1000,... if (!Recorder_ValidateInputs(IsTestInstance())) return(false); SS.All(); - return(!catch("ValidateInputs(26)")); + return(!catch("ValidateInputs(25)")); } @@ -1657,7 +1647,6 @@ string InputsToStr() { "ZigZag.Periods=", ZigZag.Periods, ";"+ NL + "Lots=", NumberToStr(Lots, ".1+"), ";"+ NL + - "EntryOrder.Distance=", EntryOrder.Distance, ";"+ NL + "ShowProfitInPercent=", BoolToStr(ShowProfitInPercent), ";") ); From 813caa4cdc854bbee727523335488c0359685a00 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Tue, 14 Oct 2025 06:37:16 +0300 Subject: [PATCH 02/48] documentation --- mql40/experts/ZigZag EA.mq4 | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index f1e192a77..ab5a207ce 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -1,27 +1,36 @@ /** - * An EA trading ZigZag reversals. + * ZigZag EA * - * The EA must not run permanently. If run permanently, it will not be profitable. - * Instead, it must be activated/deactivated depending on market sentiment. + * This EA trades ZigZag reversals. That's breakouts from a Donchian Channel which is the basis for ZigZag. * * * Requirements * ------------ - * • /mql4.0/indicators/ZigZag.mq4 + * • "mql40/experts/ZigZag EA.ex4" (this EA) + * • "mql40/indicators/ZigZag.ex4" (the MetaQuotes version is not compatible) + * • "mql40/scripts/Config.ex4" + * • "mql40/scripts/Chart.ToggleOpenOrders.ex4" + * • "mql40/scripts/Chart.ToggleTradeHistory.ex4" + * • "mql40/scripts/EA.ToggleMetrics.ex4" + * • "mql40/libraries/rsfMT4Expander.dll" + * • "mql40/libraries/rsfStdlib.ex4" + * • "mql40/libraries/rsfHistory[123].ex4" (three files) * * - * External control - * ---------------- + * Inputs + * ------ + * • ZigZag.Periods: Lookback periods of the Donchian channel. + * + * + * Manual control + * -------------- * • EA.Start: When a "start" command is received the EA opens a position in direction of the current ZigZag leg. There are * two sub-commands "start:long" and "start:short" to start the EA in a predefined direction. * The command is ignored if the EA already manages an open position. - * • EA.Stop: When a "stop" command is received the EA closes all open positions and stops waiting for new reversals. + * • EA.Stop: When a "stop" command is received the EA closes all open positions and stops trading. * The command is ignored if the EA is already in status "stopped". * • EA.Wait: When a "wait" command is received a stopped EA will wait for new reversals and start trading accordingly. * The command is ignored if the EA is already in status "waiting". - * • EA.ToggleMetrics - * • Chart.ToggleOpenOrders - * • Chart.ToggleTradeHistory * * * @@ -236,7 +245,7 @@ int onTick() { double signal[3]; if (__isChart) { - if (!HandleCommands()) return(last_error); // process incoming commands (may switch the instance on/off) + if (!HandleCommands()) return(last_error); // process incoming commands } if (instance.status != STATUS_STOPPED) { From c3f71ecd7d9aa2a5d2184522b7844e68106795c9 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Tue, 14 Oct 2025 06:43:21 +0300 Subject: [PATCH 03/48] update CI workflow --- .github/workflows/compile-mql.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/compile-mql.yml b/.github/workflows/compile-mql.yml index 5ce0cd1f3..8897a5a09 100644 --- a/.github/workflows/compile-mql.yml +++ b/.github/workflows/compile-mql.yml @@ -123,7 +123,6 @@ jobs: -d 'artifact-id=${{ steps.artifact.outputs.artifact-id }}')" echo "HTTP status: $status" cat "$response_file" - ((status >= 400)) && exit 1 || : # ------------------------------------------------------------------------------------------------------------------------------------ From 28ecaa33c595cb650dd96e97452a480ab7cbed97 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Tue, 14 Oct 2025 10:42:49 +0300 Subject: [PATCH 04/48] refactor EA --- mql40/experts/ZigZag EA.mq4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index ab5a207ce..e537198b6 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -250,7 +250,7 @@ int onTick() { if (instance.status != STATUS_STOPPED) { if (instance.status == STATUS_WAITING) { - if (IsStartSignal(signal)) { + if (IsTradeSignal(signal)) { StartTrading(signal); } } @@ -586,7 +586,7 @@ bool IsTradingTime() { * * @return bool */ -bool IsStartSignal(double &signal[]) { +bool IsTradeSignal(double &signal[]) { if (last_error || instance.status!=STATUS_WAITING) return(false); signal[SIG_TYPE ] = 0; signal[SIG_PRICE] = 0; From c5ddb371fa05489e8588bb4002d7eb9f21c9b93b Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Wed, 15 Oct 2025 11:27:55 +0300 Subject: [PATCH 05/48] update status display --- mql40/include/rsf/experts/status/SS.MetricDescription.mqh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mql40/include/rsf/experts/status/SS.MetricDescription.mqh b/mql40/include/rsf/experts/status/SS.MetricDescription.mqh index 99ba2f555..f1a3d5940 100644 --- a/mql40/include/rsf/experts/status/SS.MetricDescription.mqh +++ b/mql40/include/rsf/experts/status/SS.MetricDescription.mqh @@ -4,13 +4,13 @@ void SS.MetricDescription() { switch (status.activeMetric) { case METRIC_NET_MONEY: - status.metricDescription = "Net PnL after all costs in "+ AccountCurrency() + NL + "-----------------------------------"; + status.metricDescription = "Net PnL in "+ AccountCurrency() + NL + "------------------"; break; case METRIC_NET_UNITS: - status.metricDescription = "Net PnL after all costs in "+ spUnit + NL + "---------------------------------"+ ifString(spUnit=="point", "--", ""); + status.metricDescription = "Net PnL in "+ spUnit + NL + "-----------------"+ ifString(spUnit=="point", "--", ""); break; case METRIC_SIG_UNITS: - status.metricDescription = "Signal PnL before spread/any costs in "+ spUnit + NL + "-------------------------------------------------"+ ifString(spUnit=="point", "--", ""); + status.metricDescription = "Signal PnL in "+ spUnit + NL + "--------------------"+ ifString(spUnit=="point", "--", ""); break; default: From 0e2384c2c0d6f05417390d207743b071d1ece3a7 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Thu, 16 Oct 2025 20:37:43 +0300 Subject: [PATCH 06/48] rename cmd EA.Start to EA.Entry and EA.Wait to EA.Start --- mql40/experts/ZigZag EA.mq4 | 65 +++++++++++++++---------------------- mql40/scripts/EA.Entry.mq4 | 44 +++++++++++++++++++++++++ mql40/scripts/EA.Wait.mq4 | 2 +- 3 files changed, 71 insertions(+), 40 deletions(-) create mode 100644 mql40/scripts/EA.Entry.mq4 diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index e537198b6..6286ef364 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -24,14 +24,12 @@ * * Manual control * -------------- - * • EA.Start: When a "start" command is received the EA opens a position in direction of the current ZigZag leg. There are - * two sub-commands "start:long" and "start:short" to start the EA in a predefined direction. - * The command is ignored if the EA already manages an open position. - * • EA.Stop: When a "stop" command is received the EA closes all open positions and stops trading. + * • EA.Start: When a "start" command is received a stopped EA switches to status "waiting", waits for new signals + * and trades accordingly. The command is ignored if the EA is not in status "stopped". + * • EA.Entry: When an "entry" command is received the EA switches to status "trading" and opens a position in direction + * of the current ZigZag leg. The command is ignored if the EA already is in status "trading". + * • EA.Stop: When a "stop" command is received the EA closes all open positions and switches to status "stopped". * The command is ignored if the EA is already in status "stopped". - * • EA.Wait: When a "wait" command is received a stopped EA will wait for new reversals and start trading accordingly. - * The command is ignored if the EA is already in status "waiting". - * * * * TODO: @@ -283,25 +281,20 @@ bool onCommand(string cmd, string params, int keys) { string fullCmd = cmd +":"+ params +":"+ keys; if (cmd == "start") { + switch (instance.status) { + case STATUS_STOPPED: + logInfo("onCommand(3) "+ instance.name +" "+ DoubleQuoteStr(fullCmd)); + instance.status = STATUS_WAITING; + return(SaveStatus()); + } + } + else if (cmd == "entry") { switch (instance.status) { case STATUS_WAITING: case STATUS_STOPPED: - string sDetail = " "; - int logLevel = LOG_INFO; - - double signal[3]; - signal[SIG_TYPE ] = 0; - signal[SIG_PRICE] = 0; - if (params == "long") signal[SIG_OP] = SIG_OP_LONG; - else if (params == "short") signal[SIG_OP] = SIG_OP_SHORT; - else { - signal[SIG_OP] = ifInt(GetZigZagTrend(0) > 0, SIG_OP_LONG, SIG_OP_SHORT); - if (params != "") { - sDetail = " skipping unsupported parameter in command "; - logLevel = LOG_NOTICE; - } - } - log("onCommand(1) "+ instance.name + sDetail + DoubleQuoteStr(fullCmd), NO_ERROR, logLevel); + double signal[] = {0,0,0}; + signal[SIG_OP] = ifInt(GetZigZagTrend(0) > 0, SIG_OP_LONG, SIG_OP_SHORT); + log("onCommand(1) "+ instance.name +" "+ DoubleQuoteStr(fullCmd), NO_ERROR, LOG_INFO); return(StartTrading(signal)); } } @@ -314,14 +307,6 @@ bool onCommand(string cmd, string params, int keys) { return(StopTrading(dNull)); } } - else if (cmd == "wait") { - switch (instance.status) { - case STATUS_STOPPED: - logInfo("onCommand(3) "+ instance.name +" "+ DoubleQuoteStr(fullCmd)); - instance.status = STATUS_WAITING; - return(SaveStatus()); - } - } else if (cmd == "toggle-metrics") { int direction = ifInt(keys & F_VK_SHIFT, METRIC_PREVIOUS, METRIC_NEXT); return(ToggleMetrics(direction, METRIC_NET_MONEY, METRIC_SIG_UNITS)); @@ -332,7 +317,9 @@ bool onCommand(string cmd, string params, int keys) { else if (cmd == "toggle-trade-history") { return(ToggleTradeHistory()); } - else return(!logNotice("onCommand(4) "+ instance.name +" unsupported command: "+ DoubleQuoteStr(fullCmd))); + else { + return(!logNotice("onCommand(4) "+ instance.name +" unsupported command: "+ DoubleQuoteStr(fullCmd))); + } return(!logWarn("onCommand(5) "+ instance.name +" cannot execute command "+ DoubleQuoteStr(fullCmd) +" in status "+ StatusToStr(instance.status))); } @@ -582,7 +569,7 @@ bool IsTradingTime() { /** * Whether conditions are fullfilled to start trading. * - * @param _Out_ double &signal[] - array receiving entry signal details + * @param _Out_ double &signal[] - array receiving the entry signal * * @return bool */ @@ -852,19 +839,19 @@ double stop.profitPct.AbsValue() { /** - * Stop trading and close open positions (if any). + * Close open positions and stop trading. Depending on the passed trigger condition status changes to "waiting" or "stopped". * - * @param double signal[] - signal infos causing the call + * @param double trigger[] - trigger condition causing the call * * @return bool - success status */ -bool StopTrading(double signal[]) { +bool StopTrading(double trigger[]) { if (last_error != NULL) return(false); if (instance.status!=STATUS_WAITING && instance.status!=STATUS_TRADING) return(!catch("StopTrading(1) "+ instance.name +" cannot stop "+ StatusDescription(instance.status) +" instance", ERR_ILLEGAL_STATE)); - int sigType = signal[SIG_TYPE]; - double sigPrice = signal[SIG_PRICE]; - int sigOp = signal[SIG_OP]; + int sigType = trigger[SIG_TYPE]; + double sigPrice = trigger[SIG_PRICE]; + int sigOp = trigger[SIG_OP]; // close an open position if (instance.status == STATUS_TRADING) { diff --git a/mql40/scripts/EA.Entry.mq4 b/mql40/scripts/EA.Entry.mq4 new file mode 100644 index 000000000..b2cfe6313 --- /dev/null +++ b/mql40/scripts/EA.Entry.mq4 @@ -0,0 +1,44 @@ +/** + * EA.Entry + * + * Send an "entry" command to a running EA. + */ +#include +int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; +int __DeinitFlags[]; +#include +#include + + +/** + * Main function + * + * @return int - error status + */ +int onStart() { + // supporting EAs maintain a chart object holding the instance id and the status + string sid="", status="", label="EA.status"; + bool isActive = false; + + // check chart for an active EA + if (ObjectFind(label) == 0) { + string text = StrTrim(ObjectDescription(label)); // format: {sid}|{status} + sid = StrLeftTo(text, "|"); + status = StrRightFrom(text, "|"); + isActive = (status!="" && status!="undefined"); + } + + if (isActive) { + if (__isTesting) Tester.Pause(); + + PlaySoundEx("Windows Notify.wav"); // confirm sending the command + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to trigger an \"entry\" for EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); + if (button != IDOK) return(catch("onStart(1)")); + SendChartCommand("EA.command", "entry"); + } + else { + PlaySoundEx("Windows Chord.wav"); + MessageBoxEx(ProgramName(), "No EA found.", MB_ICONEXCLAMATION|MB_OK); + } + return(catch("onStart(2)")); +} diff --git a/mql40/scripts/EA.Wait.mq4 b/mql40/scripts/EA.Wait.mq4 index 0558cdd21..ff63ab49f 100644 --- a/mql40/scripts/EA.Wait.mq4 +++ b/mql40/scripts/EA.Wait.mq4 @@ -32,7 +32,7 @@ int onStart() { if (__isTesting) Tester.Pause(); PlaySoundEx("Windows Notify.wav"); // confirm sending the command - int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to enable status \"waiting\" on EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to enable status \"wait\" on EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); if (button != IDOK) return(catch("onStart(1)")); SendChartCommand("EA.command", "wait"); } From 65cd477bbe16ffe608f00128030fba308f35811d Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Fri, 17 Oct 2025 00:27:54 +0300 Subject: [PATCH 07/48] documentation --- mql40/experts/ZigZag EA.mq4 | 25 +++++++++---------- .../experts/status/volatile/ToggleMetrics.mqh | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 6286ef364..09d74dd03 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -1,20 +1,23 @@ /** * ZigZag EA * - * This EA trades ZigZag reversals. That's breakouts from a Donchian Channel which is the basis for ZigZag. + * The EA trades ZigZag reversals. That's breakouts from a Donchian Channel which is the basis for ZigZag. * * * Requirements * ------------ - * • "mql40/experts/ZigZag EA.ex4" (this EA) - * • "mql40/indicators/ZigZag.ex4" (the MetaQuotes version is not compatible) - * • "mql40/scripts/Config.ex4" - * • "mql40/scripts/Chart.ToggleOpenOrders.ex4" - * • "mql40/scripts/Chart.ToggleTradeHistory.ex4" - * • "mql40/scripts/EA.ToggleMetrics.ex4" + * • "mql40/experts/ZigZag EA" - this EA + * • "mql40/indicators/ZigZag" - the MetaQuotes version is not compatible + * • "mql40/scripts/Config" + * • "mql40/scripts/Chart.ToggleOpenOrders" + * • "mql40/scripts/Chart.ToggleTradeHistory" + * • "mql40/scripts/EA.ToggleMetrics" + * • "mql40/scripts/EA.Start" + * • "mql40/scripts/EA.Entry" + * • "mql40/scripts/EA.Stop" * • "mql40/libraries/rsfMT4Expander.dll" - * • "mql40/libraries/rsfStdlib.ex4" - * • "mql40/libraries/rsfHistory[123].ex4" (three files) + * • "mql40/libraries/rsfStdlib" + * • "mql40/libraries/rsfHistory[123]" - three files * * * Inputs @@ -67,10 +70,6 @@ * - reduce slippage on reversal: Close+Open => Hedge+CloseBy * - reduce slippage on short reversal: enter market via StopSell * - * - trading functionality - * support command "wait" in status "progressing" - * reverse trading and command EA.Reverse - * * - performance tracking * notifications for price feed outages * diff --git a/mql40/include/rsf/experts/status/volatile/ToggleMetrics.mqh b/mql40/include/rsf/experts/status/volatile/ToggleMetrics.mqh index 685470b6c..e0dec2544 100644 --- a/mql40/include/rsf/experts/status/volatile/ToggleMetrics.mqh +++ b/mql40/include/rsf/experts/status/volatile/ToggleMetrics.mqh @@ -1,5 +1,5 @@ /** - * Toggle the EA display status between available metrics. + * Toggle the EA display between available metrics. * * @param int direction - METRIC_NEXT|METRIC_PREVIOUS * @param int minId - min metric id From 182250fc7f15085e582770744d100128155de32b Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sat, 1 Nov 2025 22:33:48 +0100 Subject: [PATCH 08/48] update chart templates --- templates4/03 Default (FF Brackets).tpl | 4 +- templates4/26 Ichimoku.tpl | 171 ++++++++++++++++++++++ 2 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 templates4/26 Ichimoku.tpl diff --git a/templates4/03 Default (FF Brackets).tpl b/templates4/03 Default (FF Brackets).tpl index f0b913717..3591c5103 100644 --- a/templates4/03 Default (FF Brackets).tpl +++ b/templates4/03 Default (FF Brackets).tpl @@ -103,7 +103,7 @@ name=Brackets flags=339 window_num=0 -TimeWindow=08:00-09:00 +TimeWindow=09:00-10:00 NumberOfBrackets=20 BracketsColor=9639167 ; DeepPink AutoConfiguration=0 @@ -120,7 +120,7 @@ name=Brackets flags=339 window_num=0 -TimeWindow=09:00-10:00 +TimeWindow=10:00-11:00 NumberOfBrackets=20 BracketsColor=16711680 ; Blue AutoConfiguration=0 diff --git a/templates4/26 Ichimoku.tpl b/templates4/26 Ichimoku.tpl new file mode 100644 index 000000000..420d0c940 --- /dev/null +++ b/templates4/26 Ichimoku.tpl @@ -0,0 +1,171 @@ + +symbol=GBPUSD +period=60 +digits=5 + +leftpos=9229 +scale=2 +graph=1 +fore=0 +grid=0 +volume=0 +ohlc=0 +askline=0 +days=0 +descriptions=1 +scroll=1 +shift=1 +shift_size=10 + +fixed_pos=620 +window_left=0 +window_top=0 +window_right=1292 +window_bottom=812 +window_type=3 + +background_color=16316664 +foreground_color=0 +barup_color=30720 +bardown_color=210 +bullcandle_color=30720 +bearcandle_color=210 +chartline_color=11119017 +volumes_color=30720 +grid_color=14474460 +askline_color=11823615 +stops_color=17919 + + +height=5000 +fixed_height=0 + + +name=main + + + +name=Custom Indicator + +name=Grid +flags=347 +window_num=0 + +show_data=0 + + + +name=Custom Indicator + +name=ChartInfos +flags=347 +window_num=0 + +show_data=0 + + + +name=Custom Indicator + +name=SuperBars +flags=339 +window_num=0 + +show_data=0 + + + +name=Custom Indicator + +name=Inside Bars +flags=339 +window_num=0 + +Timeframe=H1 +NumberOfInsideBars=3 + + +period_flags=3 +show_data=0 + + + +name=Custom Indicator + +name=Brackets +flags=339 +window_num=0 + +TimeWindow=09:00-10:00 +NumberOfBrackets=20 +BracketsColor=9639167 ; DeepPink +AutoConfiguration=0 + + +period_flags=7 +show_data=0 + + + +name=Custom Indicator + +name=Brackets +flags=339 +window_num=0 + +TimeWindow=10:00-11:00 +NumberOfBrackets=20 +BracketsColor=16711680 ; Blue +AutoConfiguration=0 + + +period_flags=7 +show_data=0 + + + +name=Ichimoku Kinko Hyo +tenkan=9 +kijun=26 +senkou=52 +color=4294967295 +style=0 +weight=1 +color2=4294967295 +style2=0 +weight2=1 +color3=4294967295 +style3=0 +weight3=1 +color4=16760576 +style4=2 +weight4=1 +color5=16760576 +style5=2 +weight5=1 +period_flags=0 +show_data=1 + + + +name=Custom Indicator + +name=Moving Average +flags=339 + +MA.Method=SMA | LWMA | EMA* | SMMA | ALMA +MA.Periods=144 +MA.Periods.Step=0 +Draw.Type=Line* | Dot +Draw.Width=3 +UpTrend.Color=65535 +DownTrend.Color=65535 +Background.Color=11119017 +ShowChartLegend=1 +AutoConfiguration=0 + + +show_data=1 + + + From 4bbcc435e46c04bc618e19443e31415fded0f9eb Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sat, 1 Nov 2025 23:35:59 +0200 Subject: [PATCH 09/48] update ShowStatus() --- mql40/include/rsf/experts/status/SS.ClosedTrades.mqh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh b/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh index ca426f1cb..c920dba9d 100644 --- a/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh +++ b/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh @@ -8,16 +8,17 @@ void SS.ClosedTrades() { } else { CalculateStats(); + string trades = " trade"+ Pluralize(size); switch (status.activeMetric) { case METRIC_NET_MONEY: status.closedTrades = size +" trades avg: "+ NumberToStr(stats[METRIC_NET_MONEY][S_TRADES_AVG_PROFIT], "R+.2") +" "+ AccountCurrency(); break; case METRIC_NET_UNITS: - status.closedTrades = size +" trades avg: "+ NumberToStr(stats[METRIC_NET_UNITS][S_TRADES_AVG_PROFIT]/pUnit, "R+."+ pDigits) +" "+ spUnit; + status.closedTrades = size + trades +" avg: "+ NumberToStr(stats[METRIC_NET_UNITS][S_TRADES_AVG_PROFIT]/pUnit, "R+."+ pDigits) +" "+ spUnit; break; case METRIC_SIG_UNITS: - status.closedTrades = size +" trades avg: "+ NumberToStr(stats[METRIC_SIG_UNITS][S_TRADES_AVG_PROFIT]/pUnit, "R+."+ pDigits) +" "+ spUnit; + status.closedTrades = size + trades +" avg: "+ NumberToStr(stats[METRIC_SIG_UNITS][S_TRADES_AVG_PROFIT]/pUnit, "R+."+ pDigits) +" "+ spUnit; break; default: From 2564a21c5e68ac41ed88b31215e1f8094dffacef Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sat, 1 Nov 2025 23:37:19 +0200 Subject: [PATCH 10/48] improve error handling of CalculateStats() --- .../rsf/experts/status/SS.ClosedTrades.mqh | 4 ++-- .../status/file/ReadStatus.TradeStats.mqh | 5 +++-- .../rsf/experts/trade/MovePositionToHistory.mqh | 2 +- .../rsf/experts/trade/stats/CalculateStats.mqh | 16 +++++++++------- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh b/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh index c920dba9d..ac7786e58 100644 --- a/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh +++ b/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh @@ -7,12 +7,12 @@ void SS.ClosedTrades() { status.closedTrades = "-"; } else { - CalculateStats(); + if (!CalculateStats()) return; string trades = " trade"+ Pluralize(size); switch (status.activeMetric) { case METRIC_NET_MONEY: - status.closedTrades = size +" trades avg: "+ NumberToStr(stats[METRIC_NET_MONEY][S_TRADES_AVG_PROFIT], "R+.2") +" "+ AccountCurrency(); + status.closedTrades = size + trades +" avg: "+ NumberToStr(stats[METRIC_NET_MONEY][S_TRADES_AVG_PROFIT], "R+.2") +" "+ AccountCurrency(); break; case METRIC_NET_UNITS: status.closedTrades = size + trades +" avg: "+ NumberToStr(stats[METRIC_NET_UNITS][S_TRADES_AVG_PROFIT]/pUnit, "R+."+ pDigits) +" "+ spUnit; diff --git a/mql40/include/rsf/experts/status/file/ReadStatus.TradeStats.mqh b/mql40/include/rsf/experts/status/file/ReadStatus.TradeStats.mqh index 2467a5a2f..7f7238621 100644 --- a/mql40/include/rsf/experts/status/file/ReadStatus.TradeStats.mqh +++ b/mql40/include/rsf/experts/status/file/ReadStatus.TradeStats.mqh @@ -35,6 +35,7 @@ bool ReadStatus.TradeStats(string file) { stats[METRIC_SIG_UNITS][S_MAX_ABS_DRAWDOWN] = GetIniDouble(file, section, "MaxAbsDrawdown") * pUnit; // double maxAbsDrawdown = -2345.6 stats[METRIC_SIG_UNITS][S_MAX_REL_DRAWDOWN] = GetIniDouble(file, section, "MaxRelDrawdown") * pUnit; // double maxRelDrawdown = -2345.6 - CalculateStats(); - return(!catch("ReadStatus.TradeStats(1)")); + if (CalculateStats()) + return(!catch("ReadStatus.TradeStats(1)")); + return(false); } diff --git a/mql40/include/rsf/experts/trade/MovePositionToHistory.mqh b/mql40/include/rsf/experts/trade/MovePositionToHistory.mqh index 18ee1bf4d..8e6b2fba2 100644 --- a/mql40/include/rsf/experts/trade/MovePositionToHistory.mqh +++ b/mql40/include/rsf/experts/trade/MovePositionToHistory.mqh @@ -201,7 +201,7 @@ bool MovePositionToHistory(datetime closeTime, double closePrice, double closePr } if (__isChart) { - CalculateStats(); + if (!CalculateStats()) return(false); SS.OpenLots(); SS.ClosedTrades(); } diff --git a/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh b/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh index f1fe159b9..84a7acec2 100644 --- a/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh +++ b/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh @@ -9,7 +9,7 @@ * * @param bool fullRecalculation [optional] - whether to process new history entries only or to perform a full recalculation * (default: new history entries only) - * @return void + * @return bool - success status * * * TODO: @@ -18,16 +18,16 @@ * - Zephyr Pain Index: https://investexcel.net/zephyr-pain-index/ * - Zephyr K-Ratio: http://web.archive.org/web/20210116024652/https://www.styleadvisor.com/resources/statfacts/zephyr-k-ratio * - * @link https://invidious.nerdvpn.de/watch?v=GhrxgbQnEEU# [Linear Regression by Hand] + * @link https://www.youtube.com/watch?v=GhrxgbQnEEU# [Linear Regression by Hand] */ -void CalculateStats(bool fullRecalculation = false) { +bool CalculateStats(bool fullRecalculation = false) { int hstTrades = ArrayRange(history, 0); int processedTrades = stats[1][S_TRADES]; if (!hstTrades || hstTrades < processedTrades || fullRecalculation) { processedTrades = 0; } - if (processedTrades >= hstTrades) return; + if (processedTrades >= hstTrades) return(true); int metrics = ArrayRange(stats, 0) - 1; int profitFields [] = {0, H_NETPROFIT_M, H_NETPROFIT_P, H_SIG_PROFIT_P }, iProfitField; @@ -187,10 +187,12 @@ void CalculateStats(bool fullRecalculation = false) { stats[m][S_LOSERS_AVG_DRAWDOWN ] = MathDiv(stats[m][S_LOSERS_SUM_DRAWDOWN ], stats[m][S_LOSERS ]); stats[m][S_PROFIT_FACTOR] = MathAbs(MathDiv(stats[m][S_WINNERS_GROSS_PROFIT], stats[m][S_LOSERS_GROSS_LOSS], 99999)); // 99999: alias for +Infinity - stats[m][S_SHARPE_RATIO ] = CalculateSharpeRatio(m); - stats[m][S_SORTINO_RATIO] = CalculateSortinoRatio(m); - stats[m][S_CALMAR_RATIO ] = CalculateCalmarRatio(m); + stats[m][S_SHARPE_RATIO ] = CalculateSharpeRatio(m); if (!stats[m][S_SHARPE_RATIO ]) return(false); + stats[m][S_SORTINO_RATIO] = CalculateSortinoRatio(m); if (!stats[m][S_SORTINO_RATIO]) return(false); + stats[m][S_CALMAR_RATIO ] = CalculateCalmarRatio(m); if (!stats[m][S_CALMAR_RATIO ]) return(false); } + + return(!catch("CalculateStats(1)")); } From b39a8078070b4ec0848db6bdb3f206ba67f1f2f6 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 00:38:04 +0200 Subject: [PATCH 11/48] fix weekend trading errors in CalculateStats() --- mql40/experts/ZigZag EA.mq4 | 22 ++++----- mql40/include/rsf/api.mqh | 3 +- .../experts/trade/stats/CalculateStats.mqh | 27 ++++++----- .../rsf/experts/trade/stats/defines.mqh | 2 +- mql40/include/rsf/stdfunctions.mqh | 48 ++++++++++++++----- 5 files changed, 65 insertions(+), 37 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 09d74dd03..243f21e8a 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -102,8 +102,8 @@ int __virtualTicks = 10000; // every 10 seconds to continue ope ////////////////////////////////////////////////////// Configuration //////////////////////////////////////////////////////// extern string Instance.ID = ""; // instance to load from a status file, format "[T]123" -extern string Instance.StartAt = "@time(01:02)"; // @time(datetime|time) -extern string Instance.StopAt = "@time(22:59)"; // @time(datetime|time) | @profit(numeric[%]) +extern string Instance.StartAt = "@time(00:02)"; // @time(datetime|time) +extern string Instance.StopAt = "@time(23:59)"; // @time(datetime|time) | @profit(numeric[%]) extern string ___a__________________________ = "=== Signal settings ==="; extern int ZigZag.Periods = 30; @@ -112,7 +112,7 @@ extern string ___b__________________________ = "=== Trade settings ==="; extern double Lots = 0.1; extern string ___c__________________________ = "=== Status ==="; -extern bool ShowProfitInPercent = false; // whether PnL is displayed in money or percent +extern bool ShowProfitInPercent = true; // whether PnL is displayed in money or percent ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1525,7 +1525,7 @@ void SS.StartStopConditions() { // start conditions string sValue = ""; if (start.time.descr != "") { - sValue = sValue + ifString(sValue=="", "", " | ") + ifString(start.time.condition, "@", "!") + start.time.descr; + sValue = sValue + ifString(sValue=="", "", " || ") + ifString(start.time.condition, "@", "!") + start.time.descr; } if (sValue == "") status.startConditions = "-"; else status.startConditions = sValue; @@ -1533,13 +1533,13 @@ void SS.StartStopConditions() { // stop conditions sValue = ""; if (stop.time.descr != "") { - sValue = sValue + ifString(sValue=="", "", " | ") + ifString(stop.time.condition, "@", "!") + stop.time.descr; + sValue = sValue + ifString(sValue=="", "", " || ") + ifString(stop.time.condition, "@", "!") + stop.time.descr; } if (stop.profitPct.descr != "") { - sValue = sValue + ifString(sValue=="", "", " | ") + ifString(stop.profitPct.condition, "@", "!") + stop.profitPct.descr; + sValue = sValue + ifString(sValue=="", "", " || ") + ifString(stop.profitPct.condition, "@", "!") + stop.profitPct.descr; } if (stop.profitPunit.descr != "") { - sValue = sValue + ifString(sValue=="", "", " | ") + ifString(stop.profitPunit.condition, "@", "!") + stop.profitPunit.descr; + sValue = sValue + ifString(sValue=="", "", " || ") + ifString(stop.profitPunit.condition, "@", "!") + stop.profitPunit.descr; } if (sValue == "") status.stopConditions = "-"; else status.stopConditions = sValue; @@ -1568,10 +1568,10 @@ int ShowStatus(int error = NO_ERROR) { string sStatus="", sError=""; switch (instance.status) { - case NULL: sStatus = " not initialized"; break; - case STATUS_WAITING: sStatus = " waiting"; break; - case STATUS_TRADING: sStatus = " trading"; break; - case STATUS_STOPPED: sStatus = " stopped"; break; + case NULL: sStatus = " not initialized"; break; + case STATUS_WAITING: sStatus = " waiting for signal"; break; + case STATUS_TRADING: sStatus = " trading"; break; + case STATUS_STOPPED: sStatus = " stopped"; break; default: return(catch("ShowStatus(1) "+ instance.name +" illegal instance status: "+ instance.status, ERR_ILLEGAL_STATE)); } diff --git a/mql40/include/rsf/api.mqh b/mql40/include/rsf/api.mqh index 929355660..f50979ac7 100644 --- a/mql40/include/rsf/api.mqh +++ b/mql40/include/rsf/api.mqh @@ -37,9 +37,10 @@ color colorOr(color value, color altValue);; string ColorToHtmlStr(color value);; string ColorToRGBStr(color value);; string ColorToStr(color value);; +int ComputeCalendarDays(datetime from, datetime to);; +int ComputeTradingDays(datetime from, datetime to);; void CopyMemory(int destination, int source, int bytes);; int CountDecimals(double number);; -int CountWorkdays(datetime from, datetime to);; bool CreateDirectory(string path, int flags);; string CreateString(int length);; datetime DateTime1(int year, int month=1, int day=1, int hours=0, int minutes=0, int seconds=0);; diff --git a/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh b/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh index 84a7acec2..d38d5f37f 100644 --- a/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh +++ b/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh @@ -152,15 +152,16 @@ bool CalculateStats(bool fullRecalculation = false) { } } - // calculate number of workdays the instance was running (don't use OpenTime/CloseTime) + // calculate number of trading days the instance was running (don't use OpenTime/CloseTime) datetime startTime = instance.started; datetime endTime = ifInt(instance.stopped, instance.stopped, Tick.time); - int workdays = CountWorkdays(startTime, endTime); + int days = ComputeCalendarDays(startTime, endTime); + if (days < 0) return(false); // calculate summaries, percentages, averages, ratios for (m=1; m <= metrics; m++) { - stats[m][S_TRADES ] = hstTrades; - stats[m][S_WORKDAYS] = workdays; + stats[m][S_TRADES] = hstTrades; + stats[m][S_DAYS ] = days; stats[m][S_TRADES_LONG_PCT ] = MathDiv(stats[m][S_TRADES_LONG ], stats[m][S_TRADES ]); stats[m][S_TRADES_SHORT_PCT ] = MathDiv(stats[m][S_TRADES_SHORT ], stats[m][S_TRADES ]); @@ -222,9 +223,9 @@ double CalculateSharpeRatio(int metric) { if (trades > ArrayRange(history, 0)) return(!catch("CalculateSharpeRatio(1) illegal value of stats["+ metric +"][S_TRADES]: "+ trades +" (out-of-range)", ERR_ILLEGAL_STATE)); // annualize total return - int workdays = stats[metric][S_WORKDAYS]; - if (workdays <= 0) return(!catch("CalculateSharpeRatio(2) illegal value of stats["+ metric +"][S_WORKDAYS]: "+ workdays +" (must be positive)", ERR_ILLEGAL_STATE)); - double annualizedReturn = totalReturn/workdays * 252; // commonly used number of working days + int days = stats[metric][S_DAYS]; + if (days <= 0) return(!catch("CalculateSharpeRatio(2) illegal value of stats["+ metric +"][S_DAYS]: "+ days +" (must be positive)", ERR_ILLEGAL_STATE)); + double annualizedReturn = totalReturn/days * 365; // prepare dataset for iStdDevOnArray() int profitFields[] = {0, H_NETPROFIT_M, H_NETPROFIT_P, H_SIG_PROFIT_P}, iProfit=profitFields[metric]; @@ -276,9 +277,9 @@ double CalculateSortinoRatio(int metric) { if (trades > ArrayRange(history, 0)) return(!catch("CalculateSortinoRatio(1) illegal value of stats["+ metric +"][S_TRADES]: "+ trades +" (out-of-range)", ERR_ILLEGAL_STATE)); // annualize total return - int workdays = stats[metric][S_WORKDAYS]; - if (workdays <= 0) return(!catch("CalculateSortinoRatio(2) illegal value of stats["+ metric +"][S_WORKDAYS]: "+ workdays +" (must be positive)", ERR_ILLEGAL_STATE)); - double annualizedReturn = totalReturn/workdays * 252; // commonly used number of working days + int days = stats[metric][S_DAYS]; + if (days <= 0) return(!catch("CalculateSortinoRatio(2) illegal value of stats["+ metric +"][S_DAYS]: "+ days +" (must be positive)", ERR_ILLEGAL_STATE)); + double annualizedReturn = totalReturn/days * 365; // prepare dataset for iStdDevOnArray() int profitFields[] = {0, H_NETPROFIT_M, H_NETPROFIT_P, H_SIG_PROFIT_P}, iProfit=profitFields[metric]; @@ -329,9 +330,9 @@ double CalculateCalmarRatio(int metric) { if (!trades) return(0); // annualize total return - int workdays = stats[metric][S_WORKDAYS]; - if (workdays <= 0) return(!catch("CalculateCalmarRatio(1) illegal value of stats["+ metric +"][S_WORKDAYS]: "+ workdays +" (must be positive)", ERR_ILLEGAL_STATE)); - double annualizedReturn = totalReturn/workdays * 252; // commonly used number of working days + int days = stats[metric][S_DAYS]; + if (days <= 0) return(!catch("CalculateCalmarRatio(1) illegal value of stats["+ metric +"][S_DAYS]: "+ days +" (must be positive)", ERR_ILLEGAL_STATE)); + double annualizedReturn = totalReturn/days * 365; // calculate final ratio double drawdown = stats[metric][S_MAX_REL_DRAWDOWN]; diff --git a/mql40/include/rsf/experts/trade/stats/defines.mqh b/mql40/include/rsf/experts/trade/stats/defines.mqh index c9c1305f0..4d2ad9a7f 100644 --- a/mql40/include/rsf/experts/trade/stats/defines.mqh +++ b/mql40/include/rsf/experts/trade/stats/defines.mqh @@ -13,7 +13,7 @@ double stats[4][77]; // trade statistics with metric #define S_SHARPE_RATIO 7 #define S_SORTINO_RATIO 8 #define S_CALMAR_RATIO 9 -#define S_WORKDAYS 10 // number of trading days the instance was running (for annualization) +#define S_DAYS 10 // number of calendar days the instance was running (for annualization) #define S_TRADES 11 // all closed trades #define S_TRADES_LONG 12 diff --git a/mql40/include/rsf/stdfunctions.mqh b/mql40/include/rsf/stdfunctions.mqh index 3fb33fb0f..1ab298f1c 100644 --- a/mql40/include/rsf/stdfunctions.mqh +++ b/mql40/include/rsf/stdfunctions.mqh @@ -2598,35 +2598,60 @@ int TimeYearEx(datetime time) { /** - * Count the number of non-weekend days between two times. This function doesn't account for Holidays. + * Compute the full number of days between two times. Includes weekends and Holidays. * - * @param datetime from - start time (the whole day is included in the calculation) - * @param datetime to - end time (the whole day is included in the calculation) + * @param datetime from - start time (the day is included in the result) + * @param datetime to - end time (the day is included in the result) * - * @return int + * @return int - number of days or EMPTY (-1) in case of errors */ -int CountWorkdays(datetime from, datetime to) { +int ComputeCalendarDays(datetime from, datetime to) { datetime startDate = from - from % DAYS; + datetime endDate = to - to % DAYS; + + if (startDate > endDate) { + return(_EMPTY(catch("ComputeCalendarDays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); + } + + int days = (endDate - startDate) / DAYS; + return(days); +} + + +/** + * Compute the number of trading days between two times. Doesn't account for Holidays. + * + * @param datetime from - start time (the day is included in the result) + * @param datetime to - end time (the day is included in the result) + * + * @return int - number of trading days or EMPTY (-1) in case of errors + */ +int ComputeTradingDays(datetime from, datetime to) { + datetime startDate = from - from % DAYS; + datetime endDate = to - to % DAYS; + if (startDate > endDate) { + return(_EMPTY(catch("ComputeTradingDays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); + } + int startDow = TimeDayOfWeekEx(startDate); if (startDow == SAT) { startDate += 2*DAYS; startDow = MON; } else if (startDow == SUN) { startDate += 1*DAY; startDow = MON; } - datetime endDate = to - to % DAYS; int endDow = TimeDayOfWeekEx(endDate); if (endDow == SAT) { endDate -= 1*DAY; endDow = FRI; } else if (endDow == SUN) { endDate -= 2*DAYS; endDow = FRI; } - int workdays = 0; + int tradingDays = 0; if (startDate <= endDate) { int days = (endDate-startDate)/DAYS + 1; - workdays = days/7 * 5; + tradingDays = days/7 * 5; days %= 7; if (days > 0) { if (endDow < startDow) days -= 2; - workdays += days; + tradingDays += days; } } - return(workdays); + return(tradingDays); } @@ -6657,9 +6682,10 @@ void __DummyCalls() { ColorToHtmlStr(NULL); ColorToStr(NULL); CompareDoubles(NULL, NULL); + ComputeCalendarDays(NULL, NULL); + ComputeTradingDays(NULL, NULL); CopyMemory(NULL, NULL, NULL); CountDecimals(NULL); - CountWorkdays(NULL, NULL); CreateDirectory(NULL, NULL); CreateString(NULL); DateTime1(NULL); From e27dfadd6ace8ab2dba23ee19b187968d02cf0c8 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 09:55:03 +0200 Subject: [PATCH 12/48] update command scripts --- mql40/scripts/Chart.ToggleAccountBalance.mq4 | 11 +++++--- mql40/scripts/Chart.ToggleOpenOrders.mq4 | 13 ++++++++-- mql40/scripts/Chart.ToggleTradeHistory.mq4 | 13 ++++++++-- mql40/scripts/Chart.ToggleUnitSize.mq4 | 12 ++++++--- mql40/scripts/CustomPositions.LogOrders.mq4 | 13 ++++++++-- .../scripts/CustomPositions.ToggleProfit.mq4 | 25 ++++++++++++++++--- mql40/scripts/CustomPositions.ToggleRisk.mq4 | 19 ++++++++++++-- mql40/scripts/EA.Entry.mq4 | 2 +- mql40/scripts/EA.Resume.mq4 | 9 +------ mql40/scripts/EA.Start Long.mq4 | 2 +- mql40/scripts/EA.Start Short.mq4 | 2 +- mql40/scripts/EA.Stop.mq4 | 9 +------ mql40/scripts/EA.ToggleMetrics.mq4 | 13 ++++++++-- mql40/scripts/ParameterStepper.Down.mq4 | 11 +++++--- mql40/scripts/ParameterStepper.Up.mq4 | 11 +++++--- mql40/scripts/SuperBars.TimeframeDown.mq4 | 21 ++++++++++------ mql40/scripts/SuperBars.TimeframeUp.mq4 | 20 ++++++++++----- 17 files changed, 148 insertions(+), 58 deletions(-) diff --git a/mql40/scripts/Chart.ToggleAccountBalance.mq4 b/mql40/scripts/Chart.ToggleAccountBalance.mq4 index 437d5bdf7..15f9aaa41 100644 --- a/mql40/scripts/Chart.ToggleAccountBalance.mq4 +++ b/mql40/scripts/Chart.ToggleAccountBalance.mq4 @@ -1,7 +1,7 @@ /** * Chart.ToggleAccountBalance * - * Send a command to the ChartInfos indicator to toggle the display of the account balance. + * Sends a command to the ChartInfos indicator in the current chart to toggle display of the account balance. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -16,7 +16,9 @@ int __DeinitFlags[]; * @return int - error status */ int onStart() { - string modifiers = ""; + string command = "toggle-open-orders"; + string params = ""; + string modifiers = ","; if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key @@ -25,7 +27,10 @@ int onStart() { if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); - SendChartCommand("ChartInfos.command", "toggle-account-balance::"+ StrRight(modifiers, -1)); + command = command +":"+ params +":"+ modifiers; + + SendChartCommand("ChartInfos.command", command); return(catch("onStart(1)")); } diff --git a/mql40/scripts/Chart.ToggleOpenOrders.mq4 b/mql40/scripts/Chart.ToggleOpenOrders.mq4 index 6bf063e01..8a89534e5 100644 --- a/mql40/scripts/Chart.ToggleOpenOrders.mq4 +++ b/mql40/scripts/Chart.ToggleOpenOrders.mq4 @@ -1,7 +1,7 @@ /** * Chart.ToggleOpenOrders * - * Send a command to a running EA or the ChartInfos indicator to toggle the display of open orders. + * Sends a command to an EA or the ChartInfos indicator in the current chart to toggle display of open orders. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -20,7 +20,16 @@ int onStart() { string command = "toggle-open-orders"; string params = ""; - string modifiers = ifString(IsVirtualKeyDown(VK_SHIFT), "VK_SHIFT", ""); + string modifiers = ","; + if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; + if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; + if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key + if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; + if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; + if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key + if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; + if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); command = command +":"+ params +":"+ modifiers; diff --git a/mql40/scripts/Chart.ToggleTradeHistory.mq4 b/mql40/scripts/Chart.ToggleTradeHistory.mq4 index d23be0f55..5d94b8cd2 100644 --- a/mql40/scripts/Chart.ToggleTradeHistory.mq4 +++ b/mql40/scripts/Chart.ToggleTradeHistory.mq4 @@ -1,7 +1,7 @@ /** * Chart.ToggleTradeHistory * - * Send a command to a running EA or the ChartInfos indicator to toggle the display of the trade history. + * Sends a command to an EA or the ChartInfos indicator in the current chart to toggle display of the trade history. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -21,7 +21,16 @@ int onStart() { string command = "toggle-trade-history"; string params = ""; - string modifiers = ifString(IsVirtualKeyDown(VK_SHIFT), "VK_SHIFT", ""); + string modifiers = ","; + if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; + if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; + if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key + if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; + if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; + if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key + if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; + if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); command = command +":"+ params +":"+ modifiers; diff --git a/mql40/scripts/Chart.ToggleUnitSize.mq4 b/mql40/scripts/Chart.ToggleUnitSize.mq4 index 89e46b094..9b833b208 100644 --- a/mql40/scripts/Chart.ToggleUnitSize.mq4 +++ b/mql40/scripts/Chart.ToggleUnitSize.mq4 @@ -1,7 +1,8 @@ /** * Chart.ToggleUnitSize * - * Sends a command to the ChartInfos indicator to toggle the location of the unitsize display between "top" and "bottom". + * Sends a command to the ChartInfos indicator in the current chart to toggle location of the displayed unitsize + * between "top" and "bottom". */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -16,7 +17,9 @@ int __DeinitFlags[]; * @return int - error status */ int onStart() { - string modifiers = ""; + string command = "toggle-unit-size"; + string params = ""; + string modifiers = ","; if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key @@ -25,7 +28,10 @@ int onStart() { if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); - SendChartCommand("ChartInfos.command", "toggle-unit-size::"+ StrRight(modifiers, -1)); + command = command +":"+ params +":"+ modifiers; + + SendChartCommand("ChartInfos.command", command); return(catch("onStart(1)")); } diff --git a/mql40/scripts/CustomPositions.LogOrders.mq4 b/mql40/scripts/CustomPositions.LogOrders.mq4 index 05dbd0545..e4f6005e8 100644 --- a/mql40/scripts/CustomPositions.LogOrders.mq4 +++ b/mql40/scripts/CustomPositions.LogOrders.mq4 @@ -1,7 +1,7 @@ /** * CustomPositions.LogOrders * - * Send a command to the ChartInfos indicator to log all order tickets of custom positions. + * Sends a command to the ChartInfos indicator in the current chart to log all order tickets of custom positions. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -18,7 +18,16 @@ int __DeinitFlags[]; int onStart() { string command = "log-custom-positions"; string params = ""; - string modifiers = ifString(IsVirtualKeyDown(VK_SHIFT), "VK_SHIFT", ""); + string modifiers = ","; + if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; + if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; + if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key + if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; + if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; + if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key + if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; + if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); command = command +":"+ params +":"+ modifiers; diff --git a/mql40/scripts/CustomPositions.ToggleProfit.mq4 b/mql40/scripts/CustomPositions.ToggleProfit.mq4 index a63cf88fa..0d4a955d0 100644 --- a/mql40/scripts/CustomPositions.ToggleProfit.mq4 +++ b/mql40/scripts/CustomPositions.ToggleProfit.mq4 @@ -1,6 +1,8 @@ /** - * Schickt dem ChartInfos-Indikator des aktuellen Charts die Nachricht, die Anzeige der PL-Beträge der Positionen von - * "absolut" zu "prozentual" umzuschaltem. + * CustomPositions.ToggleProfit + * + * Sends a command to the ChartInfos indicator in the current chart to toggle displayed profits of custom positions between + * absolute and percentage values. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -10,11 +12,26 @@ int __DeinitFlags[]; /** - * Main-Funktion + * Main function * * @return int - error status */ int onStart() { - SendChartCommand("ChartInfos.command", "toggle-profit"); + string command = "toggle-profit"; + string params = ""; + string modifiers = ","; + if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; + if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; + if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key + if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; + if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; + if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key + if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; + if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); + + command = command +":"+ params +":"+ modifiers; + + SendChartCommand("ChartInfos.command", command); return(catch("onStart(1)")); } diff --git a/mql40/scripts/CustomPositions.ToggleRisk.mq4 b/mql40/scripts/CustomPositions.ToggleRisk.mq4 index e69cd4acb..6909d7ae8 100644 --- a/mql40/scripts/CustomPositions.ToggleRisk.mq4 +++ b/mql40/scripts/CustomPositions.ToggleRisk.mq4 @@ -1,7 +1,7 @@ /** * CustomPositions.ToggleRisk * - * Send a command to the ChartInfos indicator to toggle the MaxRisk display of custom positions. + * Sends a command to the ChartInfos indicator in the current chart to toggle the MaxRisk display of custom positions. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -16,6 +16,21 @@ int __DeinitFlags[]; * @return int - error status */ int onStart() { - SendChartCommand("ChartInfos.command", "toggle-risk"); + string command = "toggle-risk"; + string params = ""; + string modifiers = ","; + if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; + if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; + if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key + if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; + if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; + if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key + if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; + if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); + + command = command +":"+ params +":"+ modifiers; + + SendChartCommand("ChartInfos.command", command); return(catch("onStart(1)")); } diff --git a/mql40/scripts/EA.Entry.mq4 b/mql40/scripts/EA.Entry.mq4 index b2cfe6313..d9480bff8 100644 --- a/mql40/scripts/EA.Entry.mq4 +++ b/mql40/scripts/EA.Entry.mq4 @@ -1,7 +1,7 @@ /** * EA.Entry * - * Send an "entry" command to a running EA. + * Send an "entry" command to an EA in the current chart. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; diff --git a/mql40/scripts/EA.Resume.mq4 b/mql40/scripts/EA.Resume.mq4 index cb2a5dcf0..4e5955ea1 100644 --- a/mql40/scripts/EA.Resume.mq4 +++ b/mql40/scripts/EA.Resume.mq4 @@ -1,7 +1,7 @@ /** * EA.Resume * - * Send a "resume" command to a running EA. + * Send a "resume" command to an EA in the current chart. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -42,10 +42,3 @@ int onStart() { } return(catch("onStart(2)")); } - - - - - - - diff --git a/mql40/scripts/EA.Start Long.mq4 b/mql40/scripts/EA.Start Long.mq4 index 9748ea603..57cefe1a5 100644 --- a/mql40/scripts/EA.Start Long.mq4 +++ b/mql40/scripts/EA.Start Long.mq4 @@ -1,7 +1,7 @@ /** * EA.Start Long * - * Send a "start:long" command to a running EA. + * Send a "start:long" command to an EA in the current chart. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; diff --git a/mql40/scripts/EA.Start Short.mq4 b/mql40/scripts/EA.Start Short.mq4 index 31260da57..dc412f419 100644 --- a/mql40/scripts/EA.Start Short.mq4 +++ b/mql40/scripts/EA.Start Short.mq4 @@ -1,7 +1,7 @@ /** * EA.Start Short * - * Send a "start:short" command to a running EA. + * Send a "start:short" command to an EA in the current chart. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; diff --git a/mql40/scripts/EA.Stop.mq4 b/mql40/scripts/EA.Stop.mq4 index 781898d7e..2e2d5e759 100644 --- a/mql40/scripts/EA.Stop.mq4 +++ b/mql40/scripts/EA.Stop.mq4 @@ -1,7 +1,7 @@ /** * EA.Stop * - * Send a "stop" command to a running EA. + * Sends a "stop" command to an EA in the current chart. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -42,10 +42,3 @@ int onStart() { } return(catch("onStart(2)")); } - - - - - - - diff --git a/mql40/scripts/EA.ToggleMetrics.mq4 b/mql40/scripts/EA.ToggleMetrics.mq4 index bf1011c55..6b0bc34f9 100644 --- a/mql40/scripts/EA.ToggleMetrics.mq4 +++ b/mql40/scripts/EA.ToggleMetrics.mq4 @@ -1,7 +1,7 @@ /** * EA.ToggleMetrics * - * Send a command to a running EA to toggle the status between calculated/displayed metrics. + * Sends a command to an EA in the current chart to toggle displayed status between available metrics. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -18,7 +18,16 @@ int __DeinitFlags[]; int onStart() { string command = "toggle-metrics"; string params = ""; - string modifiers = ifString(IsVirtualKeyDown(VK_SHIFT), "VK_SHIFT", ""); + string modifiers = ","; + if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; + if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; + if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key + if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; + if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; + if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key + if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; + if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); command = command +":"+ params +":"+ modifiers; diff --git a/mql40/scripts/ParameterStepper.Down.mq4 b/mql40/scripts/ParameterStepper.Down.mq4 index fc9bb1a24..5c4880ae4 100644 --- a/mql40/scripts/ParameterStepper.Down.mq4 +++ b/mql40/scripts/ParameterStepper.Down.mq4 @@ -1,7 +1,7 @@ /** * ParameterStepper Down * - * Send a command to listening programs to decrease a program-specific parameter. + * Sends a command to listening programs to decrease a program-specific parameter. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -18,7 +18,9 @@ int __DeinitFlags[]; int onStart() { if (__isTesting) Tester.Pause(); - string modifiers = ""; + string command = "parameter"; + string params = "down"; + string modifiers = ","; if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key @@ -27,7 +29,10 @@ int onStart() { if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); - SendChartCommand("ParameterStepper.command", "parameter:down:"+ StrRight(modifiers, -1)); + command = command +":"+ params +":"+ modifiers; + + SendChartCommand("ParameterStepper.command", command); return(catch("onStart(1)")); } diff --git a/mql40/scripts/ParameterStepper.Up.mq4 b/mql40/scripts/ParameterStepper.Up.mq4 index 31545ff1f..7cf16137f 100644 --- a/mql40/scripts/ParameterStepper.Up.mq4 +++ b/mql40/scripts/ParameterStepper.Up.mq4 @@ -1,7 +1,7 @@ /** * ParameterStepper Up * - * Send a command to listening programs to increase a program-specific parameter. + * Sends a command to listening programs to increase a program-specific parameter. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -18,7 +18,9 @@ int __DeinitFlags[]; int onStart() { if (__isTesting) Tester.Pause(); - string modifiers = ""; + string command = "parameter"; + string params = "up"; + string modifiers = ","; if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key @@ -27,7 +29,10 @@ int onStart() { if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); - SendChartCommand("ParameterStepper.command", "parameter:up:"+ StrRight(modifiers, -1)); + command = command +":"+ params +":"+ modifiers; + + SendChartCommand("ParameterStepper.command", command); return(catch("onStart(1)")); } diff --git a/mql40/scripts/SuperBars.TimeframeDown.mq4 b/mql40/scripts/SuperBars.TimeframeDown.mq4 index be76b6081..2540b5f84 100644 --- a/mql40/scripts/SuperBars.TimeframeDown.mq4 +++ b/mql40/scripts/SuperBars.TimeframeDown.mq4 @@ -1,7 +1,7 @@ /** * SuperBars Down * - * Send a command to the SuperBars indicator to switch to the next lower timeframe. + * Sends a command to the SuperBars indicator in the current chart to switch to the next lower timeframe. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -18,22 +18,29 @@ int __DeinitFlags[]; int onStart() { if (__isTesting) Tester.Pause(); - string modifiers = ""; + bool isVkShift = IsVirtualKeyDown(VK_SHIFT); + string command = "", params = "", modifiers = ","; if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key + if (isVkShift) modifiers = modifiers +",VK_SHIFT"; if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); - if (IsVirtualKeyDown(VK_SHIFT)) { - modifiers = modifiers +",VK_SHIFT"; - SendChartCommand("TrendBars.command", "barwidth:decrease:"+ StrRight(modifiers, -1)); + if (isVkShift) { + command = "barwidth"; + params = "decrease"; + command = command +":"+ params +":"+ modifiers; + SendChartCommand("TrendBars.command", command); } else { - SendChartCommand("SuperBars.command", "timeframe:down:"+ StrRight(modifiers, -1)); + command = "timeframe"; + params = "down"; + command = command +":"+ params +":"+ modifiers; + SendChartCommand("SuperBars.command", command); } return(catch("onStart(1)")); } - diff --git a/mql40/scripts/SuperBars.TimeframeUp.mq4 b/mql40/scripts/SuperBars.TimeframeUp.mq4 index 82507cb98..eb84f9188 100644 --- a/mql40/scripts/SuperBars.TimeframeUp.mq4 +++ b/mql40/scripts/SuperBars.TimeframeUp.mq4 @@ -1,7 +1,7 @@ /** * SuperBars Up * - * Send a command to the SuperBars indicator to switch to the next higher timeframe. + * Sends a command to the SuperBars indicator in the current chart to switch to the next higher timeframe. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -18,21 +18,29 @@ int __DeinitFlags[]; int onStart() { if (__isTesting) Tester.Pause(); - string modifiers = ""; + bool isVkShift = IsVirtualKeyDown(VK_SHIFT); + string command = "", params = "", modifiers = ","; if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key + if (isVkShift) modifiers = modifiers +",VK_SHIFT"; if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); - if (IsVirtualKeyDown(VK_SHIFT)) { - modifiers = modifiers +",VK_SHIFT"; - SendChartCommand("TrendBars.command", "barwidth:increase:"+ StrRight(modifiers, -1)); + if (isVkShift) { + command = "barwidth"; + params = "increase"; + command = command +":"+ params +":"+ modifiers; + SendChartCommand("TrendBars.command", command); } else { - SendChartCommand("SuperBars.command", "timeframe:up:"+ StrRight(modifiers, -1)); + command = "timeframe"; + params = "up"; + command = command +":"+ params +":"+ modifiers; + SendChartCommand("SuperBars.command", command); } return(catch("onStart(1)")); } From 0198f6aef701d4a3649eb0097857766e2ce600a0 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 10:09:20 +0000 Subject: [PATCH 13/48] add EA command to toggle the display between absolute and percentage profits --- config/hotkeys.ini | 2 +- mql40/scripts/EA.TogglePercent.mq4 | 39 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 mql40/scripts/EA.TogglePercent.mq4 diff --git a/config/hotkeys.ini b/config/hotkeys.ini index 9b41a9ffa..0069c4a12 100644 --- a/config/hotkeys.ini +++ b/config/hotkeys.ini @@ -12,6 +12,7 @@ Chart.ToggleTradeHistory=Alt+H Chart.ToggleUnitSize=Alt+D Config=Alt+C EA.ToggleMetrics=Alt+M +EA.TogglePercent=Alt+P Empty=Ctrl+P MarketWatch.Symbols=Ctrl+S ParameterStepper.Down=Alt+L @@ -20,4 +21,3 @@ ReopenAlerts=Alt+A SuperBars.TimeframeDown=Ctrl+L SuperBars.TimeframeUp=Ctrl+K - diff --git a/mql40/scripts/EA.TogglePercent.mq4 b/mql40/scripts/EA.TogglePercent.mq4 new file mode 100644 index 000000000..c26704d2a --- /dev/null +++ b/mql40/scripts/EA.TogglePercent.mq4 @@ -0,0 +1,39 @@ +/** + * EA.TogglePercent + * + * Sends a command to an EA in the current chart to toggle displayed profits between absolute and percentage values. + */ +#include +int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; +int __DeinitFlags[]; +#include +#include + + +/** + * Main function + * + * @return int - error status + */ +int onStart() { + string command = "toggle-percent"; + string params = ""; + string modifiers = ","; + if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; + if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; + if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key + if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; + if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; + if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key + if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; + if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; + modifiers = StrRight(modifiers, -1); + + command = command +":"+ params +":"+ modifiers; + + // send to an active EA + if (ObjectFind("EA.status") == 0) { + SendChartCommand("EA.command", command); + } + return(last_error); +} From 34d21ff0abe970f9ed3802366bc8b1ef8dde59d4 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 12:12:38 +0200 Subject: [PATCH 14/48] convert EOL --- .gitattributes | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/.gitattributes b/.gitattributes index fa6847ada..3f8067a46 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,37 +1,23 @@ # default: self-detect the content type (text vs. binary) -* text=auto - - -# enforce content type "text" to fix mixed line-endings in the repo -*.h text -*.htm text -*.html text -*.ini text -*.md text -*.mq4 text -*.mq5 text -*.mqh text -*.sh text -*.txt text +* text=auto # set content encodings -* encoding=UTF-8 -*.mqh encoding=CP1252 -*.mq4 encoding=CP1252 -*.mq5 encoding=CP1252 -*.tpl encoding=CP1252 +* encoding=UTF-8 +*.mqh encoding=CP1252 +*.mq4 encoding=CP1252 +*.mq5 encoding=CP1252 +*.tpl encoding=CP1252 # checkout with Windows line-endings (not yet supported by EGit) -*.tpl text eol=crlf -hotkeys.ini text eol=crlf +*.tpl text eol=crlf +hotkeys.ini text eol=crlf # Gitgub Linguist overrides -/etc/** linguist-detectable=false -/files/** linguist-detectable=false -/sounds/** linguist-detectable=false -/templates/** linguist-detectable=false -*.mqh linguist-language=MQL4 - +/etc/** linguist-detectable=false +/files/** linguist-detectable=false +/sounds/** linguist-detectable=false +/templates/** linguist-detectable=false +*.mqh linguist-language=MQL4 From f88bf3ed1eef3ab922df68d2842d9e8eb7e6c223 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 14:06:20 +0200 Subject: [PATCH 15/48] update chart templates --- templates4/26 Ichimoku.tpl | 171 ----------------------------------- 1 file changed, 171 deletions(-) delete mode 100644 templates4/26 Ichimoku.tpl diff --git a/templates4/26 Ichimoku.tpl b/templates4/26 Ichimoku.tpl deleted file mode 100644 index 420d0c940..000000000 --- a/templates4/26 Ichimoku.tpl +++ /dev/null @@ -1,171 +0,0 @@ - -symbol=GBPUSD -period=60 -digits=5 - -leftpos=9229 -scale=2 -graph=1 -fore=0 -grid=0 -volume=0 -ohlc=0 -askline=0 -days=0 -descriptions=1 -scroll=1 -shift=1 -shift_size=10 - -fixed_pos=620 -window_left=0 -window_top=0 -window_right=1292 -window_bottom=812 -window_type=3 - -background_color=16316664 -foreground_color=0 -barup_color=30720 -bardown_color=210 -bullcandle_color=30720 -bearcandle_color=210 -chartline_color=11119017 -volumes_color=30720 -grid_color=14474460 -askline_color=11823615 -stops_color=17919 - - -height=5000 -fixed_height=0 - - -name=main - - - -name=Custom Indicator - -name=Grid -flags=347 -window_num=0 - -show_data=0 - - - -name=Custom Indicator - -name=ChartInfos -flags=347 -window_num=0 - -show_data=0 - - - -name=Custom Indicator - -name=SuperBars -flags=339 -window_num=0 - -show_data=0 - - - -name=Custom Indicator - -name=Inside Bars -flags=339 -window_num=0 - -Timeframe=H1 -NumberOfInsideBars=3 - - -period_flags=3 -show_data=0 - - - -name=Custom Indicator - -name=Brackets -flags=339 -window_num=0 - -TimeWindow=09:00-10:00 -NumberOfBrackets=20 -BracketsColor=9639167 ; DeepPink -AutoConfiguration=0 - - -period_flags=7 -show_data=0 - - - -name=Custom Indicator - -name=Brackets -flags=339 -window_num=0 - -TimeWindow=10:00-11:00 -NumberOfBrackets=20 -BracketsColor=16711680 ; Blue -AutoConfiguration=0 - - -period_flags=7 -show_data=0 - - - -name=Ichimoku Kinko Hyo -tenkan=9 -kijun=26 -senkou=52 -color=4294967295 -style=0 -weight=1 -color2=4294967295 -style2=0 -weight2=1 -color3=4294967295 -style3=0 -weight3=1 -color4=16760576 -style4=2 -weight4=1 -color5=16760576 -style5=2 -weight5=1 -period_flags=0 -show_data=1 - - - -name=Custom Indicator - -name=Moving Average -flags=339 - -MA.Method=SMA | LWMA | EMA* | SMMA | ALMA -MA.Periods=144 -MA.Periods.Step=0 -Draw.Type=Line* | Dot -Draw.Width=3 -UpTrend.Color=65535 -DownTrend.Color=65535 -Background.Color=11119017 -ShowChartLegend=1 -AutoConfiguration=0 - - -show_data=1 - - - From 1e89e6710bc4edc02fcb6cf567f645127c838438 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 14:32:03 +0200 Subject: [PATCH 16/48] move input ShowProfitInPercent to hotkey --- mql40/experts/ZigZag EA.mq4 | 24 +++++++------------ .../experts/status/SS.MetricDescription.mqh | 7 +++++- .../rsf/experts/status/SS.ProfitStats.mqh | 2 +- .../rsf/experts/status/SS.TotalProfit.mqh | 4 ++-- mql40/include/rsf/experts/status/defines.mqh | 1 + .../status/volatile/RemoveVolatileStatus.mqh | 9 ++++++- .../status/volatile/RestoreVolatileStatus.mqh | 14 ++++++++++- .../status/volatile/StoreVolatileStatus.mqh | 7 ++++++ .../status/volatile/ToggleProfitUnit.mqh | 11 +++++++++ 9 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 mql40/include/rsf/experts/status/volatile/ToggleProfitUnit.mqh diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 243f21e8a..56466b4cc 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -111,9 +111,6 @@ extern int ZigZag.Periods = 30; extern string ___b__________________________ = "=== Trade settings ==="; extern double Lots = 0.1; -extern string ___c__________________________ = "=== Status ==="; -extern bool ShowProfitInPercent = true; // whether PnL is displayed in money or percent - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // framework @@ -177,6 +174,7 @@ extern bool ShowProfitInPercent = true; // whether #include #include #include +#include #include @@ -306,6 +304,9 @@ bool onCommand(string cmd, string params, int keys) { return(StopTrading(dNull)); } } + else if (cmd == "toggle-percent") { + return(ToggleProfitUnit()); + } else if (cmd == "toggle-metrics") { int direction = ifInt(keys & F_VK_SHIFT, METRIC_PREVIOUS, METRIC_NEXT); return(ToggleMetrics(direction, METRIC_NET_MONEY, METRIC_SIG_UNITS)); @@ -1013,7 +1014,6 @@ bool SaveStatus() { WriteIniString(file, section, "Instance.StopAt", /*string */ Instance.StopAt); WriteIniString(file, section, "ZigZag.Periods", /*int */ ZigZag.Periods); WriteIniString(file, section, "Lots", /*double */ NumberToStr(Lots, ".+")); - WriteIniString(file, section, "ShowProfitInPercent", /*bool */ ShowProfitInPercent); WriteIniString(file, section, "EA.Recorder", /*string */ EA.Recorder + separator); // trade stats @@ -1081,7 +1081,6 @@ bool ReadStatus() { Instance.StopAt = GetIniStringA(file, section, "Instance.StopAt", ""); // string Instance.StopAt = @time(datetime|time) | @profit(numeric[%]) ZigZag.Periods = GetIniInt (file, section, "ZigZag.Periods" ); // int ZigZag.Periods = 40 Lots = GetIniDouble (file, section, "Lots" ); // double Lots = 0.1 - ShowProfitInPercent = GetIniBool (file, section, "ShowProfitInPercent" ); // bool ShowProfitInPercent = 1 EA.Recorder = GetIniStringA(file, section, "EA.Recorder", ""); // string EA.Recorder = 1,2,4 // [Runtime status] @@ -1251,7 +1250,6 @@ string prev.Instance.StopAt = ""; int prev.ZigZag.Periods; double prev.Lots; int prev.EntryOrder.Distance; -bool prev.ShowProfitInPercent; // backed-up runtime variables affected by changing input parameters bool prev.start.time.condition; @@ -1287,7 +1285,6 @@ void BackupInputs() { prev.Instance.StopAt = StringConcatenate(Instance.StopAt, ""); prev.ZigZag.Periods = ZigZag.Periods; prev.Lots = Lots; - prev.ShowProfitInPercent = ShowProfitInPercent; // affected runtime variables prev.start.time.condition = start.time.condition; @@ -1321,7 +1318,6 @@ void RestoreInputs() { Instance.StopAt = prev.Instance.StopAt; ZigZag.Periods = prev.ZigZag.Periods; Lots = prev.Lots; - ShowProfitInPercent = prev.ShowProfitInPercent; // affected runtime variables start.time.condition = prev.start.time.condition; @@ -1636,13 +1632,11 @@ bool CreateStatusBox() { * @return string */ string InputsToStr() { - return(StringConcatenate("Instance.ID=", DoubleQuoteStr(Instance.ID), ";"+ NL + - "Instance.StartAt=", DoubleQuoteStr(Instance.StartAt), ";"+ NL + - "Instance.StopAt=", DoubleQuoteStr(Instance.StopAt), ";"+ NL + - - "ZigZag.Periods=", ZigZag.Periods, ";"+ NL + - "Lots=", NumberToStr(Lots, ".1+"), ";"+ NL + + return(StringConcatenate("Instance.ID=", DoubleQuoteStr(Instance.ID), ";"+ NL + + "Instance.StartAt=", DoubleQuoteStr(Instance.StartAt), ";"+ NL + + "Instance.StopAt=", DoubleQuoteStr(Instance.StopAt), ";"+ NL + - "ShowProfitInPercent=", BoolToStr(ShowProfitInPercent), ";") + "ZigZag.Periods=", ZigZag.Periods, ";"+ NL + + "Lots=", NumberToStr(Lots, ".1+"), ";") ); } diff --git a/mql40/include/rsf/experts/status/SS.MetricDescription.mqh b/mql40/include/rsf/experts/status/SS.MetricDescription.mqh index f1a3d5940..4d26785ab 100644 --- a/mql40/include/rsf/experts/status/SS.MetricDescription.mqh +++ b/mql40/include/rsf/experts/status/SS.MetricDescription.mqh @@ -4,7 +4,12 @@ void SS.MetricDescription() { switch (status.activeMetric) { case METRIC_NET_MONEY: - status.metricDescription = "Net PnL in "+ AccountCurrency() + NL + "------------------"; + if (status.profitInPercent) { + status.metricDescription = "Net PnL in %"+ NL + "----------------"; + } + else { + status.metricDescription = "Net PnL in "+ AccountCurrency() + NL + "------------------"; + } break; case METRIC_NET_UNITS: status.metricDescription = "Net PnL in "+ spUnit + NL + "-----------------"+ ifString(spUnit=="point", "--", ""); diff --git a/mql40/include/rsf/experts/status/SS.ProfitStats.mqh b/mql40/include/rsf/experts/status/SS.ProfitStats.mqh index c3dacea0c..cb9af5ffb 100644 --- a/mql40/include/rsf/experts/status/SS.ProfitStats.mqh +++ b/mql40/include/rsf/experts/status/SS.ProfitStats.mqh @@ -12,7 +12,7 @@ void SS.ProfitStats() { switch (metric) { case METRIC_NET_MONEY: - if (ShowProfitInPercent) { + if (status.profitInPercent) { sMaxProfit = NumberToStr(MathDiv(stats[metric][S_MAX_PROFIT ], instance.startEquity) * 100, "R+.2"); sMaxDrawdown = NumberToStr(MathDiv(stats[metric][S_MAX_ABS_DRAWDOWN], instance.startEquity) * 100, "R+.2"); } diff --git a/mql40/include/rsf/experts/status/SS.TotalProfit.mqh b/mql40/include/rsf/experts/status/SS.TotalProfit.mqh index 9a6237349..f21b78c10 100644 --- a/mql40/include/rsf/experts/status/SS.TotalProfit.mqh +++ b/mql40/include/rsf/experts/status/SS.TotalProfit.mqh @@ -10,8 +10,8 @@ void SS.TotalProfit() { int metric = status.activeMetric; switch (metric) { case METRIC_NET_MONEY: - if (ShowProfitInPercent) status.totalProfit = NumberToStr(MathDiv(stats[metric][S_TOTAL_PROFIT], instance.startEquity) * 100, "R+.2") +"%"; - else status.totalProfit = NumberToStr(stats[metric][S_TOTAL_PROFIT], "R+.2") +" "+ AccountCurrency(); + if (status.profitInPercent) status.totalProfit = NumberToStr(MathDiv(stats[metric][S_TOTAL_PROFIT], instance.startEquity) * 100, "R+.2") +"%"; + else status.totalProfit = NumberToStr(stats[metric][S_TOTAL_PROFIT], "R+.2") +" "+ AccountCurrency(); break; case METRIC_NET_UNITS: diff --git a/mql40/include/rsf/experts/status/defines.mqh b/mql40/include/rsf/experts/status/defines.mqh index 4f0580d11..7480fca4c 100644 --- a/mql40/include/rsf/experts/status/defines.mqh +++ b/mql40/include/rsf/experts/status/defines.mqh @@ -8,6 +8,7 @@ // volatile status vars string status.filename = ""; // filepath relative to the MQL sandbox directory +bool status.profitInPercent = true; int status.activeMetric = 1; bool status.showOpenOrders; bool status.showTradeHistory; diff --git a/mql40/include/rsf/experts/status/volatile/RemoveVolatileStatus.mqh b/mql40/include/rsf/experts/status/volatile/RemoveVolatileStatus.mqh index 1bd8e104b..c127279cf 100644 --- a/mql40/include/rsf/experts/status/volatile/RemoveVolatileStatus.mqh +++ b/mql40/include/rsf/experts/status/volatile/RemoveVolatileStatus.mqh @@ -13,6 +13,13 @@ bool RemoveVolatileStatus() { Chart.RestoreString(key, sValue, true); } + // bool status.profitInPercent + if (__isChart) { + key = name +".status.profitInPercent"; + bool bValue = RemoveWindowIntegerA(__ExecutionContext[EC.chart], key); + Chart.RestoreBool(key, bValue, true); + } + // int status.activeMetric if (__isChart) { key = name +".status.activeMetric"; @@ -23,7 +30,7 @@ bool RemoveVolatileStatus() { // bool status.showOpenOrders if (__isChart) { key = name +".status.showOpenOrders"; - bool bValue = RemoveWindowIntegerA(__ExecutionContext[EC.chart], key); + bValue = RemoveWindowIntegerA(__ExecutionContext[EC.chart], key); Chart.RestoreBool(key, bValue, true); } diff --git a/mql40/include/rsf/experts/status/volatile/RestoreVolatileStatus.mqh b/mql40/include/rsf/experts/status/volatile/RestoreVolatileStatus.mqh index f1c0b4ee6..45928e07a 100644 --- a/mql40/include/rsf/experts/status/volatile/RestoreVolatileStatus.mqh +++ b/mql40/include/rsf/experts/status/volatile/RestoreVolatileStatus.mqh @@ -24,11 +24,23 @@ bool RestoreVolatileStatus() { } } + // bool status.profitInPercent + if (__isChart) { + key = name +".status.profitInPercent"; + int iValue = GetWindowIntegerA(__ExecutionContext[EC.chart], key); + if (iValue != 0) { + status.profitInPercent = (iValue > 0); + } + else if (!Chart.RestoreBool(key, status.profitInPercent, false)) { + status.profitInPercent = true; // reset to default value + } + } + // int status.activeMetric if (__isChart) { key = name +".status.activeMetric"; while (true) { - int iValue = GetWindowIntegerA(__ExecutionContext[EC.chart], key); + iValue = GetWindowIntegerA(__ExecutionContext[EC.chart], key); if (iValue != 0) { if (iValue > 0 && iValue <= 3) { // valid metrics: 1-3 status.activeMetric = iValue; diff --git a/mql40/include/rsf/experts/status/volatile/StoreVolatileStatus.mqh b/mql40/include/rsf/experts/status/volatile/StoreVolatileStatus.mqh index 941511144..914741d83 100644 --- a/mql40/include/rsf/experts/status/volatile/StoreVolatileStatus.mqh +++ b/mql40/include/rsf/experts/status/volatile/StoreVolatileStatus.mqh @@ -15,6 +15,13 @@ bool StoreVolatileStatus() { Chart.StoreString(key, value); } + // bool status.profitInPercent + if (__isChart) { + key = name +".status.profitInPercent"; + SetWindowIntegerA(__ExecutionContext[EC.chart], key, ifInt(status.profitInPercent, 1, -1)); + Chart.StoreBool(key, status.profitInPercent); + } + // int status.activeMetric if (__isChart) { key = name +".status.activeMetric"; diff --git a/mql40/include/rsf/experts/status/volatile/ToggleProfitUnit.mqh b/mql40/include/rsf/experts/status/volatile/ToggleProfitUnit.mqh new file mode 100644 index 000000000..8f348f16d --- /dev/null +++ b/mql40/include/rsf/experts/status/volatile/ToggleProfitUnit.mqh @@ -0,0 +1,11 @@ +/** + * Toggle the displayed profit unit between absolute and percentage values. + * + * @return bool - success status + */ +bool ToggleProfitUnit() { + status.profitInPercent = !status.profitInPercent; + StoreVolatileStatus(); + SS.All(); + return(true); +} From 9c47a4893ff169959272c88efc9a8e3c697c40c0 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 16:59:06 +0200 Subject: [PATCH 17/48] bugfix in CountCalendarDays() --- mql40/experts/ZigZag EA.mq4 | 9 ++-- mql40/include/rsf/api.mqh | 4 +- .../experts/trade/stats/CalculateStats.mqh | 4 +- mql40/include/rsf/stdfunctions.mqh | 45 +++++++++---------- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 56466b4cc..27189e115 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -6,18 +6,19 @@ * * Requirements * ------------ - * • "mql40/experts/ZigZag EA" - this EA - * • "mql40/indicators/ZigZag" - the MetaQuotes version is not compatible + * • "mql40/experts/ZigZag EA" (this EA) + * • "mql40/indicators/ZigZag" (the MetaQuotes version is not suitable) * • "mql40/scripts/Config" * • "mql40/scripts/Chart.ToggleOpenOrders" * • "mql40/scripts/Chart.ToggleTradeHistory" - * • "mql40/scripts/EA.ToggleMetrics" * • "mql40/scripts/EA.Start" * • "mql40/scripts/EA.Entry" * • "mql40/scripts/EA.Stop" + * • "mql40/scripts/EA.TogglePercent" + * • "mql40/scripts/EA.ToggleMetrics" * • "mql40/libraries/rsfMT4Expander.dll" * • "mql40/libraries/rsfStdlib" - * • "mql40/libraries/rsfHistory[123]" - three files + * • "mql40/libraries/rsfHistory[123]" (three files) * * * Inputs diff --git a/mql40/include/rsf/api.mqh b/mql40/include/rsf/api.mqh index f50979ac7..fde11a5c5 100644 --- a/mql40/include/rsf/api.mqh +++ b/mql40/include/rsf/api.mqh @@ -37,10 +37,10 @@ color colorOr(color value, color altValue);; string ColorToHtmlStr(color value);; string ColorToRGBStr(color value);; string ColorToStr(color value);; -int ComputeCalendarDays(datetime from, datetime to);; -int ComputeTradingDays(datetime from, datetime to);; void CopyMemory(int destination, int source, int bytes);; +int CountCalendarDays(datetime from, datetime to);; int CountDecimals(double number);; +int CountTradingDays(datetime from, datetime to);; bool CreateDirectory(string path, int flags);; string CreateString(int length);; datetime DateTime1(int year, int month=1, int day=1, int hours=0, int minutes=0, int seconds=0);; diff --git a/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh b/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh index d38d5f37f..92ad03fbf 100644 --- a/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh +++ b/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh @@ -155,8 +155,8 @@ bool CalculateStats(bool fullRecalculation = false) { // calculate number of trading days the instance was running (don't use OpenTime/CloseTime) datetime startTime = instance.started; datetime endTime = ifInt(instance.stopped, instance.stopped, Tick.time); - int days = ComputeCalendarDays(startTime, endTime); - if (days < 0) return(false); + int days = CountCalendarDays(startTime, endTime); + if (!days) return(false); // calculate summaries, percentages, averages, ratios for (m=1; m <= metrics; m++) { diff --git a/mql40/include/rsf/stdfunctions.mqh b/mql40/include/rsf/stdfunctions.mqh index 1ab298f1c..b7fecb35d 100644 --- a/mql40/include/rsf/stdfunctions.mqh +++ b/mql40/include/rsf/stdfunctions.mqh @@ -2603,18 +2603,18 @@ int TimeYearEx(datetime time) { * @param datetime from - start time (the day is included in the result) * @param datetime to - end time (the day is included in the result) * - * @return int - number of days or EMPTY (-1) in case of errors + * @return int - number of calendar days (min 1) or NULL in case of errors */ -int ComputeCalendarDays(datetime from, datetime to) { +int CountCalendarDays(datetime from, datetime to) { + if (from > to) { + return(_EMPTY(catch("CountCalendarDays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); + } + datetime startDate = from - from % DAYS; datetime endDate = to - to % DAYS; - if (startDate > endDate) { - return(_EMPTY(catch("ComputeCalendarDays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); - } - int days = (endDate - startDate) / DAYS; - return(days); + return(days + 1); // include the end day } @@ -2624,32 +2624,29 @@ int ComputeCalendarDays(datetime from, datetime to) { * @param datetime from - start time (the day is included in the result) * @param datetime to - end time (the day is included in the result) * - * @return int - number of trading days or EMPTY (-1) in case of errors + * @return int - number of trading days (min 1) or NULL in case of errors */ -int ComputeTradingDays(datetime from, datetime to) { - datetime startDate = from - from % DAYS; - datetime endDate = to - to % DAYS; - if (startDate > endDate) { - return(_EMPTY(catch("ComputeTradingDays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); +int CountTradingDays(datetime from, datetime to) { + if (from > to) { + return(_EMPTY(catch("CountTradingDays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); } + datetime startDate = from - from % DAYS; int startDow = TimeDayOfWeekEx(startDate); if (startDow == SAT) { startDate += 2*DAYS; startDow = MON; } else if (startDow == SUN) { startDate += 1*DAY; startDow = MON; } + datetime endDate = to - to % DAYS; int endDow = TimeDayOfWeekEx(endDate); if (endDow == SAT) { endDate -= 1*DAY; endDow = FRI; } else if (endDow == SUN) { endDate -= 2*DAYS; endDow = FRI; } - int tradingDays = 0; - if (startDate <= endDate) { - int days = (endDate-startDate)/DAYS + 1; - tradingDays = days/7 * 5; - days %= 7; - if (days > 0) { - if (endDow < startDow) days -= 2; - tradingDays += days; - } + int days = (endDate-startDate)/DAYS + 1; // include the end day + int tradingDays = days/7 * 5; + days %= 7; + if (days > 0) { + if (endDow < startDow) days -= 2; + tradingDays += days; } return(tradingDays); } @@ -6682,10 +6679,10 @@ void __DummyCalls() { ColorToHtmlStr(NULL); ColorToStr(NULL); CompareDoubles(NULL, NULL); - ComputeCalendarDays(NULL, NULL); - ComputeTradingDays(NULL, NULL); CopyMemory(NULL, NULL, NULL); + CountCalendarDays(NULL, NULL); CountDecimals(NULL); + CountTradingDays(NULL, NULL); CreateDirectory(NULL, NULL); CreateString(NULL); DateTime1(NULL); From da474d5f2ff8b3912325d5bb89574f4060cf20cf Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 19:50:20 +0200 Subject: [PATCH 18/48] fix typo --- mql40/scripts/Chart.ToggleAccountBalance.mq4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mql40/scripts/Chart.ToggleAccountBalance.mq4 b/mql40/scripts/Chart.ToggleAccountBalance.mq4 index 15f9aaa41..0052fc6f3 100644 --- a/mql40/scripts/Chart.ToggleAccountBalance.mq4 +++ b/mql40/scripts/Chart.ToggleAccountBalance.mq4 @@ -16,7 +16,7 @@ int __DeinitFlags[]; * @return int - error status */ int onStart() { - string command = "toggle-open-orders"; + string command = "toggle-account-balance"; string params = ""; string modifiers = ","; if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; From 19836e78d47a70a42974e7d4b785f8b5ed1a59ad Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 21:49:01 +0200 Subject: [PATCH 19/48] bugfix if CalculateStats() is called in init() --- mql40/experts/ZigZag EA.mq4 | 26 +++++------ mql40/include/rsf/api.mqh | 4 +- .../experts/trade/MovePositionToHistory.mqh | 4 +- .../experts/trade/stats/CalculateStats.mqh | 20 ++++---- mql40/include/rsf/stdfunctions.mqh | 46 +++++++++---------- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 27189e115..f1533616d 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -1565,24 +1565,24 @@ int ShowStatus(int error = NO_ERROR) { string sStatus="", sError=""; switch (instance.status) { - case NULL: sStatus = " not initialized"; break; - case STATUS_WAITING: sStatus = " waiting for signal"; break; - case STATUS_TRADING: sStatus = " trading"; break; - case STATUS_STOPPED: sStatus = " stopped"; break; + case NULL: sStatus = " not initialized"; break; + case STATUS_WAITING: sStatus = " waiting for signal"; break; + case STATUS_TRADING: sStatus = " trading"; break; + case STATUS_STOPPED: sStatus = " stopped"; break; default: return(catch("ShowStatus(1) "+ instance.name +" illegal instance status: "+ instance.status, ERR_ILLEGAL_STATE)); } if (__STATUS_OFF) sError = StringConcatenate(" [switched off => ", ErrorDescription(__STATUS_OFF.reason), "]"); - string text = StringConcatenate(WindowExpertName(), " ID: ", sInstanceId, sStatus, sError, NL, - NL, - "Start: ", status.startConditions, NL, - "Stop: ", status.stopConditions, NL, - NL, - status.metricDescription, NL, - "Open: ", status.openLots, NL, - "Closed: ", status.closedTrades, NL, - "Profit: ", status.totalProfit, " ", status.profitStats, NL + string text = StringConcatenate(WindowExpertName(), " ID ", sInstanceId, sStatus, sError, NL, + NL, + "Start: ", status.startConditions, NL, + "Stop: ", status.stopConditions, NL, + NL, + status.metricDescription, NL, + "Open: ", status.openLots, NL, + "Closed: ", status.closedTrades, NL, + "Profit: ", status.totalProfit, " ", status.profitStats, NL ); // 3 lines margin-top for instrument and indicator legends diff --git a/mql40/include/rsf/api.mqh b/mql40/include/rsf/api.mqh index fde11a5c5..b56e752cf 100644 --- a/mql40/include/rsf/api.mqh +++ b/mql40/include/rsf/api.mqh @@ -38,9 +38,9 @@ string ColorToHtmlStr(color value);; string ColorToRGBStr(color value);; string ColorToStr(color value);; void CopyMemory(int destination, int source, int bytes);; -int CountCalendarDays(datetime from, datetime to);; +int CountDays(datetime from, datetime to);; int CountDecimals(double number);; -int CountTradingDays(datetime from, datetime to);; +int CountWeekdays(datetime from, datetime to);; bool CreateDirectory(string path, int flags);; string CreateString(int length);; datetime DateTime1(int year, int month=1, int day=1, int hours=0, int minutes=0, int seconds=0);; diff --git a/mql40/include/rsf/experts/trade/MovePositionToHistory.mqh b/mql40/include/rsf/experts/trade/MovePositionToHistory.mqh index 8e6b2fba2..3adc18869 100644 --- a/mql40/include/rsf/experts/trade/MovePositionToHistory.mqh +++ b/mql40/include/rsf/experts/trade/MovePositionToHistory.mqh @@ -135,7 +135,7 @@ bool MovePositionToHistory(datetime closeTime, double closePrice, double closePr // validate the aggregated record and add it to history[] if (a.fromTicket != 0) return(!catch("MovePositionToHistory(3) "+ instance.name +" fromTicket #"+ a.fromTicket +" not found in partialClose[]", ERR_ILLEGAL_STATE)); if (NE(a.part, 1, 2)) return(!catch("MovePositionToHistory(4) "+ instance.name +" not all partial closes from ticket #"+ open.ticket +" found (found "+ NumberToStr(a.part, ".1+") +" of 1.0)", ERR_ILLEGAL_STATE)); - a.lots = NormalizeDouble(a.lots, 2); // normalize calculated fields + a.lots = NormalizeDouble(a.lots, 2); // normalize calculated fields a.part = 1; a.slippageP = NormalizeDouble(a.slippageP, Digits); a.swapM = NormalizeDouble(a.swapM, 2); @@ -143,7 +143,7 @@ bool MovePositionToHistory(datetime closeTime, double closePrice, double closePr a.grossProfitM = NormalizeDouble(a.grossProfitM, 2); a.netProfitM = NormalizeDouble(a.netProfitM, 2); - // we can't use AddHistoryRecord() as it invalidates the cache used by CalculateStats(), thus negatively impacting test speed + // we can't use AddHistoryRecord() as it invalidates the cache used by CalculateStats(), thus negatively impacting tester speed i = ArrayRange(history, 0); ArrayResize(history, i+1); history[i][H_TICKET ] = a.ticket; diff --git a/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh b/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh index 92ad03fbf..d4f8d9122 100644 --- a/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh +++ b/mql40/include/rsf/experts/trade/stats/CalculateStats.mqh @@ -1,5 +1,5 @@ /** - * Update/re-calculate trade statistics. Most important numbers: + * Update/re-calculate statistics for the trade history. Most important numbers: * * - Profit factor = GrossProfit / GrossLoss * - MaxRelativeDrawdown = Max(EquityPeak - EquityValley) @@ -7,8 +7,8 @@ * - Sortino ratio = AnnualizedReturn / DownsideVolatility * - Calmar ratio = AnnualizedReturn / MaxRelativeDrawdown * - * @param bool fullRecalculation [optional] - whether to process new history entries only or to perform a full recalculation - * (default: new history entries only) + * @param bool recalculate [optional] - whether to process new history entries only or to perform a full recalculation + * (default: new history entries only) * @return bool - success status * * @@ -20,11 +20,11 @@ * * @link https://www.youtube.com/watch?v=GhrxgbQnEEU# [Linear Regression by Hand] */ -bool CalculateStats(bool fullRecalculation = false) { +bool CalculateStats(bool recalculate = false) { int hstTrades = ArrayRange(history, 0); int processedTrades = stats[1][S_TRADES]; - if (!hstTrades || hstTrades < processedTrades || fullRecalculation) { + if (!hstTrades || hstTrades < processedTrades || recalculate) { processedTrades = 0; } if (processedTrades >= hstTrades) return(true); @@ -152,11 +152,11 @@ bool CalculateStats(bool fullRecalculation = false) { } } - // calculate number of trading days the instance was running (don't use OpenTime/CloseTime) - datetime startTime = instance.started; - datetime endTime = ifInt(instance.stopped, instance.stopped, Tick.time); - int days = CountCalendarDays(startTime, endTime); - if (!days) return(false); + // calculate number of days the instance was trading + datetime startTime = history[0][H_OPENTIME]; + datetime endTime = history[i-1][H_CLOSETIME]; + int days = CountDays(startTime, endTime); + if (days < 1) return(false); // calculate summaries, percentages, averages, ratios for (m=1; m <= metrics; m++) { diff --git a/mql40/include/rsf/stdfunctions.mqh b/mql40/include/rsf/stdfunctions.mqh index b7fecb35d..7a48ec1aa 100644 --- a/mql40/include/rsf/stdfunctions.mqh +++ b/mql40/include/rsf/stdfunctions.mqh @@ -1237,7 +1237,7 @@ string ifString(bool condition, string thenValue, string elseValue) { /** - * Inlined color OR statement. Returns the first color or the second color if the first color is CLR_NONE. + * Inlined OR statement for colors. Returns the first value or the second one if the first value is CLR_NONE. * * @param color value * @param color altValue @@ -1252,7 +1252,7 @@ color colorOr(color value, color altValue) { /** - * Inlined integer OR statement. Returns the first parameter or the second parameter if the first parameter evaluates to NULL. + * Inlined OR statement for integers. Returns the first value or the second one if the first value evaluates to NULL. * * @param int value * @param int altValue @@ -1267,7 +1267,7 @@ int intOr(int value, int altValue) { /** - * Inlined double OR statement. Returns the first parameter or the second parameter if the first parameter evaluates to NULL. + * Inlined OR statement for doubles. Returns the first value or the second one if the first value evaluates to NULL. * * @param double value * @param double altValue @@ -1282,7 +1282,7 @@ double doubleOr(double value, double altValue) { /** - * Inlined string OR statement. Returns the first parameter or the second parameter if the first parameter evaluates to empty. + * Inlined OR statement for strings. Returns the first value or the second one if the first value evaluates to empty. * * @param string value * @param string altValue @@ -2598,37 +2598,37 @@ int TimeYearEx(datetime time) { /** - * Compute the full number of days between two times. Includes weekends and Holidays. + * Count the number of days covering two times. * - * @param datetime from - start time (the day is included in the result) - * @param datetime to - end time (the day is included in the result) + * @param datetime from - start time + * @param datetime to - end time * - * @return int - number of calendar days (min 1) or NULL in case of errors + * @return int - number of days (min 1) or EMPTY (-1) in case of errors */ -int CountCalendarDays(datetime from, datetime to) { +int CountDays(datetime from, datetime to) { if (from > to) { - return(_EMPTY(catch("CountCalendarDays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); + return(_EMPTY(catch("CountDays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); } datetime startDate = from - from % DAYS; datetime endDate = to - to % DAYS; int days = (endDate - startDate) / DAYS; - return(days + 1); // include the end day + return(days + 1); } /** - * Compute the number of trading days between two times. Doesn't account for Holidays. + * Count the number of weekdays (Monday-Fridy) covering two times. * - * @param datetime from - start time (the day is included in the result) - * @param datetime to - end time (the day is included in the result) + * @param datetime from - start time + * @param datetime to - end time * - * @return int - number of trading days (min 1) or NULL in case of errors + * @return int - number of weekdays or EMPTY (-1) in case of errors */ -int CountTradingDays(datetime from, datetime to) { +int CountWeekdays(datetime from, datetime to) { if (from > to) { - return(_EMPTY(catch("CountTradingDays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); + return(_EMPTY(catch("CountWeekdays(1) illegal time range of parameters from="+ TimeToStr(from, TIME_FULL) +" / to="+ TimeToStr(to, TIME_FULL), ERR_INVALID_PARAMETER))); } datetime startDate = from - from % DAYS; @@ -2641,14 +2641,14 @@ int CountTradingDays(datetime from, datetime to) { if (endDow == SAT) { endDate -= 1*DAY; endDow = FRI; } else if (endDow == SUN) { endDate -= 2*DAYS; endDow = FRI; } - int days = (endDate-startDate)/DAYS + 1; // include the end day - int tradingDays = days/7 * 5; + int days = CountDays(startDate, endDate); + int weekdays = days/7 * 5; days %= 7; if (days > 0) { if (endDow < startDow) days -= 2; - tradingDays += days; + weekdays += days; } - return(tradingDays); + return(weekdays); } @@ -6680,9 +6680,9 @@ void __DummyCalls() { ColorToStr(NULL); CompareDoubles(NULL, NULL); CopyMemory(NULL, NULL, NULL); - CountCalendarDays(NULL, NULL); + CountDays(NULL, NULL); CountDecimals(NULL); - CountTradingDays(NULL, NULL); + CountWeekdays(NULL, NULL); CreateDirectory(NULL, NULL); CreateString(NULL); DateTime1(NULL); From 1bc7797aba0316a609ee916df19d46834363fd3b Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 22:06:40 +0200 Subject: [PATCH 20/48] rename EA.Entry to EA.EntrySignal --- mql40/experts/ZigZag EA.mq4 | 16 ++++++++-------- .../scripts/{EA.Entry.mq4 => EA.EntrySignal.mq4} | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) rename mql40/scripts/{EA.Entry.mq4 => EA.EntrySignal.mq4} (84%) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index f1533616d..dac66e1ec 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -12,7 +12,7 @@ * • "mql40/scripts/Chart.ToggleOpenOrders" * • "mql40/scripts/Chart.ToggleTradeHistory" * • "mql40/scripts/EA.Start" - * • "mql40/scripts/EA.Entry" + * • "mql40/scripts/EA.EntrySignal" * • "mql40/scripts/EA.Stop" * • "mql40/scripts/EA.TogglePercent" * • "mql40/scripts/EA.ToggleMetrics" @@ -28,12 +28,12 @@ * * Manual control * -------------- - * • EA.Start: When a "start" command is received a stopped EA switches to status "waiting", waits for new signals - * and trades accordingly. The command is ignored if the EA is not in status "stopped". - * • EA.Entry: When an "entry" command is received the EA switches to status "trading" and opens a position in direction - * of the current ZigZag leg. The command is ignored if the EA already is in status "trading". - * • EA.Stop: When a "stop" command is received the EA closes all open positions and switches to status "stopped". - * The command is ignored if the EA is already in status "stopped". + * • EA.Start: When a "start" command is received a stopped EA switches to status "waiting", waits for new signals + * and trades accordingly. The command is ignored if the EA is not in status "stopped". + * • EA.EntrySignal: When an "entry-signal" command is received the EA switches to status "trading" and opens a position in + * direction of the current ZigZag leg. The command is ignored if the EA already is in status "trading". + * • EA.Stop: When a "stop" command is received the EA closes all open positions and switches to status "stopped". + * The command is ignored if the EA already is in status "stopped". * * * TODO: @@ -286,7 +286,7 @@ bool onCommand(string cmd, string params, int keys) { return(SaveStatus()); } } - else if (cmd == "entry") { + else if (cmd == "entry-signal") { switch (instance.status) { case STATUS_WAITING: case STATUS_STOPPED: diff --git a/mql40/scripts/EA.Entry.mq4 b/mql40/scripts/EA.EntrySignal.mq4 similarity index 84% rename from mql40/scripts/EA.Entry.mq4 rename to mql40/scripts/EA.EntrySignal.mq4 index d9480bff8..bc0fbf6dd 100644 --- a/mql40/scripts/EA.Entry.mq4 +++ b/mql40/scripts/EA.EntrySignal.mq4 @@ -1,7 +1,7 @@ /** - * EA.Entry + * EA.EntrySignal * - * Send an "entry" command to an EA in the current chart. + * Sends a simulated "entry signal" to an EA in the current chart. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -32,9 +32,9 @@ int onStart() { if (__isTesting) Tester.Pause(); PlaySoundEx("Windows Notify.wav"); // confirm sending the command - int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to trigger an \"entry\" for EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to trigger an \"entry-signal\" for EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); if (button != IDOK) return(catch("onStart(1)")); - SendChartCommand("EA.command", "entry"); + SendChartCommand("EA.command", "entry-signal"); } else { PlaySoundEx("Windows Chord.wav"); From 73f69c51c82232e6c179d0dcd377fd60b89a7285 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 2 Nov 2025 23:59:24 +0200 Subject: [PATCH 21/48] dynamically position the status box --- mql40/experts/ZigZag EA.mq4 | 13 +++++-- .../rsf/experts/status/CreateStatusBox_6.mqh | 3 +- .../rsf/experts/status/ResolveTopDistance.mqh | 35 +++++++++++++++++++ mql40/include/rsf/functions/chartlegend.mqh | 6 +--- mql40/include/rsf/stddefines.mqh | 4 +++ 5 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 mql40/include/rsf/experts/status/ResolveTopDistance.mqh diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index dac66e1ec..f54715f40 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -146,6 +146,7 @@ extern double Lots = 0.1; #include #include +#include #include #include #include @@ -1585,10 +1586,15 @@ int ShowStatus(int error = NO_ERROR) { "Profit: ", status.totalProfit, " ", status.profitStats, NL ); - // 3 lines margin-top for instrument and indicator legends - Comment(NL, NL, NL, text); + // some lines margin-top for instrument and indicator legends + Comment(NL, NL, NL, NL, NL, text); if (__CoreFunction == CF_INIT) WindowRedraw(); + // 0 legends: 20 + // 1 legends: 39 + // 2 legends: 58 => 4*NL + // 3 legends: 77 => 5*NL + // store status in the chart to enable sending of chart commands string label = "EA.status"; if (ObjectFind(label) != 0) { @@ -1612,7 +1618,8 @@ int ShowStatus(int error = NO_ERROR) { bool CreateStatusBox() { if (!__isChart) return(true); - int x[]={2, 102}, y=50, fontSize=76, sizeofX=ArraySize(x); + int x[]={2, 102}, fontSize=76, sizeofX=ArraySize(x); + int y = ResolveTopDistance(); color bgColor = LemonChiffon; for (int i=0; i < sizeofX; i++) { diff --git a/mql40/include/rsf/experts/status/CreateStatusBox_6.mqh b/mql40/include/rsf/experts/status/CreateStatusBox_6.mqh index f4b6ac937..e30827982 100644 --- a/mql40/include/rsf/experts/status/CreateStatusBox_6.mqh +++ b/mql40/include/rsf/experts/status/CreateStatusBox_6.mqh @@ -7,7 +7,8 @@ bool CreateStatusBox() { if (!__isChart) return(true); - int x[]={2, 66, 136}, y=50, fontSize=54, sizeofX=ArraySize(x); + int x[]={2, 66, 136}, fontSize=54, sizeofX=ArraySize(x); + int y = ResolveTopDistance(); color bgColor = LemonChiffon; for (int i=0; i < sizeofX; i++) { diff --git a/mql40/include/rsf/experts/status/ResolveTopDistance.mqh b/mql40/include/rsf/experts/status/ResolveTopDistance.mqh new file mode 100644 index 000000000..75f7d69cb --- /dev/null +++ b/mql40/include/rsf/experts/status/ResolveTopDistance.mqh @@ -0,0 +1,35 @@ +/** + * Resolve the top-distance of the status box to create. The box is placed below all existing chart legends. + * + * @return int - offset from top + */ +int ResolveTopDistance() { + // count existing chart legends + int objects = ObjectsTotal(); + int labels = ObjectsTotal(OBJ_LABEL); + int prefixLength = StringLen(CHARTLEGEND_PREFIX); + int legends = 0; + + for (int i=objects-1; i >= 0 && labels; i--) { + string name = ObjectName(i); + + if (ObjectType(name) == OBJ_LABEL) { + if (StrStartsWith(name, CHARTLEGEND_PREFIX)) { + string data = StrRight(name, -prefixLength); + int pid = StrToInteger(data); + int hChart = StrToInteger(StrRightFrom(data, ".")); + + if (pid && hChart==__ExecutionContext[EC.chart]) { + legends++; + } + } + labels--; + } + } + + // calculate position of the next line + int yDist = 20; // y-position of the top-most legend + int lineHeight = 19; // line height of legends + int y = yDist + legends * lineHeight; + return(y); +} diff --git a/mql40/include/rsf/functions/chartlegend.mqh b/mql40/include/rsf/functions/chartlegend.mqh index 5d3914694..2d8613cc5 100644 --- a/mql40/include/rsf/functions/chartlegend.mqh +++ b/mql40/include/rsf/functions/chartlegend.mqh @@ -1,7 +1,3 @@ - -#define CHARTLEGEND_PREFIX "rsf.Legend." - - /** * Create a text label object in the main chart for an indicator's chart legend. * @@ -85,7 +81,7 @@ bool RearrangeChartLegends() { if (size > 0) { ArraySort(pids); for (i=0; i < size; i++) { - name = CHARTLEGEND_PREFIX + pids[i] +"."+ __ExecutionContext[EC.chart]; + name = StringConcatenate(CHARTLEGEND_PREFIX, pids[i], ".", __ExecutionContext[EC.chart]); ObjectSet(name, OBJPROP_CORNER, CORNER_TOP_LEFT); ObjectSet(name, OBJPROP_XDISTANCE, xDist); ObjectSet(name, OBJPROP_YDISTANCE, yDist + i*lineHeight); diff --git a/mql40/include/rsf/stddefines.mqh b/mql40/include/rsf/stddefines.mqh index d6bf9fe6d..71699b804 100644 --- a/mql40/include/rsf/stddefines.mqh +++ b/mql40/include/rsf/stddefines.mqh @@ -275,6 +275,10 @@ double INF; // 1.#INF | #define CLR_CLOSED Orange // exit marker +// prefix of chart legend labels, used by indicators and experts for ato-positioning +#define CHARTLEGEND_PREFIX "rsf.Legend." + + // timeseries identifiers, see ArrayCopySeries(), iLowest(), iHighest() #define MODE_OPEN 0 // open price #define MODE_LOW 1 // low price From b412ffbbdb3fc3c2ff9e5ae53f130e78e0c5566b Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Mon, 3 Nov 2025 10:49:40 +0200 Subject: [PATCH 22/48] rephrase command confirmations --- mql40/scripts/EA.EntrySignal.mq4 | 2 +- mql40/scripts/EA.Resume.mq4 | 2 +- mql40/scripts/EA.Start Long.mq4 | 2 +- mql40/scripts/EA.Start Short.mq4 | 2 +- mql40/scripts/EA.Start.mq4 | 2 +- mql40/scripts/EA.Stop.mq4 | 2 +- mql40/scripts/EA.Wait.mq4 | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mql40/scripts/EA.EntrySignal.mq4 b/mql40/scripts/EA.EntrySignal.mq4 index bc0fbf6dd..4bb59e7c9 100644 --- a/mql40/scripts/EA.EntrySignal.mq4 +++ b/mql40/scripts/EA.EntrySignal.mq4 @@ -32,7 +32,7 @@ int onStart() { if (__isTesting) Tester.Pause(); PlaySoundEx("Windows Notify.wav"); // confirm sending the command - int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to trigger an \"entry-signal\" for EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to send an \"entry-signal\" to the EA?", MB_ICONQUESTION|MB_OKCANCEL); if (button != IDOK) return(catch("onStart(1)")); SendChartCommand("EA.command", "entry-signal"); } diff --git a/mql40/scripts/EA.Resume.mq4 b/mql40/scripts/EA.Resume.mq4 index 4e5955ea1..628e93370 100644 --- a/mql40/scripts/EA.Resume.mq4 +++ b/mql40/scripts/EA.Resume.mq4 @@ -32,7 +32,7 @@ int onStart() { if (__isTesting) Tester.Pause(); PlaySoundEx("Windows Notify.wav"); // confirm sending the command - int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to resume EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to resume the EA?", MB_ICONQUESTION|MB_OKCANCEL); if (button != IDOK) return(catch("onStart(1)")); SendChartCommand("EA.command", "resume"); } diff --git a/mql40/scripts/EA.Start Long.mq4 b/mql40/scripts/EA.Start Long.mq4 index 57cefe1a5..c218a2a36 100644 --- a/mql40/scripts/EA.Start Long.mq4 +++ b/mql40/scripts/EA.Start Long.mq4 @@ -32,7 +32,7 @@ int onStart() { if (__isTesting) Tester.Pause(); PlaySoundEx("Windows Notify.wav"); // confirm sending the command - int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to start EA instance "+ sid +" long?", MB_ICONQUESTION|MB_OKCANCEL); + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want the EA to go long?", MB_ICONQUESTION|MB_OKCANCEL); if (button != IDOK) return(catch("onStart(1)")); SendChartCommand("EA.command", "start:long"); } diff --git a/mql40/scripts/EA.Start Short.mq4 b/mql40/scripts/EA.Start Short.mq4 index dc412f419..f77e3e184 100644 --- a/mql40/scripts/EA.Start Short.mq4 +++ b/mql40/scripts/EA.Start Short.mq4 @@ -32,7 +32,7 @@ int onStart() { if (__isTesting) Tester.Pause(); PlaySoundEx("Windows Notify.wav"); // confirm sending the command - int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to start EA instance "+ sid +" short?", MB_ICONQUESTION|MB_OKCANCEL); + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want the EA to go short?", MB_ICONQUESTION|MB_OKCANCEL); if (button != IDOK) return(catch("onStart(1)")); SendChartCommand("EA.command", "start:short"); } diff --git a/mql40/scripts/EA.Start.mq4 b/mql40/scripts/EA.Start.mq4 index 3af240525..62045da18 100644 --- a/mql40/scripts/EA.Start.mq4 +++ b/mql40/scripts/EA.Start.mq4 @@ -32,7 +32,7 @@ int onStart() { if (__isTesting) Tester.Pause(); PlaySoundEx("Windows Notify.wav"); // confirm sending the command - int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to start EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to start the EA?", MB_ICONQUESTION|MB_OKCANCEL); if (button != IDOK) return(catch("onStart(1)")); SendChartCommand("EA.command", "start"); } diff --git a/mql40/scripts/EA.Stop.mq4 b/mql40/scripts/EA.Stop.mq4 index 2e2d5e759..cce275663 100644 --- a/mql40/scripts/EA.Stop.mq4 +++ b/mql40/scripts/EA.Stop.mq4 @@ -32,7 +32,7 @@ int onStart() { if (__isTesting) Tester.Pause(); PlaySoundEx("Windows Notify.wav"); // confirm sending the command - int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to stop EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to stop the EA?", MB_ICONQUESTION|MB_OKCANCEL); if (button != IDOK) return(catch("onStart(1)")); SendChartCommand("EA.command", "stop"); } diff --git a/mql40/scripts/EA.Wait.mq4 b/mql40/scripts/EA.Wait.mq4 index ff63ab49f..21efafddb 100644 --- a/mql40/scripts/EA.Wait.mq4 +++ b/mql40/scripts/EA.Wait.mq4 @@ -32,7 +32,7 @@ int onStart() { if (__isTesting) Tester.Pause(); PlaySoundEx("Windows Notify.wav"); // confirm sending the command - int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to enable status \"wait\" on EA instance "+ sid +"?", MB_ICONQUESTION|MB_OKCANCEL); + int button = MessageBoxEx(ProgramName(), ifString(IsDemoFix(), "", "- Real Account -\n\n") +"Do you really want to switch the EA to status \"wait\"?", MB_ICONQUESTION|MB_OKCANCEL); if (button != IDOK) return(catch("onStart(1)")); SendChartCommand("EA.command", "wait"); } From 17fa09a199db70daff5b997e4f6667f2e8c29da0 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Mon, 3 Nov 2025 17:43:23 +0200 Subject: [PATCH 23/48] update input default values --- mql40/experts/ZigZag EA.mq4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index f54715f40..2223c919b 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -107,7 +107,7 @@ extern string Instance.StartAt = "@time(00:02)"; // @time(da extern string Instance.StopAt = "@time(23:59)"; // @time(datetime|time) | @profit(numeric[%]) extern string ___a__________________________ = "=== Signal settings ==="; -extern int ZigZag.Periods = 30; +extern int ZigZag.Periods = 100; extern string ___b__________________________ = "=== Trade settings ==="; extern double Lots = 0.1; From 0a4f98f1b904a966995571cf55b69a2742238ea4 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Mon, 3 Nov 2025 22:44:23 +0200 Subject: [PATCH 24/48] minor updates --- mql40/experts/ZigZag EA.mq4 | 20 ++++++++++--------- mql40/include/rsf/experts/init.mqh | 2 +- .../rsf/experts/instance/SetInstanceId.mqh | 2 +- .../rsf/experts/log/GetLogFilename.mqh | 2 +- .../experts/status/file/GetStatusFilename.mqh | 2 +- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 2223c919b..2dcdfe1fc 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -66,7 +66,6 @@ * GBPJPY,M5 2024.01.15-2024.02.02, ZigZag(30), ControlPoints: 3.0 sec, 93 trades 243.000 ticks * * - stop on reverse signal - * - signals MANUAL_LONG|MANUAL_SHORT * - track and display total slippage * - reduce slippage on reversal: Close+Open => Hedge+CloseBy * - reduce slippage on short reversal: enter market via StopSell @@ -74,9 +73,6 @@ * - performance tracking * notifications for price feed outages * - * - status display - * parameter: ZigZag.Periods - * * - trade breaks * - full session (24h) with trade breaks * - partial session (e.g. 09:00-16:00) with trade breaks @@ -1372,13 +1368,13 @@ bool ValidateInputs() { for (int i=0; i < sizeOfExprs; i++) { // validate each expression expr = StrTrim(exprs[i]); - if (!StringLen(expr)) continue; + if (expr == "") continue; if (StringGetChar(expr, 0) != '@') return(!onInputError("ValidateInputs(2) "+ instance.name +" invalid input parameter Instance.StartAt: "+ DoubleQuoteStr(Instance.StartAt))); if (Explode(expr, "(", sValues, NULL) != 2) return(!onInputError("ValidateInputs(3) "+ instance.name +" invalid input parameter Instance.StartAt: "+ DoubleQuoteStr(Instance.StartAt))); if (!StrEndsWith(sValues[1], ")")) return(!onInputError("ValidateInputs(4) "+ instance.name +" invalid input parameter Instance.StartAt: "+ DoubleQuoteStr(Instance.StartAt))); key = StrTrim(sValues[0]); sValue = StrTrim(StrLeft(sValues[1], -1)); - if (!StringLen(sValue)) return(!onInputError("ValidateInputs(5) "+ instance.name +" invalid input parameter Instance.StartAt: "+ DoubleQuoteStr(Instance.StartAt))); + if (sValue == "") return(!onInputError("ValidateInputs(5) "+ instance.name +" invalid input parameter Instance.StartAt: "+ DoubleQuoteStr(Instance.StartAt))); if (key == "@time") { if (isTimeCondition) return(!onInputError("ValidateInputs(6) "+ instance.name +" invalid input parameter Instance.StartAt: "+ DoubleQuoteStr(Instance.StartAt) +" (multiple time conditions)")); @@ -1411,13 +1407,13 @@ bool ValidateInputs() { for (i=0; i < sizeOfExprs; i++) { // validate each expression expr = StrTrim(exprs[i]); - if (!StringLen(expr)) continue; // support both OR operators "||" and "|" + if (expr == "") continue; // support both OR operators "||" and "|" if (StringGetChar(expr, 0) != '@') return(!onInputError("ValidateInputs(9) "+ instance.name +" invalid input parameter Instance.StopAt: "+ DoubleQuoteStr(Instance.StopAt))); if (Explode(expr, "(", sValues, NULL) != 2) return(!onInputError("ValidateInputs(10) "+ instance.name +" invalid input parameter Instance.StopAt: "+ DoubleQuoteStr(Instance.StopAt))); if (!StrEndsWith(sValues[1], ")")) return(!onInputError("ValidateInputs(11) "+ instance.name +" invalid input parameter Instance.StopAt: "+ DoubleQuoteStr(Instance.StopAt))); key = StrTrim(sValues[0]); sValue = StrTrim(StrLeft(sValues[1], -1)); - if (!StringLen(sValue)) return(!onInputError("ValidateInputs(12) "+ instance.name +" invalid input parameter Instance.StopAt: "+ DoubleQuoteStr(Instance.StopAt))); + if (sValue == "") return(!onInputError("ValidateInputs(12) "+ instance.name +" invalid input parameter Instance.StopAt: "+ DoubleQuoteStr(Instance.StopAt))); if (key == "@time") { if (isTimeCondition) return(!onInputError("ValidateInputs(13) "+ instance.name +" invalid input parameter Instance.StopAt: "+ DoubleQuoteStr(Instance.StopAt) +" (multiple time conditions)")); @@ -1511,7 +1507,13 @@ void SS.All() { * ShowStatus: Update the string representation of the instance name. */ void SS.InstanceName() { - instance.name = "Z."+ StrPadLeft(instance.id, 3, "0"); + if (!instance.id) { + // calling SS.All() and thus SS.InstanceName() before CreateInstanceId() is valid (e.g. after input validation of a new instance) + instance.name = "Z."; + } + else { + instance.name = "Z."+ StrPadLeft(instance.id, 3, "0"); + } } diff --git a/mql40/include/rsf/experts/init.mqh b/mql40/include/rsf/experts/init.mqh index 9849fa017..3248d7056 100644 --- a/mql40/include/rsf/experts/init.mqh +++ b/mql40/include/rsf/experts/init.mqh @@ -118,7 +118,7 @@ int onInitRecompile() { * @return int - error status */ int afterInit() { - if (__isTesting || !IsTestInstance()) { // open the log file (flushes the log buffer) except if a finished test + if (__isTesting || !IsTestInstance()) { // open the log file (flushes the log buffer) except if in a finished test string filename = GetLogFilename(); if (filename == "") return(last_error); if (!SetLogfile(filename)) return(catch("afterInit(1)")); diff --git a/mql40/include/rsf/experts/instance/SetInstanceId.mqh b/mql40/include/rsf/experts/instance/SetInstanceId.mqh index 4d2ccb48e..f25d13ce2 100644 --- a/mql40/include/rsf/experts/instance/SetInstanceId.mqh +++ b/mql40/include/rsf/experts/instance/SetInstanceId.mqh @@ -14,7 +14,7 @@ bool SetInstanceId(string value, bool &error, string caller) { error = false; value = StrTrim(value); - if (!StringLen(value)) return(false); + if (value == "") return(false); bool isTest = false; int instanceId = 0; diff --git a/mql40/include/rsf/experts/log/GetLogFilename.mqh b/mql40/include/rsf/experts/log/GetLogFilename.mqh index 7d0da3a6a..738ac29c0 100644 --- a/mql40/include/rsf/experts/log/GetLogFilename.mqh +++ b/mql40/include/rsf/experts/log/GetLogFilename.mqh @@ -5,6 +5,6 @@ */ string GetLogFilename() { string name = GetStatusFilename(); - if (!StringLen(name)) return(""); + if (name == "") return(""); return(StrLeftTo(name, ".", -1) +".log"); } diff --git a/mql40/include/rsf/experts/status/file/GetStatusFilename.mqh b/mql40/include/rsf/experts/status/file/GetStatusFilename.mqh index cf15ea2b4..af1b0c0cf 100644 --- a/mql40/include/rsf/experts/status/file/GetStatusFilename.mqh +++ b/mql40/include/rsf/experts/status/file/GetStatusFilename.mqh @@ -1,5 +1,5 @@ /** - * Return the name of the status file. If the name is not yet set, an attempt is made to find an existing status file. + * Return the name of the status file. If the name is not yet set, an attempt is made to find an existing file. * * @param bool relative [optional] - whether to return the absolute path or the path relative to the MQL "files" directory * (default: absolute path) From 5550c2eb2bdfdcf0d0f9f94517ded820bad1818c Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Tue, 4 Nov 2025 11:44:04 +0200 Subject: [PATCH 25/48] update chart templates --- templates4/03 Default (FF Brackets).tpl | 10 +++++----- templates4/04 Default (US Brackets).tpl | 4 ++-- templates4/23 ZigZag(10xM5).tpl | 4 ++-- templates4/24 ZigZag(10xM5) + NLMA.tpl | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/templates4/03 Default (FF Brackets).tpl b/templates4/03 Default (FF Brackets).tpl index 3591c5103..40476a81f 100644 --- a/templates4/03 Default (FF Brackets).tpl +++ b/templates4/03 Default (FF Brackets).tpl @@ -1,6 +1,6 @@ - @@ -103,7 +103,7 @@ name=Brackets flags=339 window_num=0 -TimeWindow=09:00-10:00 +TimeWindow=08:00-09:00 NumberOfBrackets=20 BracketsColor=9639167 ; DeepPink AutoConfiguration=0 @@ -120,7 +120,7 @@ name=Brackets flags=339 window_num=0 -TimeWindow=10:00-11:00 +TimeWindow=09:00-10:00 NumberOfBrackets=20 BracketsColor=16711680 ; Blue AutoConfiguration=0 diff --git a/templates4/04 Default (US Brackets).tpl b/templates4/04 Default (US Brackets).tpl index b28fa505a..79b5dbebf 100644 --- a/templates4/04 Default (US Brackets).tpl +++ b/templates4/04 Default (US Brackets).tpl @@ -1,5 +1,5 @@ - diff --git a/templates4/23 ZigZag(10xM5).tpl b/templates4/23 ZigZag(10xM5).tpl index 1aba1c1d7..400f49266 100644 --- a/templates4/23 ZigZag(10xM5).tpl +++ b/templates4/23 ZigZag(10xM5).tpl @@ -1,6 +1,6 @@ - diff --git a/templates4/24 ZigZag(10xM5) + NLMA.tpl b/templates4/24 ZigZag(10xM5) + NLMA.tpl index 3e76bc9b1..3cbcfebe2 100644 --- a/templates4/24 ZigZag(10xM5) + NLMA.tpl +++ b/templates4/24 ZigZag(10xM5) + NLMA.tpl @@ -1,6 +1,6 @@ -Ffy6;HsfiX6om&TZqvQ}vly+TvhkQ^e$K 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 X4*W1&T!*UPI;?$YM$GKp`Cj;c!Uq>< delta 102088 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 Date: Fri, 7 Nov 2025 13:59:42 +0200 Subject: [PATCH 29/48] remove obsolete tracking of max-lots/max-risk per custom position --- mql40/indicators/ChartInfos.mq4 | 66 ++------------------ mql40/scripts/CustomPositions.ToggleRisk.mq4 | 36 ----------- 2 files changed, 6 insertions(+), 96 deletions(-) delete mode 100644 mql40/scripts/CustomPositions.ToggleRisk.mq4 diff --git a/mql40/indicators/ChartInfos.mq4 b/mql40/indicators/ChartInfos.mq4 index 5f4f0bb83..4a9cfaee2 100644 --- a/mql40/indicators/ChartInfos.mq4 +++ b/mql40/indicators/ChartInfos.mq4 @@ -110,7 +110,7 @@ double config.terms[][5]; // @see Custom // data of configured custom positions: size(config.sData) == size(config.dData) == number-of-configured-custom-positions string config.sData[][2]; // data: {Key, Comment} -double config.dData[][8]; // data: {BemEnabled, MfaeEnabled, MfaeSignal, MfeMark, MfeValueM, MaeValueM, MaxLots, MaxRisk} +double config.dData[][6]; // data: {BemEnabled, MfaeEnabled, MfaeSignal, MfeMark, MfeValueM, MaeValueM} // indexes of config.sData[] #define I_CONFIG_KEY 0 // @@ -123,15 +123,12 @@ double config.dData[][8]; // data: {BemE #define I_MARK_MFE 3 // whether to mark MFE levels #define I_PROFIT_MFE 4 // MFE in money #define I_PROFIT_MAE 5 // MAE in money -#define I_MAX_LOTS 6 // -#define I_MAX_RISK 7 // // displayed custom position entries (may be larger than configured entries) double positions.data[][17]; // data: {ConfigLine, CustomType, PositionType, DirectionalLots, HedgedLots, PipDistance|BreakevenPrice, AdjustedProfit, TotalProfitM, TotalProfitPct, MfePrice, MfePct, MaePrice, MaePct, ProfitMarkerPrice, ProfitMarkerPct, LossMarkerPrice, LossMarkerPct} bool positions.analyzed; // bool positions.showAbsProfits; // for column adjustment (default: online=FALSE, tester=TRUE) bool positions.showMfae; // for column adjustment: whether at least one active config entry has the MFAE tracker enabled -bool positions.showMaxRisk; // default: FALSE #define CUSTOM_REAL_POSITION 1 // config line types: real position #define CUSTOM_VIRTUAL_POSITION 2 // virtual position (pure virtual or composite virtual/real) @@ -334,10 +331,6 @@ bool onCommand(string cmd, string params, int keys) { if (!CustomPositions.ToggleProfits()) return(false); } - else if (cmd == "toggle-risk") { - if (!CustomPositions.ToggleRisk()) return(false); - } - else if (cmd == "trade-account") { string key = StrReplace(params, ",", ":"); if (!InitTradeAccount(key)) return(false); @@ -977,17 +970,6 @@ string OrderMarkerText(int type, int magic, string comment) { } -/** - * Toggle the MaxLots/MaxRisk display of custom positions. - * - * @return bool - success status - */ -bool CustomPositions.ToggleRisk() { - positions.showMaxRisk = !positions.showMaxRisk; // toggle status and update positions - return(UpdatePositions()); -} - - /** * Toggle PnL amounts of custom positions between "absolute" und "percentage". * @@ -1462,9 +1444,6 @@ bool UpdatePositions() { sProfitMinMax = StringConcatenate("(", sProfitMin, "/", sProfitMax, ")"); } sComment = config.sData[configLine][I_CONFIG_COMMENT]; - if (positions.showMaxRisk) { - sComment = StringConcatenate("(", NumberToStr(config.dData[configLine][I_MAX_LOTS], ".+"), " lot/R?%) ", sComment); - } } // history only @@ -1943,8 +1922,6 @@ bool UpdateStopoutLevel() { // reset existing stats config.dData[line][I_PROFIT_MFE] = 0; config.dData[line][I_PROFIT_MAE] = 0; - config.dData[line][I_MAX_LOTS ] = 0; - config.dData[line][I_MAX_RISK ] = 0; } else { positions.showMfae = positions.showMfae || config.dData[line][I_MFAE_ENABLED]; @@ -2359,7 +2336,7 @@ int SearchLfxTicket(int ticket) { bool CustomPositions.ReadConfig() { double confTerms[][5]; ArrayResize(confTerms, 0); if (ArrayRange(confTerms, 1) != ArrayRange(config.terms, 1)) return(!catch("CustomPositions.ReadConfig(1) array mis-match config.terms[] / confTerms[]", ERR_INCOMPATIBLE_ARRAY)); string confsData[][2]; ArrayResize(confsData, 0); if (ArrayRange(confsData, 1) != ArrayRange(config.sData, 1)) return(!catch("CustomPositions.ReadConfig(2) array mis-match config.sData[] / confsData[]", ERR_INCOMPATIBLE_ARRAY)); - double confdData[][8]; ArrayResize(confdData, 0); if (ArrayRange(confdData, 1) != ArrayRange(config.dData, 1)) return(!catch("CustomPositions.ReadConfig(3) array mis-match config.dData[] / confdData[]", ERR_INCOMPATIBLE_ARRAY)); + double confdData[][6]; ArrayResize(confdData, 0); if (ArrayRange(confdData, 1) != ArrayRange(config.dData, 1)) return(!catch("CustomPositions.ReadConfig(3) array mis-match config.dData[] / confdData[]", ERR_INCOMPATIBLE_ARRAY)); // parse configuration string keys[], values[], iniValue="", sValue="", comment="", confComment="", openComment="", hstComment="", sNull, symbol=Symbol(), stdSymbol=StdSymbol(); @@ -2688,8 +2665,6 @@ bool CustomPositions.ReadConfig() { confdData[i][I_PROFIT_MFE] = config.dData[n][I_PROFIT_MFE]; confdData[i][I_PROFIT_MAE] = config.dData[n][I_PROFIT_MAE]; } - confdData[i][I_MAX_LOTS] = config.dData[n][I_MAX_LOTS]; - confdData[i][I_MAX_RISK] = config.dData[n][I_MAX_RISK]; break; } } @@ -3975,7 +3950,6 @@ bool StoreCustomPosition(bool isVirtual, double longPosition, double shortPositi config.dData[configLine][I_PROFIT_MFE] = MathMax(totalProfit, config.dData[configLine][I_PROFIT_MFE]); config.dData[configLine][I_PROFIT_MAE] = MathMin(totalProfit, config.dData[configLine][I_PROFIT_MAE]); - config.dData[configLine][I_MAX_LOTS ] = MathMax(totalPosition, config.dData[configLine][I_MAX_LOTS ]); positions.data[n][I_PROFIT_MFE_PCT] = MathDiv(config.dData[configLine][I_PROFIT_MFE], equity100Pct) * 100; positions.data[n][I_PROFIT_MAE_PCT] = MathDiv(config.dData[configLine][I_PROFIT_MAE], equity100Pct) * 100; @@ -4091,7 +4065,6 @@ bool StoreCustomPosition(bool isVirtual, double longPosition, double shortPositi config.dData[configLine][I_PROFIT_MFE] = MathMax(totalProfit, config.dData[configLine][I_PROFIT_MFE]); config.dData[configLine][I_PROFIT_MAE] = MathMin(totalProfit, config.dData[configLine][I_PROFIT_MAE]); - config.dData[configLine][I_MAX_LOTS ] = MathMax(-totalPosition, config.dData[configLine][I_MAX_LOTS ]); positions.data[n][I_PROFIT_MFE_PCT] = MathDiv(config.dData[configLine][I_PROFIT_MFE], equity100Pct) * 100; positions.data[n][I_PROFIT_MAE_PCT] = MathDiv(config.dData[configLine][I_PROFIT_MAE], equity100Pct) * 100; @@ -4529,7 +4502,7 @@ bool AnalyzePos.ProcessLfxProfits() { /** - * Store the runtime status. + * Store runtime status. * - in the chart: for init cycles and terminal restart * - in the chart window: for loading of templates * @@ -4551,29 +4524,17 @@ bool StoreStatus() { SetWindowIntegerA(__ExecutionContext[EC.chart], key, iValue); // chart window Chart.StoreInt(key, iValue); // chart - // bool positions.showMaxRisk - key = indicatorName +".positions.showMaxRisk"; - iValue = ifInt(positions.showMaxRisk, 1, -1); // GetWindowInteger() cannot restore integer 0 - SetWindowIntegerA(__ExecutionContext[EC.chart], key, iValue); // chart window - Chart.StoreInt(key, iValue); // chart - // risk/MFE/MAE stats of custom positions - string keys="", configKey=""; + string keys = ""; int size = ArrayRange(config.sData, 0); for (int i=0; i < size; i++) { - configKey = config.sData[i][I_CONFIG_KEY]; - key = indicatorName +"."+ Symbol() +".config."+ configKey +".risk"; - sValue = NumberToStr(config.dData[i][I_MAX_LOTS], ".1+") +"|"+ NumberToStr(config.dData[i][I_MAX_RISK], ".1+"); - SetWindowStringA(__ExecutionContext[EC.chart], key, sValue); // chart window - Chart.StoreString(key, sValue); // chart - if (config.dData[i][I_MFAE_ENABLED] > 0) { - key = indicatorName +"."+ Symbol() +".config."+ configKey +".mfae"; + key = indicatorName +"."+ Symbol() +".config."+ config.sData[i][I_CONFIG_KEY] +".mfae"; sValue = NumberToStr(config.dData[i][I_PROFIT_MFE], ".1+") +"|"+ NumberToStr(config.dData[i][I_PROFIT_MAE], ".1+"); SetWindowStringA(__ExecutionContext[EC.chart], key, sValue); // chart window Chart.StoreString(key, sValue); // chart } - keys = keys +"="+ configKey; // config keys can't contain equal signs "=" + keys = keys +"="+ config.sData[i][I_CONFIG_KEY]; // config keys can't contain equal signs "=" } // config keys of custom positions @@ -4624,13 +4585,6 @@ bool RestoreStatus() { Chart.RestoreInt(key, iValue2); positions.showAbsProfits = (iValue1==1 || iValue2==1); - // bool positions.showMaxRisk - key = indicatorName +".positions.showMaxRisk"; - iValue1 = RemoveWindowIntegerA(__ExecutionContext[EC.chart], key); // +1 || -1 - iValue2 = 0; - Chart.RestoreInt(key, iValue2); - positions.showMaxRisk = (iValue1==1 || iValue2==1); - // config keys of custom positions string configKeys[]; key = indicatorName +"."+ Symbol() +".config.keys"; @@ -4649,14 +4603,6 @@ bool RestoreStatus() { config.sData[i][I_CONFIG_KEY ] = configKeys[i]; config.sData[i][I_CONFIG_COMMENT] = ""; - key = indicatorName +"."+ Symbol() +".config."+ configKeys[i] +".risk"; - sValue1 = RemoveWindowStringA(__ExecutionContext[EC.chart], key); - sValue2 = ""; - Chart.RestoreString(key, sValue2); - if (!StringLen(sValue1)) sValue1 = sValue2; - config.dData[i][I_MAX_LOTS] = StrToDouble(StrLeftTo(sValue1, "|")); - config.dData[i][I_MAX_RISK] = StrToDouble(StrRightFrom(sValue1, "|")); - key = indicatorName +"."+ Symbol() +".config."+ configKeys[i] +".mfae"; sValue1 = RemoveWindowStringA(__ExecutionContext[EC.chart], key); sValue2 = ""; diff --git a/mql40/scripts/CustomPositions.ToggleRisk.mq4 b/mql40/scripts/CustomPositions.ToggleRisk.mq4 deleted file mode 100644 index 6909d7ae8..000000000 --- a/mql40/scripts/CustomPositions.ToggleRisk.mq4 +++ /dev/null @@ -1,36 +0,0 @@ -/** - * CustomPositions.ToggleRisk - * - * Sends a command to the ChartInfos indicator in the current chart to toggle the MaxRisk display of custom positions. - */ -#include -int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; -int __DeinitFlags[]; -#include -#include - - -/** - * Main function - * - * @return int - error status - */ -int onStart() { - string command = "toggle-risk"; - string params = ""; - string modifiers = ","; - if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; - if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; - if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key - if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; - if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; - if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key - if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; - if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; - modifiers = StrRight(modifiers, -1); - - command = command +":"+ params +":"+ modifiers; - - SendChartCommand("ChartInfos.command", command); - return(catch("onStart(1)")); -} From 43f4594a580ce1bf8a64d4fa289d48a630cb6a8c Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Fri, 7 Nov 2025 15:14:02 +0200 Subject: [PATCH 30/48] fix MFE/MAE signaling issues --- mql40/indicators/ChartInfos.mq4 | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/mql40/indicators/ChartInfos.mq4 b/mql40/indicators/ChartInfos.mq4 index 4a9cfaee2..700c88f7e 100644 --- a/mql40/indicators/ChartInfos.mq4 +++ b/mql40/indicators/ChartInfos.mq4 @@ -121,8 +121,8 @@ double config.dData[][6]; // data: {BemE #define I_MFAE_ENABLED 1 // whether to track MFE/MAE of a custom position #define I_MFAE_SIGNAL 2 // whether to signal new MFE/MAE of a custom position #define I_MARK_MFE 3 // whether to mark MFE levels -#define I_PROFIT_MFE 4 // MFE in money -#define I_PROFIT_MAE 5 // MAE in money +#define I_PROFIT_MFE 4 // current MFE maximum in money +#define I_PROFIT_MAE 5 // current MAE minimum in money // displayed custom position entries (may be larger than configured entries) double positions.data[][17]; // data: {ConfigLine, CustomType, PositionType, DirectionalLots, HedgedLots, PipDistance|BreakevenPrice, AdjustedProfit, TotalProfitM, TotalProfitPct, MfePrice, MfePct, MaePrice, MaePct, ProfitMarkerPrice, ProfitMarkerPct, LossMarkerPrice, LossMarkerPct} @@ -1907,14 +1907,14 @@ bool UpdateStopoutLevel() { if (line >= 0) { if (lineSkipped) { - // if the line gets skipped we must reset tracked MFAE signaling levels + // if the line gets skipped reset tracked MFAE signal levels int hWnd; string property = ""; if (!__isTesting && config.dData[line][I_PROFIT_MFE]) { property = GetMfaeSignalKey(config.sData[line][I_CONFIG_KEY], I_PROFIT_MFE); SetWindowPropertyA(hWndDesktop, property, 0); } - if (__isTesting && config.dData[line][I_PROFIT_MAE]) { + if (!__isTesting && config.dData[line][I_PROFIT_MAE]) { property = GetMfaeSignalKey(config.sData[line][I_CONFIG_KEY], I_PROFIT_MAE); SetWindowPropertyA(hWndDesktop, property, 0); } @@ -3869,6 +3869,7 @@ bool StoreCustomPosition(bool isVirtual, double longPosition, double shortPositi totalProfit = NormalizeDouble(totalProfit, 2); if (configLine >= 0) { + // theoretically there can be a single tick with new MFE/MAE which should be signaled if configured config.dData[configLine][I_PROFIT_MFE] = MathMax(totalProfit, config.dData[configLine][I_PROFIT_MFE]); config.dData[configLine][I_PROFIT_MAE] = MathMin(totalProfit, config.dData[configLine][I_PROFIT_MAE]); positions.data[n][I_PROFIT_MFE_PCT] = MathDiv(config.dData[configLine][I_PROFIT_MFE], equity100Pct) * 100; @@ -3944,12 +3945,12 @@ bool StoreCustomPosition(bool isVirtual, double longPosition, double shortPositi markMfe = false; if (configLine >= 0) { - isNewMfe = (config.dData[configLine][I_MFAE_SIGNAL] && config.dData[configLine][I_PROFIT_MFE] && totalProfit > config.dData[configLine][I_PROFIT_MFE]); - isNewMae = (config.dData[configLine][I_MFAE_SIGNAL] && config.dData[configLine][I_PROFIT_MAE] && totalProfit < config.dData[configLine][I_PROFIT_MAE]); markMfe = (config.dData[configLine][I_MFAE_ENABLED] && config.dData[configLine][I_MARK_MFE]); + isNewMfe = (config.dData[configLine][I_MFAE_SIGNAL] && totalProfit > config.dData[configLine][I_PROFIT_MFE]); + isNewMae = (config.dData[configLine][I_MFAE_SIGNAL] && totalProfit < config.dData[configLine][I_PROFIT_MAE]); - config.dData[configLine][I_PROFIT_MFE] = MathMax(totalProfit, config.dData[configLine][I_PROFIT_MFE]); - config.dData[configLine][I_PROFIT_MAE] = MathMin(totalProfit, config.dData[configLine][I_PROFIT_MAE]); + config.dData[configLine][I_PROFIT_MFE] = MathMax(totalProfit, config.dData[configLine][I_PROFIT_MFE]); + config.dData[configLine][I_PROFIT_MAE] = MathMin(totalProfit, config.dData[configLine][I_PROFIT_MAE]); positions.data[n][I_PROFIT_MFE_PCT] = MathDiv(config.dData[configLine][I_PROFIT_MFE], equity100Pct) * 100; positions.data[n][I_PROFIT_MAE_PCT] = MathDiv(config.dData[configLine][I_PROFIT_MAE], equity100Pct) * 100; @@ -4059,12 +4060,12 @@ bool StoreCustomPosition(bool isVirtual, double longPosition, double shortPositi markMfe = false; if (configLine >= 0) { - isNewMfe = (config.dData[configLine][I_MFAE_SIGNAL] && config.dData[configLine][I_PROFIT_MFE] && totalProfit > config.dData[configLine][I_PROFIT_MFE]); - isNewMae = (config.dData[configLine][I_MFAE_SIGNAL] && config.dData[configLine][I_PROFIT_MAE] && totalProfit < config.dData[configLine][I_PROFIT_MAE]); markMfe = (config.dData[configLine][I_MFAE_ENABLED] && config.dData[configLine][I_MARK_MFE]); + isNewMfe = (config.dData[configLine][I_MFAE_SIGNAL] && totalProfit > config.dData[configLine][I_PROFIT_MFE]); + isNewMae = (config.dData[configLine][I_MFAE_SIGNAL] && totalProfit < config.dData[configLine][I_PROFIT_MAE];); - config.dData[configLine][I_PROFIT_MFE] = MathMax(totalProfit, config.dData[configLine][I_PROFIT_MFE]); - config.dData[configLine][I_PROFIT_MAE] = MathMin(totalProfit, config.dData[configLine][I_PROFIT_MAE]); + config.dData[configLine][I_PROFIT_MFE] = MathMax(totalProfit, config.dData[configLine][I_PROFIT_MFE]); + config.dData[configLine][I_PROFIT_MAE] = MathMin(totalProfit, config.dData[configLine][I_PROFIT_MAE]); positions.data[n][I_PROFIT_MFE_PCT] = MathDiv(config.dData[configLine][I_PROFIT_MFE], equity100Pct) * 100; positions.data[n][I_PROFIT_MAE_PCT] = MathDiv(config.dData[configLine][I_PROFIT_MAE], equity100Pct) * 100; @@ -4964,8 +4965,9 @@ bool onNewMFE(string configKey, double profit) { // sound: once per system if (!__isTesting) { string property = GetMfaeSignalKey(configKey, I_PROFIT_MFE); + int iStored = GetWindowPropertyA(hWndDesktop, property); - if (GetWindowPropertyA(hWndDesktop, property) < iProfit) { + if (iProfit > iStored) { SetWindowPropertyA(hWndDesktop, property, iProfit); PlaySoundEx("Beacon.wav"); } @@ -4996,8 +4998,9 @@ bool onNewMAE(string configKey, double profit) { // sound: once per system if (!__isTesting) { string property = GetMfaeSignalKey(configKey, I_PROFIT_MAE); + int iStored = GetWindowPropertyA(hWndDesktop, property); - if (GetWindowPropertyA(hWndDesktop, property) > iProfit) { + if (iProfit < iStored) { SetWindowPropertyA(hWndDesktop, property, iProfit); PlaySoundEx("Windows Ping.wav"); } From cd4a158c9e919f67336a8ec551ce02a4e0d66714 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Fri, 7 Nov 2025 22:02:13 +0200 Subject: [PATCH 31/48] show avg percent/trade if profit-in-percent is active --- mql40/include/rsf/experts/status/SS.ClosedTrades.mqh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh b/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh index ac7786e58..20c76847e 100644 --- a/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh +++ b/mql40/include/rsf/experts/status/SS.ClosedTrades.mqh @@ -12,7 +12,15 @@ void SS.ClosedTrades() { switch (status.activeMetric) { case METRIC_NET_MONEY: - status.closedTrades = size + trades +" avg: "+ NumberToStr(stats[METRIC_NET_MONEY][S_TRADES_AVG_PROFIT], "R+.2") +" "+ AccountCurrency(); + if (status.profitInPercent) { + double totalPerformance = 1 + MathDiv(stats[METRIC_NET_MONEY][S_TOTAL_PROFIT], instance.startEquity); + double avgPerformance = MathPow(totalPerformance, 1./size); + double avgTrade = (avgPerformance - 1) * 100; + status.closedTrades = size + trades +" avg: "+ NumberToStr(avgTrade, "R+.2") +"%"; + } + else { + status.closedTrades = size + trades +" avg: "+ NumberToStr(stats[METRIC_NET_MONEY][S_TRADES_AVG_PROFIT], "R+.2") +" "+ AccountCurrency(); + } break; case METRIC_NET_UNITS: status.closedTrades = size + trades +" avg: "+ NumberToStr(stats[METRIC_NET_UNITS][S_TRADES_AVG_PROFIT]/pUnit, "R+."+ pDigits) +" "+ spUnit; From fd55a537b49be6ad4b14f0f4a5c19ce2fcf22725 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Fri, 7 Nov 2025 22:09:10 +0200 Subject: [PATCH 32/48] start new online instance in status "stopped" --- mql40/include/rsf/experts/init.mqh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mql40/include/rsf/experts/init.mqh b/mql40/include/rsf/experts/init.mqh index 3248d7056..02d3cb027 100644 --- a/mql40/include/rsf/experts/init.mqh +++ b/mql40/include/rsf/experts/init.mqh @@ -33,7 +33,7 @@ int onInitUser() { Instance.ID = ifString(instance.isTest, "T", "") + StrPadLeft(instance.id, 3, "0"); SS.InstanceName(); instance.created = GetLocalTime(); // local system time (also in tester) instance.started = TimeServer(); // trade server time (modeled in tester) - instance.status = STATUS_WAITING; + instance.status = ifInt(__isTesting, STATUS_WAITING, STATUS_STOPPED); SetStatusFilename(); logInfo("onInitUser(2) instance "+ instance.name +" created"); SaveStatus(); From 3c98fa4ba42c2bd33d0d65a0f4b5a5cb7093132c Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sat, 8 Nov 2025 08:39:05 +0200 Subject: [PATCH 33/48] update instance name --- mql40/experts/ZigZag EA.mq4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 2dcdfe1fc..7c2c2bd62 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -1509,10 +1509,10 @@ void SS.All() { void SS.InstanceName() { if (!instance.id) { // calling SS.All() and thus SS.InstanceName() before CreateInstanceId() is valid (e.g. after input validation of a new instance) - instance.name = "Z."; + instance.name = "ZZ."; } else { - instance.name = "Z."+ StrPadLeft(instance.id, 3, "0"); + instance.name = "ZZ."+ StrPadLeft(instance.id, 3, "0"); } } From 332df8b23f814c62ac9929fcea239368789831e2 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sat, 8 Nov 2025 12:57:14 +0200 Subject: [PATCH 34/48] disregard --- mql40/experts/ZigZag EA.mq4 | 2 +- .../status/file/SaveStatus.TradeHistory.mqh | 14 ++++++++------ .../include/rsf/experts/trade/AddHistoryRecord.mqh | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 7c2c2bd62..7aeff76d4 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -1207,7 +1207,7 @@ bool SynchronizeStatus() { } SS.All(); - if (open.ticket!=prevOpenTicket || ArrayRange(history, 0)!=prevHistorySize) { + if (open.ticket != prevOpenTicket || ArrayRange(history, 0) != prevHistorySize) { CalculateStats(true); return(SaveStatus()); // immediately save status if orders changed } diff --git a/mql40/include/rsf/experts/status/file/SaveStatus.TradeHistory.mqh b/mql40/include/rsf/experts/status/file/SaveStatus.TradeHistory.mqh index 8f8bd393e..4e9817f86 100644 --- a/mql40/include/rsf/experts/status/file/SaveStatus.TradeHistory.mqh +++ b/mql40/include/rsf/experts/status/file/SaveStatus.TradeHistory.mqh @@ -13,18 +13,20 @@ bool SaveStatus.TradeHistory(string file, bool fileExists) { if (!fileExists) separator = CRLF; // an empty line separator double netProfit, netProfitP, sigProfitP; + int sizeHistory = ArrayRange(history, 0), sizePartials = ArrayRange(partialClose, 0); - string section = "Trade history"; - int size = ArrayRange(history, 0); - for (int i=0; i < size; i++) { - WriteIniString(file, section, "full."+ i, HistoryRecordToStr(i, false)); + string section = "Trade history", suffix = ""; + for (int i=0; i < sizeHistory; i++) { + if (sizePartials && i == sizeHistory-1) { + suffix = separator; + } + WriteIniString(file, section, "full."+ i, HistoryRecordToStr(i, false) + suffix); netProfit += history[i][H_NETPROFIT_M ]; netProfitP += history[i][H_NETPROFIT_P ]; sigProfitP += history[i][H_SIG_PROFIT_P]; } - size = ArrayRange(partialClose, 0); - for (i=0; i < size; i++) { + for (i=0; i < sizePartials; i++) { WriteIniString(file, section, "part."+ i, HistoryRecordToStr(i, true)); } diff --git a/mql40/include/rsf/experts/trade/AddHistoryRecord.mqh b/mql40/include/rsf/experts/trade/AddHistoryRecord.mqh index 0777223a7..c60edf61f 100644 --- a/mql40/include/rsf/experts/trade/AddHistoryRecord.mqh +++ b/mql40/include/rsf/experts/trade/AddHistoryRecord.mqh @@ -92,7 +92,7 @@ int AddHistoryRecord(int ticket, int fromTicket, int toTicket, int type, double else { // resolve the history[] index to insert at size = ArrayRange(history, 0); - for (i=size-1; i >= 0; i--) { // iterate from the end (in most use cases faster) + for (i=size-1; i >= 0; i--) { // iterate from the end (faster in most cases) if (ticket == history[i][H_TICKET]) return(_EMPTY(catch("AddHistoryRecord(2) "+ instance.name +" cannot add record, ticket #"+ ticket +" already exists (history["+ i +"])", ERR_INVALID_PARAMETER))); if (openTime > history[i][H_OPENTIME]) { From 3f98883a5cc0918afd16372a0038dd500f95950b Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sat, 8 Nov 2025 18:25:57 +0200 Subject: [PATCH 35/48] add helper GetPressedVirtualKeys() --- mql40/include/rsf/MT4Expander.mqh | 1 + mql40/include/rsf/api.mqh | 3 ++- mql40/libraries/rsfMT4Expander.dll | Bin 285184 -> 285696 bytes 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mql40/include/rsf/MT4Expander.mqh b/mql40/include/rsf/MT4Expander.mqh index 2aa6597ed..9de8659da 100644 --- a/mql40/include/rsf/MT4Expander.mqh +++ b/mql40/include/rsf/MT4Expander.mqh @@ -176,6 +176,7 @@ // other string GetInternalWindowTextA(int hWnd); int GetLastWin32Error(); + int GetPressedVirtualKeys(int flags); bool IsProgramType(int type); bool IsVirtualKeyDown(int vKey); bool IsWindowAreaVisible(int hWnd); diff --git a/mql40/include/rsf/api.mqh b/mql40/include/rsf/api.mqh index b56e752cf..cb5812205 100644 --- a/mql40/include/rsf/api.mqh +++ b/mql40/include/rsf/api.mqh @@ -787,6 +787,7 @@ string GetMqlDirectoryA();; string GetMqlDirectoryW();; string GetMqlSandboxPathA(int inTester);; string GetMqlSandboxPathW(int inTester);; +int GetPressedVirtualKeys(int flags);; string GetReparsePointTargetA(string name);; string GetStringA(int address);; string GetStringW(int address);; @@ -838,7 +839,7 @@ bool IsStandardTimeframe(int timeframe);; bool IsSymlinkA(string path);; bool IsTerminalConfigKeyA(string section, string key);; bool IsUIThread(int threadId=NULL);; -bool IsVirtualKeyDown(int vKey);; +bool IsVirtualKeyDown(int key);; bool IsWindowAreaVisible(int hWnd);; int LeaveContext(int &ec[]);; bool LoadMqlProgramA(int hChart, int programType, string programName);; diff --git a/mql40/libraries/rsfMT4Expander.dll b/mql40/libraries/rsfMT4Expander.dll index 04bd5db91125ba2740469658b31153c5c7f7ac25..b35cd80e9f3ddd231709b822833fc5a0d8664925 100644 GIT binary patch delta 32258 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 JE9zR({{dlyfS&*W delta 31955 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 Date: Sun, 9 Nov 2025 00:20:23 +0200 Subject: [PATCH 36/48] chart commands support integer modifiers --- .../include/rsf/functions/HandleCommands.mqh | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/mql40/include/rsf/functions/HandleCommands.mqh b/mql40/include/rsf/functions/HandleCommands.mqh index e937f6a01..82404461e 100644 --- a/mql40/include/rsf/functions/HandleCommands.mqh +++ b/mql40/include/rsf/functions/HandleCommands.mqh @@ -2,8 +2,8 @@ * Retrieve received commands and pass them to the command handler. Command format: "cmd[:params[:modifiers]]" * * cmd: command identifier (required) - * params: one or more command parameters separated by comma "," (optional) - * modifiers: one or more virtual key modifiers separated by comma "," (optional) + * params: one or more command parameters separated by comma (optional) + * modifiers: one or more virtual key modifiers separated by comma (optional) * * @param string channel [optional] - channel to check for commands (default: the program's standard command channel) * @@ -19,24 +19,38 @@ bool HandleCommands(string channel = "") { int size = ArraySize(commands); for (int i=0; i < size && !last_error; i++) { - string cmd="", params="", modifier="", values[]; + string cmd="", params="", modifiers="", sValue="", sValues[]; - int parts = Explode(commands[i], ":", values, NULL), virtKeys=0; - if (parts > 0) cmd = StrTrim(values[0]); - if (parts > 1) params = StrTrim(values[1]); + int parts = Explode(commands[i], ":", sValues, NULL), iValue=0, keys=0; + if (parts > 0) cmd = StrTrim(sValues[0]); + if (parts > 1) params = StrTrim(sValues[1]); if (parts > 2) { - parts = Explode(values[2], ",", values, NULL); - for (int n=0; n < parts; n++) { - modifier = StrTrim(values[n]); - if (modifier == "VK_ESCAPE") virtKeys |= F_VK_ESCAPE; - else if (modifier == "VK_TAB") virtKeys |= F_VK_TAB; - else if (modifier == "VK_CAPITAL") virtKeys |= F_VK_CAPITAL; // CAPSLOCK key - else if (modifier == "VK_SHIFT") virtKeys |= F_VK_SHIFT; - else if (modifier == "VK_CONTROL") virtKeys |= F_VK_CONTROL; - else if (modifier == "VK_MENU") virtKeys |= F_VK_MENU; // ALT key - else if (modifier == "VK_LWIN") virtKeys |= F_VK_LWIN; // left Windows key - else if (modifier == "VK_RWIN") virtKeys |= F_VK_RWIN; // right Windows key - else if (modifier != "") logNotice("HandleCommands(1) skipping unsupported key modifier: "+ modifier); + modifiers = StrTrim(sValues[2]); + if (StrIsDigits(modifiers)) { + iValue = StrToInteger(modifiers); + if (iValue & F_VK_ESCAPE && 1) keys |= F_VK_ESCAPE; + if (iValue & F_VK_TAB && 1) keys |= F_VK_TAB; + if (iValue & F_VK_CAPITAL && 1) keys |= F_VK_CAPITAL; // CAPSLOCK key + if (iValue & F_VK_SHIFT && 1) keys |= F_VK_SHIFT; + if (iValue & F_VK_CONTROL && 1) keys |= F_VK_CONTROL; + if (iValue & F_VK_MENU && 1) keys |= F_VK_MENU; // ALT key + if (iValue & F_VK_LWIN && 1) keys |= F_VK_LWIN; + if (iValue & F_VK_RWIN && 1) keys |= F_VK_RWIN; + } + else { + parts = Explode(modifiers, ",", sValues, NULL); + for (int n=0; n < parts; n++) { + sValue = StrTrim(sValues[n]); + if (sValue == "VK_ESCAPE") keys |= F_VK_ESCAPE; + else if (sValue == "VK_TAB") keys |= F_VK_TAB; + else if (sValue == "VK_CAPITAL") keys |= F_VK_CAPITAL; + else if (sValue == "VK_SHIFT") keys |= F_VK_SHIFT; + else if (sValue == "VK_CONTROL") keys |= F_VK_CONTROL; + else if (sValue == "VK_MENU") keys |= F_VK_MENU; + else if (sValue == "VK_LWIN") keys |= F_VK_LWIN; + else if (sValue == "VK_RWIN") keys |= F_VK_RWIN; + else if (sValue != "") logNotice("HandleCommands(1) skipping unsupported key modifier: "+ sValue); + } } } @@ -44,7 +58,7 @@ bool HandleCommands(string channel = "") { logNotice("HandleCommands(2) skipping empty command: \""+ commands[i] +"\""); continue; } - onCommand(cmd, params, virtKeys); + onCommand(cmd, params, keys); } } return(!last_error); From 9ba86bb79c17361cf554f3d8e7447c853dc0022c Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 9 Nov 2025 06:02:40 +0200 Subject: [PATCH 37/48] add support for flag F_VK_ALL --- mql40/include/rsf/expander/defines.h | 1 + mql40/include/rsf/stdfunctions.mqh | 2 +- mql40/libraries/rsfMT4Expander.dll | Bin 285696 -> 285696 bytes mql40/scripts/Chart.ToggleOpenOrders.mq4 | 26 +++++++-------------- mql40/scripts/Chart.ToggleTradeHistory.mq4 | 26 +++++++-------------- mql40/scripts/Chart.ToggleUnitSize.mq4 | 4 ++-- 6 files changed, 22 insertions(+), 37 deletions(-) diff --git a/mql40/include/rsf/expander/defines.h b/mql40/include/rsf/expander/defines.h index 63ee75c4f..79ea2f639 100644 --- a/mql40/include/rsf/expander/defines.h +++ b/mql40/include/rsf/expander/defines.h @@ -212,6 +212,7 @@ #define F_VK_MENU 32 // ALT key #define F_VK_LWIN 64 // left Windows key #define F_VK_RWIN 128 // right Windows key +#define F_VK_ALL 255 // F_VK_ESCAPE|F_VK_TAB|F_VK_CAPITAL|F_VK_SHIFT|F_VK_CONTROL|F_VK_MENU|F_VK_LWIN|F_VK_RWIN // order and operation types diff --git a/mql40/include/rsf/stdfunctions.mqh b/mql40/include/rsf/stdfunctions.mqh index 7a48ec1aa..27e5457f4 100644 --- a/mql40/include/rsf/stdfunctions.mqh +++ b/mql40/include/rsf/stdfunctions.mqh @@ -6243,7 +6243,7 @@ string ShellExecuteErrorDescription(int error) { * @return bool - success status */ bool SendChartCommand(string cmdObject, string cmd, string mutex = "") { - if (!StringLen(mutex)) { + if (mutex == "") { mutex = StringConcatenate("mutex.", cmdObject); // generate mutex if needed } if (!AquireLock(mutex)) return(false); // aquire write-lock diff --git a/mql40/libraries/rsfMT4Expander.dll b/mql40/libraries/rsfMT4Expander.dll index b35cd80e9f3ddd231709b822833fc5a0d8664925..778965bb00055e023b996aad6a9e66838f3299a0 100644 GIT binary patch delta 45 zcmZqJA=t1(aKaDfPXYoHfBQ0t-fs3{Z1-XWVW#a~jLf?(Fn<&f*v@{5`DZ5plv@!f delta 45 zcmZqJA=t1(aKaB}={)|4zkQj~rZ#&qwtF#xFw=G~M&?}?n5FXgx3gbj{@Dotf3^@m diff --git a/mql40/scripts/Chart.ToggleOpenOrders.mq4 b/mql40/scripts/Chart.ToggleOpenOrders.mq4 index 8a89534e5..9b1e474e7 100644 --- a/mql40/scripts/Chart.ToggleOpenOrders.mq4 +++ b/mql40/scripts/Chart.ToggleOpenOrders.mq4 @@ -1,7 +1,7 @@ /** * Chart.ToggleOpenOrders * - * Sends a command to an EA or the ChartInfos indicator in the current chart to toggle display of open orders. + * Sends a command to EA or ChartInfos indicator in the current chart to toggle the display of open orders. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -18,23 +18,15 @@ int __DeinitFlags[]; int onStart() { if (__isTesting) Tester.Pause(); - string command = "toggle-open-orders"; - string params = ""; - string modifiers = ","; - if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; - if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; - if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key - if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; - if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; - if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key - if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; - if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; - modifiers = StrRight(modifiers, -1); + int virtKeys = GetPressedVirtualKeys(F_VK_ALL); + string command = "toggle-open-orders::"+ virtKeys; - command = command +":"+ params +":"+ modifiers; + bool isEA = (ObjectFind("EA.status") == 0); + bool isShiftKey = (virtKeys & F_VK_SHIFT && 1); + bool isWinKey = (virtKeys & F_VK_LWIN && 1); - // send to a running EA or the ChartInfos indicator - if (ObjectFind("EA.status") == 0) SendChartCommand("EA.command", command); - else SendChartCommand("ChartInfos.command", command); + // send the command to an existing EA or the chart + if (isEA && !isShiftKey && !isWinKey) SendChartCommand("EA.command", command); + else SendChartCommand("ChartInfos.command", command); return(last_error); } diff --git a/mql40/scripts/Chart.ToggleTradeHistory.mq4 b/mql40/scripts/Chart.ToggleTradeHistory.mq4 index 5d94b8cd2..cad36ec4a 100644 --- a/mql40/scripts/Chart.ToggleTradeHistory.mq4 +++ b/mql40/scripts/Chart.ToggleTradeHistory.mq4 @@ -1,7 +1,7 @@ /** * Chart.ToggleTradeHistory * - * Sends a command to an EA or the ChartInfos indicator in the current chart to toggle display of the trade history. + * Sends a command to EA or ChartInfos indicator in the current chart to toggle the display of closed trades. */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; @@ -19,23 +19,15 @@ int __DeinitFlags[]; int onStart() { if (__isTesting) Tester.Pause(); - string command = "toggle-trade-history"; - string params = ""; - string modifiers = ","; - if (IsVirtualKeyDown(VK_ESCAPE)) modifiers = modifiers +",VK_ESCAPE"; - if (IsVirtualKeyDown(VK_TAB)) modifiers = modifiers +",VK_TAB"; - if (IsVirtualKeyDown(VK_CAPITAL)) modifiers = modifiers +",VK_CAPITAL"; // CAPSLOCK key - if (IsVirtualKeyDown(VK_SHIFT)) modifiers = modifiers +",VK_SHIFT"; - if (IsVirtualKeyDown(VK_CONTROL)) modifiers = modifiers +",VK_CONTROL"; - if (IsVirtualKeyDown(VK_MENU)) modifiers = modifiers +",VK_MENU"; // ALT key - if (IsVirtualKeyDown(VK_LWIN)) modifiers = modifiers +",VK_LWIN"; - if (IsVirtualKeyDown(VK_RWIN)) modifiers = modifiers +",VK_RWIN"; - modifiers = StrRight(modifiers, -1); + int virtKeys = GetPressedVirtualKeys(F_VK_ALL); + string command = "toggle-trade-history::"+ virtKeys; - command = command +":"+ params +":"+ modifiers; + bool isEA = (ObjectFind("EA.status") == 0); + bool isShiftKey = (virtKeys & F_VK_SHIFT && 1); + bool isWinKey = (virtKeys & F_VK_LWIN && 1); - // send to a running EA or the ChartInfos indicator - if (ObjectFind("EA.status") == 0) SendChartCommand("EA.command", command); - else SendChartCommand("ChartInfos.command", command); + // send the command to an existing EA or the chart + if (isEA && !isShiftKey && !isWinKey) SendChartCommand("EA.command", command); + else SendChartCommand("ChartInfos.command", command); return(last_error); } diff --git a/mql40/scripts/Chart.ToggleUnitSize.mq4 b/mql40/scripts/Chart.ToggleUnitSize.mq4 index 9b833b208..b5a47b2d0 100644 --- a/mql40/scripts/Chart.ToggleUnitSize.mq4 +++ b/mql40/scripts/Chart.ToggleUnitSize.mq4 @@ -1,8 +1,8 @@ /** * Chart.ToggleUnitSize * - * Sends a command to the ChartInfos indicator in the current chart to toggle location of the displayed unitsize - * between "top" and "bottom". + * Sends a command to the ChartInfos indicator in the current chart to toggle the "unitsize" location between + * "top" and "bottom". */ #include int __InitFlags[] = {INIT_NO_BARS_REQUIRED}; From 0a8ba289e61ac88e41847f1ba1a4acb5212dc2d7 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 9 Nov 2025 11:59:56 +0200 Subject: [PATCH 38/48] adjust status and logfile names [skip ci] --- mql40/experts/ZigZag EA.mq4 | 20 ++++---------- mql40/include/rsf/experts/init.mqh | 15 ++++------- ...{GetLogFilename.mqh => GetLogFileName.mqh} | 4 +-- .../experts/status/file/FindStatusFile.mqh | 9 +++++-- ...atusFilename.mqh => GetStatusFileName.mqh} | 4 +-- .../experts/status/file/SetStatusFileName.mqh | 27 +++++++++++++++++++ .../experts/status/file/SetStatusFilename.mqh | 22 --------------- 7 files changed, 48 insertions(+), 53 deletions(-) rename mql40/include/rsf/experts/log/{GetLogFilename.mqh => GetLogFileName.mqh} (76%) rename mql40/include/rsf/experts/status/file/{GetStatusFilename.mqh => GetStatusFileName.mqh} (84%) create mode 100644 mql40/include/rsf/experts/status/file/SetStatusFileName.mqh delete mode 100644 mql40/include/rsf/experts/status/file/SetStatusFilename.mqh diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index 7aeff76d4..d84b2603c 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -137,7 +137,7 @@ extern double Lots = 0.1; #include #include -#include +#include #include #include @@ -154,8 +154,8 @@ extern double Lots = 0.1; #include #include -#include -#include +#include +#include #include #include #include @@ -997,7 +997,7 @@ bool SaveStatus() { } else if (IsTestInstance()) return(true); // don't modify the status file of a finished test - string section="", separator="", file=GetStatusFilename(); + string section="", separator="", file=GetStatusFileName(); bool fileExists = IsFile(file, MODE_SYSTEM); if (!fileExists) separator = CRLF; // an empty line separator SS.All(); // update trade stats and global string representations @@ -1065,7 +1065,7 @@ bool ReadStatus() { if (IsLastError()) return(false); if (!instance.id) return(!catch("ReadStatus(1) "+ instance.name +" illegal value of instance.id: "+ instance.id, ERR_ILLEGAL_STATE)); - string section="", file=GetStatusFilename(); + string section="", file=GetStatusFileName(); if (file == "") return(!catch("ReadStatus(2) "+ instance.name +" status file not found", ERR_RUNTIME_ERROR)); if (!IsFile(file, MODE_SYSTEM)) return(!catch("ReadStatus(3) "+ instance.name +" file \""+ file +"\" not found", ERR_FILE_NOT_FOUND)); @@ -1215,16 +1215,6 @@ bool SynchronizeStatus() { } -/** - * Return a distinctive instance detail to be inserted in the status/log filename. - * - * @return string - */ -string GetStatusFilenameData() { - return("P="+ ZigZag.Periods); -} - - /** * Whether the specified ticket exists in the local history of closed positions. * diff --git a/mql40/include/rsf/experts/init.mqh b/mql40/include/rsf/experts/init.mqh index 02d3cb027..bf5aad663 100644 --- a/mql40/include/rsf/experts/init.mqh +++ b/mql40/include/rsf/experts/init.mqh @@ -23,7 +23,7 @@ int onInit() { int onInitUser() { if (ValidateInputs.ID()) { // TRUE: a valid instance id was specified if (RestoreInstance()) { // try to reload the given instance - logInfo("onInitUser(1) "+ instance.name +" restored in status \""+ StatusDescription(instance.status) +"\" from file \""+ GetStatusFilename(true) +"\""); + logInfo("onInitUser(1) "+ instance.name +" restored in status \""+ StatusDescription(instance.status) +"\" from file \""+ GetStatusFileName(true) +"\""); } } else if (StrTrim(Instance.ID) == "") { // no instance id was specified @@ -34,7 +34,7 @@ int onInitUser() { instance.created = GetLocalTime(); // local system time (also in tester) instance.started = TimeServer(); // trade server time (modeled in tester) instance.status = ifInt(__isTesting, STATUS_WAITING, STATUS_STOPPED); - SetStatusFilename(); + SetStatusFileName(); logInfo("onInitUser(2) instance "+ instance.name +" created"); SaveStatus(); } @@ -88,7 +88,7 @@ int onInitSymbolChange() { int onInitTemplate() { if (RestoreVolatileStatus()) { // an instance id was found and restored if (RestoreInstance()) { // the instance was restored - logInfo("onInitTemplate(1) "+ instance.name +" restored in status \""+ StatusDescription(instance.status) +"\" from file \""+ GetStatusFilename(true) +"\""); + logInfo("onInitTemplate(1) "+ instance.name +" restored in status \""+ StatusDescription(instance.status) +"\" from file \""+ GetStatusFileName(true) +"\""); } return(last_error); } @@ -104,7 +104,7 @@ int onInitTemplate() { int onInitRecompile() { if (RestoreVolatileStatus()) { // same as for onInitTemplate() if (RestoreInstance()) { - logInfo("onInitRecompile(1) "+ instance.name +" restored in status \""+ StatusDescription(instance.status) +"\" from file \""+ GetStatusFilename(true) +"\""); + logInfo("onInitRecompile(1) "+ instance.name +" restored in status \""+ StatusDescription(instance.status) +"\" from file \""+ GetStatusFileName(true) +"\""); } return(last_error); } @@ -119,7 +119,7 @@ int onInitRecompile() { */ int afterInit() { if (__isTesting || !IsTestInstance()) { // open the log file (flushes the log buffer) except if in a finished test - string filename = GetLogFilename(); + string filename = GetLogFileName(); if (filename == "") return(last_error); if (!SetLogfile(filename)) return(catch("afterInit(1)")); } @@ -132,8 +132,3 @@ int afterInit() { StoreVolatileStatus(); // store the instance id for template reload/restart/recompilation etc. return(catch("afterInit(2)")); } - - -#import "rsfMT4Expander.dll" - string GetStatusFilenameData(); // A no-op in the Expander allows the user to optionally provide his own data. -#import diff --git a/mql40/include/rsf/experts/log/GetLogFilename.mqh b/mql40/include/rsf/experts/log/GetLogFileName.mqh similarity index 76% rename from mql40/include/rsf/experts/log/GetLogFilename.mqh rename to mql40/include/rsf/experts/log/GetLogFileName.mqh index 738ac29c0..99f441046 100644 --- a/mql40/include/rsf/experts/log/GetLogFilename.mqh +++ b/mql40/include/rsf/experts/log/GetLogFileName.mqh @@ -3,8 +3,8 @@ * * @return string - filename or an empty string in case of errors */ -string GetLogFilename() { - string name = GetStatusFilename(); +string GetLogFileName() { + string name = GetStatusFileName(); if (name == "") return(""); return(StrLeftTo(name, ".", -1) +".log"); } diff --git a/mql40/include/rsf/experts/status/file/FindStatusFile.mqh b/mql40/include/rsf/experts/status/file/FindStatusFile.mqh index 9d6a1a8b6..443b33979 100644 --- a/mql40/include/rsf/experts/status/file/FindStatusFile.mqh +++ b/mql40/include/rsf/experts/status/file/FindStatusFile.mqh @@ -1,5 +1,10 @@ /** - * Find an existing status file for the specified instance. + * Find an existing status file for the specified instance id. Finds any file matching + * + * ", ,* id=.set" (no matter what the custom name is) + * + * but doesn't scan subdirectories. The EA will use whatever was found for status and logfile. + * A more strict pattern is used for name generation in SetStatusFileName(). * * @param int instanceId - instance id * @param bool isTest - whether the instance is a test instance @@ -13,7 +18,7 @@ string FindStatusFile(int instanceId, bool isTest) { string sandboxDir = GetMqlSandboxPath() +"\\"; string statusDir = "presets\\"+ ifString(isTest, "Tester", GetAccountCompanyId()) +"\\"; - string basePattern = ProgramName() +", "+ Symbol() +",* id="+ StrPadLeft(""+ instanceId, 3, "0") +".set"; // matches files with and w/o user-specified data in the name + string basePattern = ProgramName() +", "+ Symbol() +",* id="+ StrPadLeft(""+ instanceId, 3, "0") +".set"; // matches files with custom names string pathPattern = sandboxDir + statusDir + basePattern; string result[]; diff --git a/mql40/include/rsf/experts/status/file/GetStatusFilename.mqh b/mql40/include/rsf/experts/status/file/GetStatusFileName.mqh similarity index 84% rename from mql40/include/rsf/experts/status/file/GetStatusFilename.mqh rename to mql40/include/rsf/experts/status/file/GetStatusFileName.mqh index af1b0c0cf..86b0776f7 100644 --- a/mql40/include/rsf/experts/status/file/GetStatusFilename.mqh +++ b/mql40/include/rsf/experts/status/file/GetStatusFileName.mqh @@ -6,11 +6,11 @@ * * @return string - filename or an empty string in case of errors */ -string GetStatusFilename(bool relative = false) { +string GetStatusFileName(bool relative = false) { relative = relative!=0; if (status.filename == "") { - status.filename = FindStatusFile(instance.id, instance.isTest); // intentionally trigger an error if instance.id is not set + status.filename = FindStatusFile(instance.id, instance.isTest); if (status.filename == "") return(""); } diff --git a/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh b/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh new file mode 100644 index 000000000..4d0f6a8fb --- /dev/null +++ b/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh @@ -0,0 +1,27 @@ +/** + * Generates and initializes the name of the status file. Requires 'instance.id' and 'instance.created' to be set. + * + * If the expert implements GetStatusFileNameData() the returned string will be inserted into the resulting + * filename. This can be used to insert custom data into the name (e.g. SL/TP vars or trading modes). + * + * @return bool - success status + */ +bool SetStatusFileName() { + if (status.filename != "") return(!catch("SetStatusFileName(1) "+ instance.name +" cannot modify an already set status filename: \""+ status.filename +"\"", ERR_ILLEGAL_STATE)); + if (!instance.id) return(!catch("SetStatusFileName(2) "+ instance.name +" illegal value of instance.id: 0", ERR_ILLEGAL_STATE)); + if (!instance.created) return(!catch("SetStatusFileName(3) "+ instance.name +" cannot create status filename (instance.created not set)", ERR_ILLEGAL_STATE)); + + string userData = StrTrim(GetStatusFileNameData()); + if (userData != "") userData = userData +", "; + + string directory = "presets\\"+ ifString(IsTestInstance(), "Tester", GetAccountCompanyId()) +"\\"; + string baseName = ProgramName() +", "+ Symbol() +","+ PeriodDescription() +" "+ userData + GmtTimeFormat(instance.created, "%Y.%m.%d %H.%M") +", id="+ StrPadLeft(instance.id, 3, "0") +".set"; + status.filename = directory + baseName; + + return(!catch("SetStatusFileName(4)")); +} + + +#import "rsfMT4Expander.dll" + string GetStatusFileNameData(); // a no-op in the Expander allows optional override in MQL +#import diff --git a/mql40/include/rsf/experts/status/file/SetStatusFilename.mqh b/mql40/include/rsf/experts/status/file/SetStatusFilename.mqh deleted file mode 100644 index 72d6633dc..000000000 --- a/mql40/include/rsf/experts/status/file/SetStatusFilename.mqh +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Initializes the name of the used status file. Requires 'instance.id' and 'instance.created' to be set. - * - * If the strategy implements the function GetStatusFilenameData() the returned string will be inserted into the resulting - * filename. This can be used to insert distinctive runtime parameters into the name (e.g. SL/TP vars or trading modes). - * - * @return bool - success status - */ -bool SetStatusFilename() { - if (status.filename != "") return(!catch("SetStatusFilename(1) "+ instance.name +" cannot modify an already set status filename: \""+ status.filename +"\"", ERR_ILLEGAL_STATE)); - if (!instance.id) return(!catch("SetStatusFilename(2) "+ instance.name +" illegal value of instance.id: 0", ERR_ILLEGAL_STATE)); - if (!instance.created) return(!catch("SetStatusFilename(3) "+ instance.name +" cannot create status filename (instance.created not set)", ERR_ILLEGAL_STATE)); - - string userData = StrTrim(GetStatusFilenameData()); - if (userData != "") userData = userData +", "; - - string directory = "presets\\"+ ifString(IsTestInstance(), "Tester", GetAccountCompanyId()) +"\\"; - string baseName = ProgramName() +", "+ Symbol() +","+ PeriodDescription() +" "+ userData + GmtTimeFormat(instance.created, "%Y-%m-%d %H.%M") +", id="+ StrPadLeft(instance.id, 3, "0") +".set"; - status.filename = directory + baseName; - - return(!catch("SetStatusFilename(4)")); -} From 1e2ed04bd49a1e268550e32f70f09a31d6f18f18 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 9 Nov 2025 12:47:30 +0200 Subject: [PATCH 39/48] rename GetStatusFilenameData() to GetStatusFileNameData() [skip ci] --- mql40/include/rsf/MT4Expander.mqh | 3 ++- .../experts/status/file/SetStatusFileName.mqh | 5 ----- mql40/libraries/rsfMT4Expander.dll | Bin 285696 -> 285696 bytes 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/mql40/include/rsf/MT4Expander.mqh b/mql40/include/rsf/MT4Expander.mqh index 9de8659da..360b92590 100644 --- a/mql40/include/rsf/MT4Expander.mqh +++ b/mql40/include/rsf/MT4Expander.mqh @@ -182,7 +182,7 @@ bool IsWindowAreaVisible(int hWnd); int PlaySoundA(string soundfile); - // Virtual no-ops. Automatically overwritten by MQL implementations of the same name. + // Virtual no-ops. Overridden by custom MQL implementations of the same name. int onInit(); int onInitUser(); int onInitParameters(); @@ -213,6 +213,7 @@ int DeleteRegisteredObjects(); // other virtual no-ops void DummyCalls(); + string GetStatusFileNameData(); string InputsToStr(); bool RemoveChartLegend(); int ShowStatus(int error); diff --git a/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh b/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh index 4d0f6a8fb..9dcb4b11c 100644 --- a/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh +++ b/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh @@ -20,8 +20,3 @@ bool SetStatusFileName() { return(!catch("SetStatusFileName(4)")); } - - -#import "rsfMT4Expander.dll" - string GetStatusFileNameData(); // a no-op in the Expander allows optional override in MQL -#import diff --git a/mql40/libraries/rsfMT4Expander.dll b/mql40/libraries/rsfMT4Expander.dll index 778965bb00055e023b996aad6a9e66838f3299a0..0fbce9c3fe02ba54dc2adffe52cea17a6c26321e 100644 GIT binary patch delta 51 zcmZqJA=t1(aKaC!X9W{~`7!;gYW8Am_hJNLrtMyg%)2fyJuTSIc8NJ#kkM~@(PQSm F3IH&f6q5h| delta 51 zcmZqJA=t1(aKaC!PXZHv`7w#!ZuVkq_hJNLrtMyg%)2fyeH7Tvc8NJ#kTGw2(PQSm F3IGeR6N3N% From e69eda9c485c2f474c33f33ebf1f65bf1f9a463a Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Mon, 10 Nov 2025 12:10:09 +0200 Subject: [PATCH 40/48] drop obsolete GetStatusFileNameData() --- mql40/include/rsf/MT4Expander.mqh | 1 - mql40/include/rsf/experts/deinit.mqh | 2 +- .../experts/status/file/SetStatusFileName.mqh | 8 +------- mql40/libraries/rsfMT4Expander.dll | Bin 285696 -> 285696 bytes 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/mql40/include/rsf/MT4Expander.mqh b/mql40/include/rsf/MT4Expander.mqh index 360b92590..d667962ca 100644 --- a/mql40/include/rsf/MT4Expander.mqh +++ b/mql40/include/rsf/MT4Expander.mqh @@ -213,7 +213,6 @@ int DeleteRegisteredObjects(); // other virtual no-ops void DummyCalls(); - string GetStatusFileNameData(); string InputsToStr(); bool RemoveChartLegend(); int ShowStatus(int error); diff --git a/mql40/include/rsf/experts/deinit.mqh b/mql40/include/rsf/experts/deinit.mqh index 99bf681de..27ba504dd 100644 --- a/mql40/include/rsf/experts/deinit.mqh +++ b/mql40/include/rsf/experts/deinit.mqh @@ -95,7 +95,7 @@ int onDeinitTemplate() { * @return int - error status */ int onDeinitRemove() { - if (instance.status != STATUS_STOPPED) { + if (instance.status && instance.status != STATUS_STOPPED) { SS.TotalProfit(); SS.ProfitStats(); logInfo("onDeinitRemove(1) "+ instance.name +" expert removed in status \""+ StatusDescription(instance.status) +"\", profit: "+ status.totalProfit +" "+ status.profitStats); diff --git a/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh b/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh index 9dcb4b11c..b5b3b5a4d 100644 --- a/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh +++ b/mql40/include/rsf/experts/status/file/SetStatusFileName.mqh @@ -1,9 +1,6 @@ /** * Generates and initializes the name of the status file. Requires 'instance.id' and 'instance.created' to be set. * - * If the expert implements GetStatusFileNameData() the returned string will be inserted into the resulting - * filename. This can be used to insert custom data into the name (e.g. SL/TP vars or trading modes). - * * @return bool - success status */ bool SetStatusFileName() { @@ -11,11 +8,8 @@ bool SetStatusFileName() { if (!instance.id) return(!catch("SetStatusFileName(2) "+ instance.name +" illegal value of instance.id: 0", ERR_ILLEGAL_STATE)); if (!instance.created) return(!catch("SetStatusFileName(3) "+ instance.name +" cannot create status filename (instance.created not set)", ERR_ILLEGAL_STATE)); - string userData = StrTrim(GetStatusFileNameData()); - if (userData != "") userData = userData +", "; - string directory = "presets\\"+ ifString(IsTestInstance(), "Tester", GetAccountCompanyId()) +"\\"; - string baseName = ProgramName() +", "+ Symbol() +","+ PeriodDescription() +" "+ userData + GmtTimeFormat(instance.created, "%Y.%m.%d %H.%M") +", id="+ StrPadLeft(instance.id, 3, "0") +".set"; + string baseName = ProgramName() +", "+ Symbol() +","+ PeriodDescription() +" "+ GmtTimeFormat(instance.created, "%Y.%m.%d %H.%M") +", id="+ StrPadLeft(instance.id, 3, "0") +".set"; status.filename = directory + baseName; return(!catch("SetStatusFileName(4)")); diff --git a/mql40/libraries/rsfMT4Expander.dll b/mql40/libraries/rsfMT4Expander.dll index 0fbce9c3fe02ba54dc2adffe52cea17a6c26321e..f9738aa4e672746c391d3a4ff650c13685f82c03 100644 GIT binary patch delta 30505 zcmc$`cYKW5`~QERdj<(26E#hQ1PQ@Cz0X8%!4O>#J$g&jFd5~0NZ_2WgiAglO+DG;xRg8p z*WaMRI8WM0I^D9$_#|XYXPrG^s5Gx(ysq-)Xo$f;ZFWsEV^t9)&pfQBwGM`tJ z%2L&@@LBs+@(J0oZ@kz+o1+(PUs6{y?89GA+e^e6&Yw?yE}MR4Y(?dnwDM^j{@OX( z)2xzK-gmlO%S?L!KGBxXVl|!EVvFdbrcmpzz-U&ZX)?{s|I~(%`t?^H5ENN z1d4KDMcPJFkMy8+O$Gky8M;{Wwq*1fS9L$1rc_(UI^?puzPXTG^=(99)b~0-tmB|R z;gXDqpK4qnPFd>Pcx|VSj&(@3Ia<>~Z9RVbX_}-dPpZ?#x)yxt#&o`}rB`kvPIeAl z1~=(b9VG>HlD=j2osBu!K9B=W1NX*CZ4@d^E|83^Sr|G!j=s>h^a^gm$-;mDbhNRd zu1Ed=PF5J2X3LsrXihv%3>0IF#$}y1?&V1aO}0$&=sVb{k)KuCT9*)k8Y5qm^sGAd zpBD3IG}5VV%L;1h!xK}M(0rUX5wbRQ9>SMuTF9a69@yyVRM#!ue{oYzwhF8V+reIN z1Vjv=+q*5xij5J7f6o@2YycPz#sWK-381r}`yI<(h)2LlXOfuw-&{c6HE;(!27iDL z;9CoNvd18Q?;lSP%!qc8n+r$01BAlK-uJe@~-^UQHUv+Hf*o5DdbA0CeE*s(+717UefXXlp}% z@8w_Pe^UP|kMg~1P@%VPaaR&;(tC|PvMu@rY@joU1#v)B8Br99^0@EhB*?Rh;^_#N ze3$pv(>Pg1QTj%NJHOM}2RR3vZp+aDeX7&+KEBQ#G;4=;fX*Nm#DNjO`Fv+cFZ8MC zZ2iV|^p8INYWLIfk?R1jKmp*QIhhA==YJo{sJib>f6{@Ic>*8c2daW_fW=U@-)9=y zzdcRt@5@IF$Xd`}LHw6@=45NZ7LWxFf?NRgI|KarFP&*nT*d!!8ftfOHz!LI=g-^w zbmwG)!DuiMB!g7&eN+EGlP({a@_I|A)iXEzW?h3HM$2jBuECSJviEMSTCAC%-8TQgZN*w z`tLgQ!!Qdq4gaevwPQJ%5*R^4&=N$0ZlDhs3`T>0ug;Y|5&6j=_5Y$rk-r?QjpgXM z;bvNTgsUCVz3@EH2Mh+I!90{iq6r$+pjzL4oNNe)2Z>-Bm;=O` z7dk^abyP?CZj^8Mzdg*I7T+Jm!89<3%0~yZcjx>wJOPdK;0m|}9)joK9ry~|`cgEY zG$;=OK`5ZLNG|ZON5qFF7f8o8gOeFSGsgB* zxB9Ne++1j#0T+RE8YjDo(B+qU*fsGHV#!F9yh2suI+0^^&bYAhouJke3;^k1IH0pG zh0^EajAS|u8{dKdI)mEBS0+((<@m|<<%|B&gPiH`0ze)(4GKU$@R$J)0ig_`FW|s+ z#Oa(%!8CD#k$->FIqXAdnTbJU9j!C5NsAlwAC=o?!YKir!8j0G6pFIQ2;iQlqK=NO zhaRfv;fYO@&Hq_lKGX|<19;3ryNg0m7U5=5O=6H=Trb=b9(6xn8dpA7tx~63e~S)H zv?wwc{G*ZSD7g_V0Xsnk_`X;ay`5-L%l}!7OToYZ4-gDw0Bc5?R-a&|$F@N?9Mm?c ze~ZQQ|MtMN=3Ew3M}vtVSu6%9SPs^LEkGQWe^j?*!Q}qR$Las9tn)!8KAfrH)7bT<0TK(1BClkk>S3YA~HM zRq%P9hW!q{0x48tvkp)7CM1#moIKfwr2egwsfBdNv|ukG9Zds~pcze>)`<+E_onsa zOV6earW^Rc1$6TCp+3D*{#my44Bz0q`B=UnA2@(})rlc?eqxqthMwRx#ju&Wa_eXQ zt%i;^hl=i>8B3JZf0n@WGilVUT2)U@b>CYZkX-lHvOjaUldd=~es-j6);KbV%2E~) zM%Sk(`HV62W=iV{A=Aa$%R6mI56o~NjEsp|W`|XLJngUNx^oH>XyWWz71#eu*3krd zXm$fKkrvLjkw2*|RYOkFnfRPXkEC`cHE5+db%}~LnPcb2PNo}D6eNj$p3|m+Wa@u6 zAdvP*3#)i?%6~WD?kKt9RKKtuiKhb>*5mID zrpp%gB*WgYH$itb(#g4ZS2mrS(p|H5&dq8<0YV!s?LjWPc5rm)vO4yuE6|tc9&fyTq^RzS58-Y%jzGfgKtvan zgLb1(RkWj91;Q0p=q&KEXvgr1NR1CQi9HtEiShF6SXd!Z$;+Pc%A9CNk4jKztV+G? z*j-y}`7l2*uCt|{bBvA<6T0J_P4dB6XfgF$UP)%c`<>Lrk+x`u0$p`XqLIrjGQ1c= zVhmp7l|V*D5S_ccRv%Re66fZjVB2U%r|O6-zJdrwq(u!xW}HU^+U8JE?Yc`%s41%5 za;Zg!iE8;SwI<=B+H;qhzLq#jm#fl%6{ko^`d~!}xkgK@4CRm2qIFhAkYw5wkuSYz z(#i&8HqBib&gbRPjGYyzWK}u-S~&GxRh5*ZhE-ii7rJm&Yx0>sSd~x0>50`L55(OaLpl{=9z12kP7tl+Lo35EF9kKdQVHndPz3Nq-;D z?E&Jxk~#N*oPNR1!kD>xJJ>aArzr@>2!4~=*xO-T%m7$kG-=t}kL*Ap|% zUw@~w(SP4_qDVeBd*K^31c0Xh#`(cXI%_Prqyq;(zXt6|jXTeBo?bW$Q=&nm_g3R;L>4 zAyyfv%z4(RljAoP#~pFE=7_5`Gw6VAHONsqcUv|7Ogp-3n+5M)UvI0;XXId#YDhad z*89aOOmJ{klJCf<3UDZh|n!alg|E4_s zx~p(}KUek3QSeg#S{TD)+{f&Fp}Q>`*>+k}UOvEv0dRLq@mlt*Y4` z1`GLW`Umf6l?n?B;~Mpr$T~WfE^*dgHuYqhjm-9loOUMRQ|;rkqJ$5r9SYiYbabK8 z?8_w*I*CWYMS3T@X62@kF~K&+s?MSyP3w0{?0c)P%AXybX|;X*@pe6B-#gNiPToJL zMBff#$s_dT{<{3?-OkOcI*>`m(rX8r1=?)R#g&GWxUEBe$T`Sm(%OdxwsT0R;h>3c zl7$lG>70Xh{$45Sbx6gRilz+@b?|Io3ey*jvw~(GszT<_V~6ID)wK2DCZ3&Pr^TEc zy7F)&vC}(;efbZbwBT@MqNNp&AeFJlEgTeK+#fh8LWpHiyBBl8R zG`ypu0S!3X0j|-=qy8S3#ygYFQhL5CjckGuqD(KB>mZe7vT=2`=X;JYdKR+ya9Z5^$${xdnH z+c~Pxpc7-tou1`fIu`x*NG#n1O|+v4-Ebm^%%mqz%oJzir!X=GpT2b7Pihk8ly3jj zh}5RlPEO(X&!9h?tS^2Yax#d2!qF0^DnqCGDU)Z*N=~2RN(voxs!@TJyA> zjG!A%N0LRsZFG*lRat|h+jYz7JUIVjMh98QKr6DY`oO@mDt4wwAY!w zB%7W*L&Y=YY&iePN~fP4PZnnto~=p5%FlE5hW}Zab>@69;oq8Qx3gie_~kE5 zAjPxhU8qU;4?XF=i(#-r_b!ek4YH#1yoeMI>>9f8QXCw-KQFx?3+S_7vdA5}`f_Fd zMFc%^*}^9o>9MPRw9*w{*bU)II3Hr9-L8a@PjuRq&U}W!nfnpBL#ga)ILx%+YB=v_ zp#83PC5LIwRW%0m!_^4Vm4;l4;OppV+iP9OFLcYbDY$pn`Lz=1M;rgzmw&IMTYlA% zrS##iq8l1;y*JF>m?l?z&x% zoTm408%Qjzd?ygL&2Xm;K4;&t;QD{&P6K|VKxO&0sP?W6C;P0sxaiTdcN@XW^v<8g zKdnns?gr4E`Q<&{Jr|Aq^yl<^zEJEMB9F4jy@`Y$F~@017Tgafa(d`~JF(=0hJ39E z+WtWsa)YjWV4|-cSn&n1@GyiZX#0mR0;9)&{pIh$HM*f>WJ^Uz;dOr1{ zj~_*0N|cX{d{8(Y@mK@LcEjVnm10E43+I4`^E*%HRR>E%LF1ovB}eGRCrT`6iKCua z)UluZOkX$x>6vFpzLWq+$T;won0nQbo`3$)926Aqt7bQF~3KV zvo!a2C0R^A{$39|sOGb|K2ms#qN0owzu3iBsMPi_fZlu7Sj>N}l6lDV|-l~U~Q>w0{xAlmo!8%%7cH(xze91*7~ zn09-+lVsA;@2ZCO3~-*Mt_xZ>=W8BIAt!4LqChlQLfgL6VPVaF7lSL$lXproiUz!& zO!Dcn_vdi*^#35o3R&`D44F*5KiVqP@fE9d-TAuYx~`nQH2&icq&f93Xyy056wbnF znMx-VsC_e_4fF^}{c1e8WBdDxnff=$s`& zvs!+w%fmaF@GXVU4Wh*gck^!o>A}J%-wx8hN__VW+ny>UnkT*4Xr5&7H~g|INXR=v z&SjS@MuwxN>(GFSUut%<=8 z1|0d{veUhYQVO*bJ|x<=@tc2Di_ET9mZ(Inz7?VM!;62_%E-Q0k>FPY4<56|{-i!x z#Af=FFfx|y^d~XCryq&$GF-N{<7>{j@CQXYBrG(5G$09VKmc?ZOAjD1UXF)<>5gHa z0!S;;fi(>zlfAsZ%UPNIOCXUGvWZE9NKcZ;`Ua7QvKlWqF0K3-$uHu6?Hm_b=PIN= zeOnZ7!)RDKU{+SWF0PAD#TT-?HViLC9(X`$lPsf=S2TUeb$`}{Nz zD}J*kYGM1kU1jAaZ-zI zsY9B0J^1CXr8F}8TOIPAkfrQhJyPGd=9Ryeaj3XM!<>7f8fzt>gYDP=fovt^Sa~@) zOeE|VIjQe;?80BQwPC>uvV*K@`%b#e9mEZ+BPUvjO$iEOKqOdx7j zQ$!g4fZ5viHoQA2|kF{??+Ifc@5UXnj>kAuxyC&PygtQkYswwGFQFYp>?ljQGT<_@#xc55j zY;sdFq2jtzf6s$i;2c$0nPy}HU;Y%E-3)^6KrWqSL0ifyW|nu7e(Y1vyT7uARM&G1th*~Tn;OYBF*>? z$651MM5q|@Z#uHx%e(2A?B>%way=NIPD?oAv94 zXeluoz_xWGMzWhd?FRj6%rA!6d1)3~7K7+zb~c9eB2!pIcZ^&T8`Pb2Avf5G?j%CB z^+#tDob&ax2P~1}_u?F<{9rtp5Ehp>9+t5zF2951aiOAY!iQr#3++L=5i3jTL1M`v zcCQB+g^ks|Cs{$Zve!MyZ1grUmaOJ|wz0~+U;~!2M!iVe5+$~O!&R>rThWWu^tiE2 zj3{@obG=9=jK|R4q=Puoy~$D%%ewa=Rr#Q;EU6EPTs)GLW%v7#7=G9m7TFgYCzkE) zi&4yAPx_LkK9e{9)rB6pHiv~-`@tSnWAXh6d^C2kAGz!EZR6jSjA->G3nY>s-YbsTA0uF`*HTxA>MFy!so!#EPh$F66s2a-!9jRg-P zfB39g_gy_`Qx8^UFxu3awI2+lmY@CeU_yw`oi%^gTE*@Tfz~coekke6Th_1=5}g~Lc`LNZzD z5u_0xvzoOVL5BGJvFh)S+s-bIAS?L#tHe>Q$dX5ri9Xv`{#|Dn^B#pJrLab$u;wbR zWJ^Yo$3Ase{9Wr0Hg7bvBG`%1B#|GooHd9iLX`=3;aMeycJ{#axzT7T4zxyiSK2}cy z5*b}G+*O0kk;KxHNDRqkzbC=$HJi)&PZ7Jr?oJ`0rJLUneNhi_e!nsAsl=azuv$|| zuy^k?u>rwPpTcBza_nYZrjk@%=fxgRg#|FMa>?jtJkuwWxv+t!li{vhWzD9MP_O)J z|EY0NS0O?p*GsvZZYiy9SXvQH@_rer6?U{7t1 z?X2f)tg%G4a5lL@*0F)9q$Qcf_N0=Ud~6cClS-PA8LZ|U?CQ43tos}y@Kuu8yg4wO zPuR&hq%PlV3i~jJ)Zl~dEGP{&i(~WC;4VF7C(__%E@x%ul6K@e>o*t2*;{sCE(zni z+S$Xoq%Y2+=JUw1(p@HgE3DtKlktXk@&PKGuUxDH-k+amKIvo+Imu3?qu@vuIG^<6 zOVwn_^GS=6!xNp&SLWohvGZY*KFuesyg#5C^a+Xk@>3)-)^7o+U2GptN{&rmKzfif z*$)>Gn}i=Xp1~THW6c-iybc}D+AhU5%~(uo!<{<67{|$?O>)+Dg*e^ZaJ`Iz8XoAYRiu2ei<^jSA@f{K8t}bF zuvV+dFQOI9Ad~qr!&ycKxy#oX#-?PVz-^Y7iBlS_UPIcTxob&FzSj`eYAspFpBpSz zT8@2KOFHnq2D4V{#Kb{GiR;9~L9EpeP>4E4oRH_G42vV+F$wY(@nY`noFEx&+=wo{S%+<8IgY>A+sIS$3%j`;>kn719XJJ_vtv8R?>y~HVp^A?w7sshy<%GDqO@#RS~kfNhkYLz0;i_(esY%osRR4*6bZn0#6v=T zQx9y!7VOZ;v6-@4r$`tx9e@eJDmg&1cyoI;@*o*Z^4RTzWHVnqS}a(OtvH0+P!g+i z7-?7Ay3(E>#)w9=Wq&Rwp=|vTGJ;=hb9JKjQLIIUjU^r>l&oa39I}k(+pw)Ugz`gM zv--!#K%Q^SwjLAXtyulzV!RdGdR&Z0vHH1UJc@1272_>g{Sz1l4EhN?&)kBI{0S3^ z=ucv_IU9MB3@7K=y_4jE*y~fcBHU*;Pm#$aigi5=>-&LapT?kSScx;F6CC)SXNWv7 zV&u2Nlt!V6sWbLrFpRkg#qs?~)U%Q{N7<2V>lxzfUogU{0gJWJsS#h^7I!`!F40SN z=L{_H874gocL!(kSy-x}EcPt6>M=IwEPTop>_;&e%dUyRFYL=%qJ>MTJO@W+AnSXM zOo5qtd=5=L#C*;}g)z-}@>^`_U80Gc7An9zw2k8^?u@p!aWXMXohvvE=TrDLsf!~Z zBPUSIY3E3Qzk+s#i3)KlXDP=>xTer(<|-TNEPI=c_?c9*PY_+RwsEsuS>l(QZ5?Br z)rwzwB4>%KTHaNy_^oK$xSg(Qzhyhio^X}Ln;w*Hu}N%__(dwp-g1@gI7Dnx6OXoW z@5F@YwsEBwioJ1E-obn>kN|t^Dk8(>cnBKiy0s#!k!UGB#A|E;ULe{WrT>}mrJ<;S zyX+gc@YF5jyMd;U$@z_P+$(3&Syz(y^)t50d{Hag zmE`<-I!@+HTIWhS@6-x)Ce3#xopvVaoJo^iNhidlR5tMvd0_u$EgJ0iZsEC8_^ux} z-8sLwg)?p;$1P;Lh3#(P2e+`oEl{^G+bvA7+KZk(&YASxWDRiZ^l%Fu{;AT)onv$h z3bzpM7J}VECAUzvj!EAS%5Gu2hNT6zOZxQZ*nC$<|=F;fhXc}>jJvjl{sfbF>kzyBnAat+?& zIHvxUSWD=1Sf`E!Y~rs(Cl*JVSX>NH{JL0N%Mz};>PA$|6a!@55HmF_;f5;{Q87~t zkolXKsb&ekxiS$IGsOUzH^of60J@1cuQ=HdC41RNF&M+Nw~(n(vT?Ui5}AmKnPPy< z+hV4Ijl1p2L{!YYO-y37cf@MtEa8spnTU$d6a!@56*C2vaMzW2mqcQ#zQ2nXckqGo zNkhJIJvJ~OK6tr$Ymm*H2~ zjfY5G!F(Q(Uc8|ei+=>a7k9o#s0f#@N0_q&rh81}e8X@y_%Sgt?PGW~2Oi@#4X5le zZb55V$tSo+_6uXppAZEv4`Y*_kO*E9#E#7@;@ z%N?#RJ6v6UDt5UB8~9Yrt-+Q*b#?ivt5d&=ovO~p|1Ng9I@|KQtINN;a-ZQ9zdn>D zJadioGuJeT0jA-(I1M2z;khdlQE`qClo(%4T#1*N;RT5*QNtO*)$;|>dpxcx=0t|D z3onSyW4$xRg|ISz5FOvYDr@!!F4r}xvRMepV|MBfI7*9IrI+yEngp}Bm&h$1%r?G+ zFO0SF5>LcJdWGB?RoJXoNWI2>MO3_ty+*1gh>d%VRMdp1SkoI!Q-{i|%Nukzq%uo= zL!vyc28wN}7sT$qA)QMkIAa%?6>FzQfA^RREjt4p+Oc z?8kRldk5K@cceex-k)`SPbT61di_1dV3D6#>q{2&f%M=7KQ{RTu6ro)fvhjl!B@=N z!Fqgz`_<_qUM}AJh&3^dg%^;SqEZ2A;<2ZqsO0U-3Jb`7TmtuhB1+$!3Ze~k-imuf z_7vYQek1X<<2WY$OnUOoE3kf_F-D#h*bfLj&XgCc2&u@PeJ0ICGw}sC(dOk@zb|-$ zrusrcB(}KStm_wCXdGWiM2RKkMEy4`;49uLg_mQ~zoN}pw_izjxP%q|#4W3RS=RMW zqTvI}vIT!){BN>f{v`cMB>0FWF>2r7D;M)&o4&!dyv%<4hHgz~VTD-g(K6Pp5W`(e zCaU)IVLJ**JV{v;${WeHMX|h^ujIXm;s0}OmhDY=9VsI#kFSch;MX9hq@j1pWq08D zjeLc99Ngf>57F&}Q2gp8xD$TfH8HzvF@B!-UDTQq{6MlYyR--IL&*N@AW!~eF}|~e zJubti+Dl!8U)Vm>BdtN{)D{lqX-B8mg@yY%Z z?)&`bC|^m;_uYp4vts_1f98LDh5T`1{$}K}SKhq36eHgXbJH5<;Dl@g-H3t@zR)y%q+f%0uT%mE5>IG3JAq z^NYjtakAQolmEmW-54LlhX&e^e5%-a{J*d`bU4TH2krvr9bZ@jQ6t@{@zAX?^*fE_ zY^tb{=hV3F)@bmTMx2Zt5906O!joBrx8fz-Ju#S*T{W1$OK1)sxwjg>Q&J}>oR?*Q zogfErXnQ0`0%EhozhAs~*^IKh%*<|A=PQvT+3&0K(S&TB*}Nv7-MpscvX!J~R~{g_ ztg!Dd$8(8(Jhutl00I6yHyCULH-KjVHNkN_5e{ooA<3go#SU@6D}Z-8HAo@)+9 zfTbV@GXg%(k9}StspZ4@ zyK+4d;=09^W$KlZ+CH|TaUVC^|SpSvNP~|dzs|K`U7SovWiPF3ZeLzDApax=t~L$HtPpTX_oVYBp7YJCnn^J3BKzk!G7t%u7XPu ziUq|`Z0{P@cD;?_M^tmw?GAN_uEzu-6npLt!;0dO zMWG6z*t*sT@y!+26=4a4{SkT~9EH#m;bep*5za<*8S5vOku8$ii?>Lm#l%6_#MFp-?k3oDu(gwEMPC{|w`p{Oq}ii@E*JBFfo6Si%uq@F$rQKWJy2*pk;D$33* zii@F`FNR|EVkl1TW>#Sv`u_@1_eL|Z%x#hYma$nFeBa4&eeod@mM`JeK@gmu_K@eMm*xU95pJVC=7CtCX6K9DWjxe&+r1ekCEO9GIWy==7`}G5(&?q2VjC6+dmCz}Lvoi@6 zaVpY-(U2mzs?dd;1TyUShy}tb!o?<`CunufU1a2Q+=%;7x(6ar8HRNy!n_6m>8}uf z6^Ee~d-)vY@FSrlOpy(Zi$z)Ix3t2<(a4q*1DU9Q6Y5!rx#i{%E_fd1 z9$^j?^MEj)02!)tORFsZ#`3Pe_qdvfi^&m_aAN~z>gIB04 zUCLwLv9>Vf>5uUYy8{XkY#>jR(F@rVWJ0!qbj0(-IFN~YVu->vq}vb%!)z-+6VM*? z0As)`uoP?t`@mUnA3O&if%i)Z7YytX_|SpYpeGm(rhvs@3&;Uiz+>%3(fzQC>t%UOfwSfh+0X@J_kO)%2GO!&SeruO-KSQ_&-UH8f z60QnRfu^7v7y%N&EU*l006V~Oa2DJK4)6wW?KT2d@G50rLYYfnZP%=s^R}5_AMZ!8EW8YzEmN7u*6bf%Kz1sB0B@c0wv{~ZMGvxKV%YJvKoBNzhgU;$VM_JLF29(WH*e35WvfIkQY zO3)N^1B1YLkOnfrZg2|R0IxtH@cSy^!oFhu^$^;CeqaKa2iAf^AP?LI&%p;!{7(t& z0;UBhfEBa@J;4w#38VoAegL~b4#)#H!E^8tcznYMgK#@O%%COc0tSObFcT~W>%b9k z3fu;-fwT|{3{(Xw&;)b<{lPe32NbLWyTBQ613U*`fDb3-0zoLS*T#nqGy*oz4GaNe zz#Om|YzKS6d2k0j1%CqkUmjdVPzOYUD9{-U1o2=Zm<7^72G|U;z+vzcxI(0M?iqwa zP>z>!HGm4#2Q5KoFbpIE1~!4?-~zY{-hna_DOVjBK@^Aq1Hn`L9 za*ogT&zH23WZNG|rc}fWso=ezC1*YCC3F!woi0VUQMX%nT=!8Isu%QA^cniC`V;!! z^d$@`L!@E8VW;7c;fmpf;hRBf^fyj2ZZ+;R7Bkg08BEPgT}(Yq38uFu&RoOX!klbQ zH}5bXH2-EUXQ^S)SuR^XTWVTW*80{S)>YPA>qRRU%yCILm0ByhDtg)#;}z2tvlZ(U zI~98r7Zo=Z`HIhq;>uFWPRc&YIOPoG0wq%(QJz&^R6bR{SAJHOR|Tu8tE{S)Dw}GM zYK&^4>bUBH>ayy!>Q5D~uB$evt?Dl7e(FK$Y3g+KBJ~dSL3NJ$H}zxnGqplv(KOWb z(hSv%(yZ2O(d^XN&uM5N$oJO4~-;O&hD-sNJpIul-f~KYJLFdYeX?#+VkEGEM7Exu#!CznY4f8<^XgJDNwCrznB9E%hDsBlVN@8}-}thxO<5 zxAi3rRSgXdy$yp635JP=R71L9v*D=WgyE{;9%ha+mNzPmMq`w*gRz^jpK-8pqA|(1 z(zwTXz*uN3V=8B=YciV}nEIMVo5q=_X^m-v>8$C7>5l1}$;({UEH}3@$C!JWCz@xQ z=b3-Bn{&)RnID?pnhP+CAr_Togk`K{re(P$({kJL-16E|%Ia^eVvV%6v9`Akvre*3 zwfAt0?O#mCBCFe#$}EaEp}7lt)!3ROeM!RJT-* zu(3Xk+@@ikTR^3qDOx+#ZYp8mLI!(P*{iAxn`WPnd7xf+W19gG=tD4hDHG!I{ znsAL=qt`UhwAMsx;xt1vBQ)`vwVKVE9hz*-QOzmMMeMx0tYrylJ@3`p4cZ;rZ0%9) zDeXJ$7nV{&y1ZtbVT56vA;~ZcGqB5$jX5}FxM=v*aM$p}@Y3+nP-rM&R2hw|rH54O zzQL%u7`9p-n0(!l%DW@t@wns>#6in z`YD?zTQO@%>2R-->auEobv1P@wL;x9`(R1wHl9ec2YN{ZNV&eMAy~YLs%fe@Si;$= zqpItSm64`+|EVda_0m?*R@PS6Dzp}6^p?um^$Jp7W~wM%&uo>Xy|d@Yq|e14(~d7I zP4g`;_r=xm0G1-n>I>S;f! zJOQJdr@W@TrF^UWq%2exS5;8?t0Gi_N~O}NOsZ&AXH~2!PBlU`PPIUlsalV{ovS*B zRdXHdCSUbX^%e%Xj9R7cs2-wDRv&V%lS}Fw>U-*^>euQ|*yNrXAB`Wjx?H2tL}_fA z_L|O`7)`9EZ>|3vCT%?a%MubOh&C~aG97i}-?Kvr7b9D1{OLdvLO}bsWW3b(~bosi6 zFbL0eUv)(9srS+Q>8t7GdW}8`w!FQ*vpz;YTpzDb)KAmT(J$1m(67U}xEprjy#A8j zp?|J_rGKX{(0|o)c7xR5VW?sVHALXZG#MHhq73Z~F^0Z|p@u}m6vGn33PXlronfP4 zD-P4$hJ7$4?+u>~HH~#)PV`2rv9YnGv8}O_vAeO4aiDRyF#(2UigBhf%}9;e#>2*3 z;~C=}<8$L1V}bFjkuyn69;VVJnW?5@CgqA*{oJv0r#1d=S0}GOX%gc~f~uc{i-Vaq=YC znN0a6`H%Ab@?-Kd@=Nj?@_X{9SeZmoToH&PB~%e+S41cTg-W4Qm=uwUXhnYog%fa4 zaaAEzmd66^t=y(Oq&%j4qbz|VzaCayTdcQ!IPM#$JE^azpQy`g1dUbG2*-J%W{PH; z=8)!?hG=D4t+u1KpLUQoO}j#ysXd{+ti7)NQ`=5A7N)<1UWK{ssqd?|r(r@f^*`%x z;i#@^5Dc9RV+_*`vklwe>>M*ZG`uww7%Ce_88;jKaT0Ggy)aqKlgw9Pt)H4pS^O#}|ckof`ZL2uKTn64fW01WtrbZ!Bh!-YfC$AK4 z*#$p&ZEXH&aw=aU&%*E&$p4i4Dw<)_PExGGrhTB`m8D^&_FylbQF1D|YLaS&s+)S5 zW|QU#j=_AS}snbS~1+1`Bej;iTc3 z;VC-yr@<~W{$c#n$eWs*I-9ziCYYw0mYPGEqW}c7|T4%D$82S5zFtEPnK^MKWjs4jCGuKHO{BY=>2_hZSCZ^ zK&-9$LJOg@FhUp)M+k1^v49O0Er3b?dw zO&v`!re3Bv9N;reb4*)JyG`dzd8TWon;4TfCaJlkImF!F+{YXTXP04AuA6V08(Cs4 z>F~aOuxz#*fCu)A<+{bw8f>ix=Qqk~!|6TAI>$QSnr%I8{S7DgA9itga&UfQc)YP7 zw!*X@hiShiNU?$D$rs9hknhF@dLj=|XcXNPBNYo2YZQ+Ze=6!KTfuOTQl=_@P;SB6 zxTAc5T~b;VfPKgeTBus3TC3WL(L1C%1FQTD*0>T5IJ3Hqx`Vo_+TK^4 zf`40iiF%*cUh2_F|9F6&cPM#|-qX<)IVO#nuhA3V* z55U@3SyttI?4|3<+e&)~oKpo3vihpls`jd`ILbz0*jA|4s5YpwR3~waU4t*0kE7}X z+!_zu96I6dut{@Xb5HXP+pn~?F0Km(ZGViNU%d59_taX|-1rGjw z>l1Ng@;PoCM#fX9AovSP!74Nox(fq^Vet0%Vx`$n!PS2+d=|>dtH2*I!$)ilFL5!J z-R2?>@sr#`;iagdP%8|Iws8K_6^j&G6u03rmQ_|#Mk>20H!8D~`!S#wly`AEtAlau zgK=DdWhcSWUtS%m4#$nmrnal+VyzufpHpAL@&8!eMAO->>7f~>84aIs54^$WnlBno z8>UrjjoOx4n|3IMbO{#SR_!kBakzyZxTiLQJ3m*Kt@~B?R99SIMjxWr#&^a~ z#tNnYQ&m{OuGsP8O_QdV6slq98whQL!9oJg&=g^luunKF z+z=dg;RO!PU^s$Gc>^4r!?5f3%TLR%%3sP|1Fu#zQZ!c#b>6$SDh?@5;DUTd@ljDm zSxp&@^Km#Vz#`>3<))$){YmMe@`8UBscHuAthZ{sYPsr1)l*f0s+hV8CPIh3-yOc# zNOm`WTHFvQ2kb2PSYH&S3lgeQZ!36D{;>{ueqdo0gsDltKy!e*EZ5dYsYFg zY4>SQ;!^xV`xTB>Rh^(y>)PsKb^UbHbqnEK?bn^rU4mov4C}s}z9H;EPyIw(ix=vb z>i6qUP-;bxuLY>qXLFpqQYoK5Ea=DWC56|)3b z>RVb^+T*b5Z5ayh$8Jf%C7EG5?OQCnadw@AdHK~+#;UX$tX69iYb$J}Zq`I=vURp~ znRN?J@`Ki^)|=MH)_2%cc;StCl?Wa}un;PQ2@!&TEj1CI(KcM3j|umLSJ+eHO>4G1 z9aH(cyby*cM9~A&I7;!O;(#JY@sr|$;)>#-UGWs>dnugn6>$Tru2jQ1MJxL%rzq#* z7JVK*cPUj3%x09Ti)tf0>pxWG)J?ICA~k86449!iFwauFvrxey?x7uly^^cFrhS8n z@r7|7jh%8t_dw?hAJ)qd?Yz&fGHijhd0=>F@WEcN*E5baE;Vj3o;2pcul!_eX6j>_ zYsxY`G1WEan{}3EmTp-6t1Mfv>hD|LS~#qEg|!_(r&@W1b%BbqETBh0zo7zOZR-J|QCegICYYuAOV$TPm2hrL| z+T~bQ1}vs-x<0yrx_I3rokU*|*L00O5`7q`AE%$8r}{Pe&G5f3=$=Y%*eH{APM>`eKrpJS`wH!X#ha#*-6@oML&^@Q~X?EV+L+#wvkgvBBi?S2H@&W1uWbgd^` z&x69x!d>CD@J%Qyw+F%fRLYI=C|LbT@&)p(u=;;s_DbN`Y_8}6cYL+t2gQEHImLAx zpS)6|9F4`vVDm4)K`T&tsj8_AFf!tqc|w(^x~lpFLsPQo9b6}vngsO}ye4>qtDZ)a z3ahe5lcV`X!)v|q?k3C*vtrcN$Lj5<{RMB@-f1i7!gZ~6OK>y$e|kC-|D4V=3@1dC zqJo%)hKeN>#aX_yZ?#pYh@cxIsI5~=TB)Hr)nkb^25E^%jV7ZRp=B&VQyojSmQH0N zu`3mWFqO7RMXBoiVy^F`Gk-$f_dNG=-`D+qzns}a?ORN*tWkk9T3!VrRd)n^d z?s328uYTT}jO6-ys#W=R)8(LAY2x9@O>W@zYC0d zj3i5=rG=QX_nGN3Ci+{XxgMoFCy$Y*a$0YYs<%Qf56Iuh)l5ReP>aw*u*+oRd5X`` zy}?*~4rw_Sx`IEu&GE%?Y-!3X$`Y33E#JG~>8M+o6?V(ynL?^k#a3?hn<+AT{%V zxhwS@`hGlYt$rUz)&W~F2+5giEG9~9G>VPg#Hs5>wQ;}Ml8m23zJJa9v$;N)<}zSZ z6HVvr$3D14en#q}Nw2H0We7yJB57^!9mG&mP$lgz`I%!|BuK~+j`%VHhaH#V; z_SRfyF~|Hjj(Hz3>ymTNi6UOU?q<5n+|BM6ET~$yGd{1s_ginG_cC@P$1CuPpy?G{ z!H@MZ{$*-kuA!5V2nO`98*dBiae4=YQ$i(HFG_4F#sywuh&WFCgXm|7i^cWuz}@0u zM&w)ZNAW*mBSKh5sh{*TH};~Gf#X{(?T|i~O5yUi2<83#>Z4G@K~Tfd5W_ivKRl>@sa6t3eg*(_)nx6r0rXAR7HDq;v1GqiqFvB# zviqX+=4`(tF7^+42EE2QeH*6agnkX^8*6kix-$#YjdUa5@V6PeaO9On1Ti@YVl&-* z#av((LT5e!G2Sp6U`D!Inl+p|&1X**S)Z{hf3R*_(f0keY>%)f*)wsBEA6$+%pn;4 zCHsD+rV~X-a*z+FIoVF3vlBCNk|lKAY3Q~=z#Vt6JItN!gL!ipr9y1w0k@10-P-F) zSTryo<1m#Ax#M*#+B2ZQYu@d!vIoVhld;mxtpw|L+%#%++DV|8sD;s$=n6M`++>)p1bP}t(#1YTB>L3 zd3phQc97w0Y_!Mm4W;8R5BgQEs-W2Ip1_IkAm@lDjAE9mEnAPSlaQYsWWI0wp zD+M?CvNg-f1fm@TqMg7^UdK!N;dN`+u07eF#P9xk#@Q>BO#g(##FS*tDqeSnSfc}IT{aRM?C*E;FYH)4uLCCMxAt?CmfmcoES3QRwf% zvTmyMWd<^6u2w6jm8(jX(oI!ptk0PSbsT zAY8gYFVc&HL|eng*7?2%<8cqtM4hESyh)yY+~0o;CBD zwHQe{Wu3DI*iS(YR@k4iV9wds?QV|Zn9eL`5k1rAP7V8{sT=1$<4$s?zz;qGQTerQ zq!;ZaA~>Fx>}7hn-g{nc_{g?JM(7+_q!7)wYOqR@VZXUh-&#Z_n%-in_`0|NhkR7L zAYK;xNGa@#1?=}#e43?HgIA>=fmDv%7ud2ENOcfhIWJeq-9ieEad~KMs4#TU51pn7 ztO|8g6cE93Wvx=E9OM|!|Nj{OCnFcA{BDtTTy2Op{MTbVNd&#eC)x(bmI9<$@UWFzS!Oav*EUVgXdVSUe+M_xbGVL_O$M3y6lr(iswAjZ`P;EZD(vfn3BypJlN&3^fTo zNQXL!!uvqzc<2n%{2TIoFYI}liyJ``9^h<=dEwY=WEewWwmv$uZ zKD~5D@5?s4tp8j0?=#vN9gSYh>oQ}75o0Ers%gQd(wNhQ=3euk<}tIAC0L5}6dmAM6tuf z%X4B|sjHMEy~XFY25WL2x>rlRRX?mB${)*dp&po!R7$%Utgf<99ADUDfX*!dsB9L!&lV-;N-VSB_)`}e?dF$s4+}y4+3enwicpSf?51QdjQ_q8P3sPA3->stuN7Y z^}k{(!a8_FufSSF7;Pwy`ti|?G+r^@GIETq#(rqWc?jsgsfm83RBmq$rrSxgeCr+S z7ps~5*l0T$h0C@-K;eq*FYT}G8ccVr^9VKnP-l!Y*_r9gb8(x?Zvcsp=4 z*Sv25WsyA2f#4!Ej|#q!C1m@w*Ws&=V=49&$B5I#bT0od;ySTH{GJA>CH}Mv&1Ncb zu3mGtNuPqos!8Jw0ApcClR~JN2u8_ci1zbxog$X0x(g>0GW{1c}w#nG_PmM9Rm_>UOnUy@&^m4`+&8QIG1;sHb5YeR7}I zqdNNa!_wS($m(Pbu|{JU^LQguWL*HP+^~AuHcvX{vdfm+rQGu+JKlK|??0VnKOdR- zr*q7ybDF!YT!CcYAKbXiUEvnF6(rqSHwK{@><#l~;s`Uz_}_TnbL;%>i*xHJ^a3wF z>(kLqrI*`4iBRA9YvLQ?GBW)>u~a-R-oOY-r21!p;ZxagS)5p%6n?y6(EiTF0lv>E zRS>=!hn|JiOr<*5jB`I08XJVsY$RrbazH6pnp4+yP=}}^aeB+t)f`QQTBZ6mYV%;x zC1dc$!g8o(8K1D`RBJtXIpgZj>(ljg{ZouXxqeS?0UXo;(oB3x zT*~3Ui@jI}OfM35iJzifrQ%8K#ucL0O|W1Xt+$rq!RsO(y{|N!>q(U+@E~fQlqtQ9 zs->W5qvhxEb|p0N7eNR&dHxhfF>xVO6Kbw>QWAOMv`Tr;=YF>G1mhx+_@?rU(wImr z^VX?9T~3Pn3_)tVnx(B|b?(!WDZ;b#ew5p@p|XW|wNu8oMkBL@`5V*3YJ}1GpLnKp zibqO~Se64>mTy@bXva#dC_9#Z>}k5f9P;dO`>I_{8y4$~=-~_?$1XsvHaW+gTBj8Y za{z~%#?h|9ZJa{*le{NLHgicey8x|^B~@)Pn5c@a;J4$H^+b{n%IyMRmW;IUB}MD9&` zh<9-V+QCQ)J)JN1|YBpeH3yI|vJ0O-F&_{bto2b3~ zr1rX2sC~q-eSt0hDqyRf^q#uP7ENXqEaF8$p1z*<1m`Jx9^^@pi-SvH31n0E6d8w& zGUE(i_ci0Y+)Z{=9A;!WuK>2t{~x1auBM2MvARGHbW-LdI{(+K6;@&JnCLj|#4YQt S6=QedNs<0>6DR7Y4*vy~IC9kh delta 30691 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 From 6977a13998961d77381eafc3cd74a10d20d922f7 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Mon, 10 Nov 2025 12:39:00 +0200 Subject: [PATCH 41/48] update signal messages --- mql40/indicators/ZigZag.mq4 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mql40/indicators/ZigZag.mq4 b/mql40/indicators/ZigZag.mq4 index b35810ab4..ba5e6fa7a 100644 --- a/mql40/indicators/ZigZag.mq4 +++ b/mql40/indicators/ZigZag.mq4 @@ -882,7 +882,7 @@ int FindPrevSemaphore(int bar, int &type) { /** - * Update buffers after a SINGLE or DOUBLE upper band crossing at the specified bar offset. Resolves the preceeding ZigZag + * Update buffers after an upper band crossing at the specified bar offset. Resolves the preceeding ZigZag * semaphore and counts the trend forward from there. * * @param int bar - offset @@ -932,14 +932,14 @@ bool ProcessUpperCross(int bar) { sema2 = sema1; sema1 = Low[prevSem]; lastLegHigh = 0; // reset last leg high - if (Signal.onReversal && __isChart && ChangedBars <= 2) onReversal(D_LONG); + if (Signal.onReversal && __isChart && ChangedBars <= 2) onReversal(D_LONG, upperCross[bar]); } return(true); } /** - * Update buffers after a SINGLE or DOUBLE lower band crossing at the specified bar offset. Resolves the preceeding ZigZag + * Update buffers after a lower band crossing at the specified bar offset. Resolves the preceeding ZigZag * semaphore and counts the trend forward from there. * * @param int bar - offset @@ -990,7 +990,7 @@ bool ProcessLowerCross(int bar) { sema1 = High[prevSem]; lastLegLow = 0; // reset last leg low - if (Signal.onReversal && __isChart && ChangedBars <= 2) onReversal(D_SHORT); + if (Signal.onReversal && __isChart && ChangedBars <= 2) onReversal(D_SHORT, lowerCross[bar]); } return(true); } @@ -1024,11 +1024,12 @@ void UpdateTrend(int fromBar, int fromValue, int toBar, bool resetReversalBuffer /** * Event handler signaling new ZigZag reversals. * - * @param int direction - reversal direction: D_LONG | D_SHORT + * @param int direction - reversal direction: D_LONG | D_SHORT + * @param double level - the crossed price level causing the signal * * @return bool - success status */ -bool onReversal(int direction) { +bool onReversal(int direction, double level) { if (direction!=D_LONG && direction!=D_SHORT) return(!catch("onReversal(1) invalid parameter direction: "+ direction, ERR_INVALID_PARAMETER)); if (!__isChart) return(true); if (IsPossibleDataPumping()) return(true); // skip signals during possible data pumping @@ -1036,7 +1037,7 @@ bool onReversal(int direction) { // skip the signal if it was already processed elsewhere string sPeriod = PeriodDescription(); string eventName = "rsf::"+ StdSymbol() +","+ sPeriod +"."+ indicatorName +"(P="+ ZigZag.Periods +").onReversal("+ direction +")."+ TimeToStr(Time[0]), propertyName = ""; - string message1 = ifString(direction==D_LONG, "up", "down") +" (bid: "+ NumberToStr(_Bid, PriceFormat) +")"; + string message1 = ifString(direction==D_LONG, "up", "down") +" (level: "+ NumberToStr(level, PriceFormat) +")"; string message2 = Symbol() +","+ sPeriod +": "+ WindowExpertName() +"("+ ZigZag.Periods +") reversal "+ message1; int hWndTerminal=GetTerminalMainWindow(), hWndDesktop=GetDesktopWindow(); From f65d172a9a152a08ad6da8b58894b7b6b3096222 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Fri, 14 Nov 2025 07:35:06 +0200 Subject: [PATCH 42/48] update chart templates --- templates4/17 ZigZag(50,30) + Donchian.tpl | 191 +++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 templates4/17 ZigZag(50,30) + Donchian.tpl diff --git a/templates4/17 ZigZag(50,30) + Donchian.tpl b/templates4/17 ZigZag(50,30) + Donchian.tpl new file mode 100644 index 000000000..e1275ccf4 --- /dev/null +++ b/templates4/17 ZigZag(50,30) + Donchian.tpl @@ -0,0 +1,191 @@ + + + +symbol=GBPUSD +period=60 +digits=5 + +leftpos=9229 +scale=2 +graph=1 +fore=0 +grid=0 +volume=0 +ohlc=0 +askline=0 +days=0 +descriptions=1 +scroll=1 +shift=1 +shift_size=10 + +fixed_pos=620 +window_left=0 +window_top=0 +window_right=1292 +window_bottom=812 +window_type=3 + +background_color=16316664 +foreground_color=0 +barup_color=30720 +bardown_color=210 +bullcandle_color=30720 +bearcandle_color=210 +chartline_color=11119017 +volumes_color=30720 +grid_color=14474460 +askline_color=11823615 +stops_color=17919 + + +height=5000 +fixed_height=0 + + +name=main + + + +name=Custom Indicator + +name=Grid +flags=347 +window_num=0 + +show_data=0 + + + +name=Custom Indicator + +name=ChartInfos +flags=347 +window_num=0 + +show_data=0 + + + +name=Custom Indicator + +name=SuperBars +flags=339 +window_num=0 + +show_data=0 + + + +name=Custom Indicator + +name=Inside Bars +flags=339 +window_num=0 + +Timeframe=H1 +NumberOfInsideBars=3 + + +period_flags=3 +show_data=0 + + + +name=Custom Indicator + +name=Moving Average +flags=339 + +MA.Method=SMA | LWMA | EMA* | SMMA | ALMA +MA.Periods=144 +MA.Periods.Step=0 +Draw.Type=Line* | Dot +Draw.Width=3 +UpTrend.Color=65535 +DownTrend.Color=65535 +Background.Color=11119017 +ShowChartLegend=1 +AutoConfiguration=0 + + +show_data=1 + + + +name=Custom Indicator + +name=ZigZag +flags=339 +window_num=0 + +ZigZag.Periods=50 +ZigZag.Type=Lines* | Semaphores +ZigZag.Width=0 +ZigZag.Color=16711935 +Donchian.ShowChannel=0 +Donchian.Channel.UpperColor=16711680 +Donchian.Channel.LowerColor=255 +Donchian.ShowCrossings=off | first* | all +Donchian.Crossing.Width=2 +Signal.onReversal=1 +Signal.onReversal.Types=sound* | alert* | mail | sms +Signal.onBreakout=0 +Sound.onChannelWidening=0 + + +style_2=2 +style_3=2 +show_data=1 + + + +name=Custom Indicator + +name=ZigZag +flags=339 +window_num=0 + +ZigZag.Periods=30 +ZigZag.Width=0 +Donchian.ShowChannel=1 +Donchian.Channel.UpperColor=16711680 +Donchian.Channel.LowerColor=16711935 +Donchian.ShowCrossings=off | first* | all +Donchian.Crossing.Symbol=dot* | narrow-ring | ring | bold-ring +Donchian.Crossing.Width=1 +Signal.onReversal=1 +Signal.onReversal.Types=sound* | alert* | mail | sms +Signal.onBreakout=0 +Sound.onChannelWidening=0 + + +style_2=2 +style_3=2 +color_6=4294967295 +color_7=4294967295 +show_data=1 + + + +name=Custom Indicator + +name=Donchian Channel Width +flags=339 +window_num=1 + +Donchian.Periods=30 + + +levels_color=15453831 +levels_style=1 +level_0=200 + +period_flags=0 +show_data=1 + + + + From f1f248d2b1b610c732bc5e0f6076f5e40efea89f Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 23 Nov 2025 13:46:16 +0200 Subject: [PATCH 43/48] bugfix in CreateInstanceId() --- mql40/include/rsf/experts/instance/CreateInstanceId.mqh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mql40/include/rsf/experts/instance/CreateInstanceId.mqh b/mql40/include/rsf/experts/instance/CreateInstanceId.mqh index deddb3c52..53456e9df 100644 --- a/mql40/include/rsf/experts/instance/CreateInstanceId.mqh +++ b/mql40/include/rsf/experts/instance/CreateInstanceId.mqh @@ -32,8 +32,9 @@ int CreateInstanceId() { else { // online: generate a random id while (!magicNumber) { + instanceId = 0; while (instanceId < INSTANCE_ID_MIN || instanceId > INSTANCE_ID_MAX) { - instanceId = MathRand(); // select random id between ID_MIN and ID_MAX + instanceId = MathRand(); // pseudo-random id between ID_MIN and ID_MAX } magicNumber = CalculateMagicNumber(instanceId); if (!magicNumber) return(NULL); From 8c7ff27f55d2e34be07177ff5362d1f7bd3e2623 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 23 Nov 2025 11:32:29 +0200 Subject: [PATCH 44/48] fix parsing of compiler messages --- bin/mqlc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bin/mqlc b/bin/mqlc index 8870369df..32fd82bee 100755 --- a/bin/mqlc +++ b/bin/mqlc @@ -289,7 +289,7 @@ function compileMql40() { # on success if ((!errorStatus)); then [[ "${output[-1]}" =~ ^(Exp|Library exp)\ file\ .+\ produced\ -\ ([0-9]+)\ error.+\ ([0-9]+)\ warning.+ ]] || { - fail "unexpected compiler output in last line: \"${output[-1]}\""; + fail "unexpected compiler output in last line:${NL}${output[-1]}"; } errors="${BASH_REMATCH[2]}"; ((errors == 1)) && epl='' || epl='s' warnings="${BASH_REMATCH[3]}"; ((warnings == 1)) && wpl='' || wpl='s' @@ -442,25 +442,26 @@ function compileMql5() { function formatCompiler40Msg() { ((BASH_SUBSHELL)) && fail "${FUNCNAME[0]}() can't write to outer scope from subshell" local -n logmsg="$1" - local srcFile="$2" errors="$3" warnings="$4" type code level levelS file location message len + local srcFile="$2" errors="$3" warnings="$4" type code level levelS file line location='' message len - [[ "$logmsg" =~ ^([0-9]+)\;([0-9]+)\;(.+)\;([0-9]+:[0-9]+)?\;(.*)$ ]] || fail "unexpected format of compiler message:$NL\"$logmsg\"" + [[ "$logmsg" =~ ^([0-9]+)\;([0-9]+)\;(.+)?\;([0-9]+:[0-9]+)?\;(.*)$ ]] || fail "unexpected format of compiler message:${NL}${logmsg}" type="${BASH_REMATCH[1]}" code="${BASH_REMATCH[2]}" file="${BASH_REMATCH[3]}" - location="${BASH_REMATCH[4]}" + line="${BASH_REMATCH[4]}" message="${BASH_REMATCH[5]}" - [[ "$type" == [12] ]] || fail "unexpected message type $type in compiler message:$NL\"$logmsg\"" + [[ "$type" == [12] ]] || fail "unexpected message type $type in compiler message:${NL}${logmsg}" ((type == 1)) && level='warn' || level='error' ((errors)) && len=6 || len=5 levelS="$level: " levelS="${levelS:0:$len}" - [[ -n "$location" ]] && location=" ($location)" - - logmsg="${levelS} ${file}${location}: ${message}" - #logmsg="${file}${location}: ${level}: ${message}" + if [[ -n "$file" ]]; then + [[ -n "$line" ]] && line=" ($line)" + location="${file}${line}: " + fi + logmsg="${levelS} ${location}${message}" } @@ -1186,15 +1187,14 @@ metalang.exe 1=warn 2=error -2 ; 124 ; C:\z\F\Projects\mt4\mql\bin\Dow Jones Breakout.mq4 ; 146:12 ; 'HandleCommand' - function is not defined 1 ; 33 ; C:\z\F\Projects\mt4\mql\bin\Dow Jones Breakout.mq4 ; 149:8 ; '5' - comparison expression expected -2 ; 89 ; C:\z\F\Projects\mt4\mql\bin\Dow Jones Breakout.mq4 ; 526:1 ; '\end_of_program' - unbalanced left parenthesis +2 ; 124 ; C:\z\F\Projects\mt4\mql\bin\Dow Jones Breakout.mq4 ; 146:12 ; 'HandleCommand' - function is not defined 2 ; 114 ; C:\z\F\Projects\mt4\mql\bin\output.txt ; 11:53 ; 'some text' - more than 1 symbol 1 ; 41 ; C:\z\F\Projects\mt4\mql\bin\metalang.exe ; 1:1 ; 'MZ???' - expression on global scope not allowed 2 ; 75 ; C:\z\F\Projects\mt4\mql\bin\metalang.exe ; 1:1 ; 'MZ???' - variable not defined 2 ; 76 ; ; ; cannot open the source file 2 ; 52 ; ; ; cannot open the output file - +1 ; 39 ; ; ; Function "Foo" is not referenced and will be removed from exp-file metaeditor.exe -------------- From fbece267d87c580a0926c1664923b52240024c11 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sun, 23 Nov 2025 12:44:34 +0200 Subject: [PATCH 45/48] convert some warnings to info --- bin/mqlc | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/bin/mqlc b/bin/mqlc index 32fd82bee..3e087a1db 100755 --- a/bin/mqlc +++ b/bin/mqlc @@ -262,8 +262,8 @@ function compareDirs() { # @param $3 - include directory # function compileMql40() { - local srcFile="$1" version="$2" include="$3" s_compiler s_srcFile - local compiler errorStatus=0 startms endms time='' output=() line errors=0 epl='' warnings=0 wpl='' retVal + local srcFile="$1" version="$2" include="$3" s_compiler s_srcFile logFile + local compiler errorStatus=0 startms endms time='' output=() line i errors=0 epl='' warnings=0 wpl='' retVal ((originalOutput)) || echo "Compiling \"$srcFile\" as $(versionName "$version")" @@ -271,6 +271,7 @@ function compileMql40() { setCompilerPath 'compiler' "$version" "$include" [[ "$compiler" == /* ]] && compiler="$(cygpath -w "$compiler")" [[ "$srcFile" == /* ]] && srcFile="$(cygpath -w "$srcFile")" + logFile="${srcFile%.*}.log" ((debug)) && { [[ "$compiler" == *\ * ]] && s_compiler="\"$compiler\"" || s_compiler="$compiler" [[ "$srcFile" == *\ * ]] && s_srcFile="\"$srcFile\"" || s_srcFile="$srcFile" @@ -291,10 +292,18 @@ function compileMql40() { [[ "${output[-1]}" =~ ^(Exp|Library exp)\ file\ .+\ produced\ -\ ([0-9]+)\ error.+\ ([0-9]+)\ warning.+ ]] || { fail "unexpected compiler output in last line:${NL}${output[-1]}"; } - errors="${BASH_REMATCH[2]}"; ((errors == 1)) && epl='' || epl='s' - warnings="${BASH_REMATCH[3]}"; ((warnings == 1)) && wpl='' || wpl='s' + errors="${BASH_REMATCH[2]}" + warnings="${BASH_REMATCH[3]}" if ((!originalOutput)); then + # reset some warnings + for ((i=3; i < ${#output[@]}; ++i)); do + [[ "${output[i]}" == '1;'* ]] && [[ "${output[i]}" == *'is not referenced and will be removed from exp-file' ]] && ((--warnings)) + done + + # add final results + ((errors == 1)) && epl='' || epl='s' + ((warnings == 1)) && wpl='' || wpl='s' time="$(duration "$startms" "$endms")" output[-1]="Result: $errors error$epl, $warnings warning$wpl, $time elapsed" ((!warnings)) && output+=('Success') @@ -310,9 +319,8 @@ function compileMql40() { ((++errors)) else # count errors and warnings - local i for ((i=3; i < ${#output[@]}; ++i)); do - [[ "${output[i]}" == '1;'* ]] && ((++warnings)) + [[ "${output[i]}" == '1;'* ]] && [[ "${output[i]}" != *'is not referenced and will be removed from exp-file' ]] && ((++warnings)) [[ "${output[i]}" == '2;'* ]] && ((++errors)) done fi @@ -349,6 +357,7 @@ function compileMql40() { printf '%s\n' "${output[@]}" ((warn2error)) || warnings=0 + ((!errors && !warnings)) && rm -f "$logFile" ((retVal = errors + warnings)) ((retVal > 255)) && retVal=255 return "$retVal" @@ -434,10 +443,10 @@ function compileMql5() { # # Reformat a single log message produced by the MQL4.0 compiler. # -# @param _InOut_ $1 - named reference to the log message +# @param _InOut_ $1 - reference to the log message # @param _In_ $2 - source file of the compilation -# @param _In_ $3 - number of errors during the compilation -# @param _In_ $4 - number of warnings during the compilation +# @param _In_ $3 - number of errors during compilation +# @param _In_ $4 - number of warnings during compilation # function formatCompiler40Msg() { ((BASH_SUBSHELL)) && fail "${FUNCNAME[0]}() can't write to outer scope from subshell" @@ -452,7 +461,11 @@ function formatCompiler40Msg() { message="${BASH_REMATCH[5]}" [[ "$type" == [12] ]] || fail "unexpected message type $type in compiler message:${NL}${logmsg}" - ((type == 1)) && level='warn' || level='error' + if ((type == 1)); then + [[ "$message" == *'is not referenced and will be removed from exp-file' ]] && level='info' || level='warn' + else + level='error' + fi ((errors)) && len=6 || len=5 levelS="$level: " levelS="${levelS:0:$len}" From b588c3f1d715cad2e0d09a2fa3f47250c98a547b Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sat, 29 Nov 2025 01:48:57 +0200 Subject: [PATCH 46/48] fix call of AddHistoryRecord() [skip ci] --- mql40/experts/ZigZag EA.mq4 | 2 +- mql40/experts/tools/HtmlReport2Chart.mq4 | 2 +- mql40/include/rsf/experts/trade/AddHistoryRecord.mqh | 2 +- templates4/17 ZigZag(50,30) + Donchian.tpl | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index d84b2603c..d1c1caa13 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -1188,7 +1188,7 @@ bool SynchronizeStatus() { double netProfitP = grossProfitP + MathDiv(swapM + commissionM, PointValue(lots)); logWarn("SynchronizeStatus(4) "+ instance.name +" orphaned closed position found: #"+ ticket +", adding to instance..."); - if (IsEmpty(AddHistoryRecord(ticket, 0, 0, lots, 1, openType, openTime, openPrice, openPrice, stopLoss, takeProfit, closeTime, closePrice, closePrice, slippageP, swapM, commissionM, grossProfitM, netProfitM, netProfitP, grossProfitP, grossProfitP, grossProfitP, grossProfitP, grossProfitP))) return(false); + if (IsEmpty(AddHistoryRecord(ticket, 0, 0, openType, lots, 1, openTime, openPrice, openPrice, stopLoss, takeProfit, closeTime, closePrice, closePrice, slippageP, swapM, commissionM, grossProfitM, netProfitM, netProfitP, grossProfitP, grossProfitP, grossProfitP, grossProfitP, grossProfitP))) return(false); // update closed PL numbers stats[NET_MONEY][S_CLOSED_PROFIT] += netProfitM; diff --git a/mql40/experts/tools/HtmlReport2Chart.mq4 b/mql40/experts/tools/HtmlReport2Chart.mq4 index a278bc12d..d76f7f24b 100644 --- a/mql40/experts/tools/HtmlReport2Chart.mq4 +++ b/mql40/experts/tools/HtmlReport2Chart.mq4 @@ -524,7 +524,7 @@ bool UpdateHistoryRecord(int ticket, double stopLoss, double takeProfit, datetim int size = ArrayRange(history, 0); // find the ticket to update - for (int i=size-1; i >= 0; i--) { // iterate from the end (in most use cases faster) + for (int i=size-1; i >= 0; i--) { // iterate from the end (in most cases faster) if (ticket == history[i][H_TICKET]) break; } if (i < 0) return(!catch("UpdateHistoryRecord(1) ticket #"+ ticket +" not found", ERR_INVALID_PARAMETER)); diff --git a/mql40/include/rsf/experts/trade/AddHistoryRecord.mqh b/mql40/include/rsf/experts/trade/AddHistoryRecord.mqh index c60edf61f..44880df25 100644 --- a/mql40/include/rsf/experts/trade/AddHistoryRecord.mqh +++ b/mql40/include/rsf/experts/trade/AddHistoryRecord.mqh @@ -36,7 +36,7 @@ int AddHistoryRecord(int ticket, int fromTicket, int toTicket, int type, double if (isPartial) { // resolve the partialClose[] index to insert at int size = ArrayRange(partialClose, 0); - for (int i=size-1; i >= 0; i--) { // iterate from the end (in most use cases faster) + for (int i=size-1; i >= 0; i--) { // iterate from the end (in most cases faster) if (ticket == partialClose[i][H_TICKET]) return(_EMPTY(catch("AddHistoryRecord(1) "+ instance.name +" cannot add record, ticket #"+ ticket +" already exists (partialClose["+ i +"])", ERR_INVALID_PARAMETER))); if (openTime > partialClose[i][H_OPENTIME]) { diff --git a/templates4/17 ZigZag(50,30) + Donchian.tpl b/templates4/17 ZigZag(50,30) + Donchian.tpl index e1275ccf4..998c124c9 100644 --- a/templates4/17 ZigZag(50,30) + Donchian.tpl +++ b/templates4/17 ZigZag(50,30) + Donchian.tpl @@ -179,8 +179,8 @@ window_num=1 Donchian.Periods=30 -levels_color=15453831 -levels_style=1 +levels_color=16316664 +levels_style=2 level_0=200 period_flags=0 From fe4b840eb72c3ed4c33643174e1c0c13b6b3ca3b Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Sat, 29 Nov 2025 02:15:32 +0200 Subject: [PATCH 47/48] stop the sequence after ERR_CONCURRENT_MODIFICATION [skip ci] --- mql40/experts/ZigZag EA.mq4 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mql40/experts/ZigZag EA.mq4 b/mql40/experts/ZigZag EA.mq4 index d1c1caa13..4441213aa 100644 --- a/mql40/experts/ZigZag EA.mq4 +++ b/mql40/experts/ZigZag EA.mq4 @@ -959,6 +959,9 @@ bool UpdateStatus() { int error; if (IsError(onPositionClose("UpdateStatus(2) "+ instance.name +" "+ ComposePositionCloseMsg(error), error))) return(false); if (!MovePositionToHistory(OrderCloseTime(), exitPrice, exitPriceSig)) return(false); + if (error == ERR_CONCURRENT_MODIFICATION) { + SendChartCommand("EA.command", "stop"); // asynchronously stop the sequence + } } // update PnL stats From 909f5dcddd29a263b19ae4269e37324ffec527d1 Mon Sep 17 00:00:00 2001 From: Peter Walther Date: Mon, 1 Dec 2025 13:00:48 +0200 Subject: [PATCH 48/48] adjust number format of ZigZag balance [skip ci] --- mql40/indicators/ZigZag.mq4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mql40/indicators/ZigZag.mq4 b/mql40/indicators/ZigZag.mq4 index ba5e6fa7a..01569d1f9 100644 --- a/mql40/indicators/ZigZag.mq4 +++ b/mql40/indicators/ZigZag.mq4 @@ -658,7 +658,7 @@ int onTick() { else fontColor = Red; string name = shortName + ifString(eventType==EVENT_REVERSAL_UP, ".up.", ".down.") + TimeToStr(eventTime); ObjectCreateRegister(name, OBJ_TEXT, 0, eventTime, eventPrice-markerOffset); - ObjectSetText(name, NumberToStr(balance/pUnit, ",'R."+ pDigits), fontSize, fontName, fontColor); + ObjectSetText(name, NumberToStr(balance/pUnit, ",'R.0"), fontSize, fontName, fontColor); // reset positive balances if (balance > -HalfPoint) balance = 0;