<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Arnold Holm</title>
    <description>The latest articles on DEV Community by Arnold Holm (@stratcorealpha).</description>
    <link>https://dev.to/stratcorealpha</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4078355%2Fc2ecba14-1ac4-43ef-b437-2a38e810c2ca.png</url>
      <title>DEV Community: Arnold Holm</title>
      <link>https://dev.to/stratcorealpha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stratcorealpha"/>
    <language>en</language>
    <item>
      <title>MT4 error 129: keep your signal level out of the market request</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Fri, 16 Oct 2026 14:00:21 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/mt4-error-129-keep-your-signal-level-out-of-the-market-request-2l96</link>
      <guid>https://dev.to/stratcorealpha/mt4-error-129-keep-your-signal-level-out-of-the-market-request-2l96</guid>
      <description>&lt;p&gt;Your strategy level and your market execution price need separate variables. My MT4 run passed 3 local price checks and accepted current Ask, while a deliberately fractional request failed. The returned error was 4107. I did not reproduce error 129.&lt;/p&gt;

&lt;p&gt;An old &lt;a href="https://www.mql5.com/en/forum/141771" rel="noopener noreferrer"&gt;MQL4 question&lt;/a&gt; shows the distinction well. A trader had Bid at 104.323 and Ask at 104.355, then tried a SELL at 104.326. The value had three decimal places. The code refreshed prices and allowed slippage. The remaining question was where that requested price came from: a pivot level used by the strategy.&lt;/p&gt;

&lt;p&gt;That is a useful debugging question even when your own error number differs. Find the assignment that produced the submitted value before changing the entry rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before: the trigger level travels into OrderSend
&lt;/h2&gt;

&lt;p&gt;This reduced example illustrates the mistake. It is not the original trader's complete code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;double requested = NormalizeDouble(triggerLevel, Digits);
int ticket = OrderSend(Symbol(), OP_SELL, lots,
                       requested, 10, 0, 0);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;triggerLevel&lt;/code&gt; may explain why the strategy wants to enter. It does not establish the current price for a market SELL. If the requirement is to enter only at a specified future price, define the appropriate pending-order behaviour separately.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.mql4.com/trading/ordersend" rel="noopener noreferrer"&gt;official OrderSend reference&lt;/a&gt; says market BUY requests use Ask and market SELL requests use Bid. It also distinguishes invalid prices, outdated prices and slippage handling. Adding a larger slippage value does not establish that your calculated level is a valid requested market price.&lt;/p&gt;

&lt;h2&gt;
  
  
  After: take a fresh quote and retain the actual result
&lt;/h2&gt;

&lt;p&gt;For the chart symbol, separate the signal decision from the request value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RefreshRates();
double requested = NormalizeDouble(Bid, Digits);
ResetLastError();
int ticket = OrderSend(Symbol(), OP_SELL, lots,
                       requested, 10, 0, 0);
int sendError = ticket &amp;lt; 0 ? GetLastError() : 0;
Print("price=", DoubleToString(requested, Digits),
      " ticket=", ticket, " error=", sendError);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a small request-path example, not a complete EA. A different traded symbol requires that symbol's quote and precision. Capture the values close to submission. A chart screenshot taken later cannot tell you what the earlier request contained.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the native test actually showed
&lt;/h2&gt;

&lt;p&gt;I used MT4 build 1491 and the existing EURUSD H4 Strategy Tester environment. The test EA refuses initialization outside the tester. The native compiler reported zero errors and warnings.&lt;/p&gt;

&lt;p&gt;The local price guard had three cases: reject zero, reject a fractional-point value and accept current Ask. All three passed. Those are local validation results, not invented trade-server responses.&lt;/p&gt;

&lt;p&gt;The first simulated BUY requested 1.2054370. It returned ticket -1 and error 4107. The next request used refreshed, normalized Ask at 1.2054. It opened simulated ticket 1 with error 0. Reading that ticket back confirmed the opening price, and simulated cleanup succeeded.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhhvvbrkik8if0itjoypd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhhvvbrkik8if0itjoypd.png" alt="Native MT4 tester: actual error 4107, accepted current Ask and three local checks" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The retained &lt;a href="https://stratcorealpha.com/proof/fix/FIX-14-runtime.txt" rel="noopener noreferrer"&gt;native log&lt;/a&gt; includes the actual error and the ticket-price comparison. The earlier attempt expecting 129 remains in the proof package. I changed the claim to match the result, rather than relabelling 4107 as the number I had hoped to reproduce.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your five-minute check
&lt;/h2&gt;

&lt;p&gt;Choose one failed request. Put its symbol, order type, submitted price, Bid, Ask, timestamp and immediately captured error on one line. Then trace the submitted price back to its assignment. Was it a quote, a stored quote or the level that triggered the strategy?&lt;/p&gt;

&lt;p&gt;Keep that trace when you compare the next request. Otherwise, a successful retry may hide which input actually changed. My stance is simple: preserve the failed request before making the next one look cleaner.&lt;/p&gt;

&lt;p&gt;This test sent zero account orders. It did not reproduce server error 129 or prove that a broker would accept the next request. The useful result is the separation between a signal level, a local check and an observed execution response.&lt;/p&gt;

&lt;p&gt;For a small source check, &lt;a href="https://stratcorealpha.com/developers?ref=devto" rel="noopener noreferrer"&gt;Free EA Code Check&lt;/a&gt; starts with the readable code and one expected result.&lt;/p&gt;

</description>
      <category>mql4</category>
      <category>debugging</category>
      <category>algotrading</category>
      <category>trading</category>
    </item>
    <item>
      <title>MT5 invalid price: two decimal places can still miss the tick</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Thu, 15 Oct 2026 13:59:28 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/mt5-invalid-price-two-decimal-places-can-still-miss-the-tick-2o0b</link>
      <guid>https://dev.to/stratcorealpha/mt5-invalid-price-two-decimal-places-can-still-miss-the-tick-2o0b</guid>
      <description>&lt;p&gt;Your price can keep 2 decimals and still miss the allowed tick. My native MT5 check rejected 100.13 on a chosen 0.25 grid. Rounding by the tick produced 100.25, which passed the same check.&lt;/p&gt;

&lt;p&gt;I check the price grid before widening deviation or changing the entry signal.&lt;/p&gt;

&lt;p&gt;The question came from an &lt;a href="https://www.mql5.com/en/forum/259922" rel="noopener noreferrer"&gt;MT5 developer whose XAUUSD position closes returned 10015&lt;/a&gt;. Increasing deviation had not helped. A reply pointed to a mismatch between the symbol's tick size and prices returned in that old tester case. That thread concerns older terminal builds. My check below isolates the arithmetic, rather than diagnosing that person's broker today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistake I put under test
&lt;/h2&gt;

&lt;p&gt;The assumption is simple: if a symbol displays two decimals, rounding to two decimals makes a price valid.&lt;/p&gt;

&lt;p&gt;That fails when the allowed price step is larger than the displayed decimal unit. With a tick size of 0.25, 100.00 and 100.25 sit on the grid. 100.13 does not. Keeping two decimals leaves 100.13 unchanged.&lt;/p&gt;

&lt;p&gt;Here is the incomplete version from my example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;100.13&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NormalizeDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Still 100.13. Decimal rounding did not check the 0.25 grid.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The number of displayed decimals and the minimum price change are separate &lt;a href="https://www.mql5.com/en/docs/constants/environment_state/marketinfoconstants" rel="noopener noreferrer"&gt;symbol properties in MQL5&lt;/a&gt;. I read the actual symbol settings rather than infer its step from its appearance on a chart.&lt;/p&gt;

&lt;h2&gt;
  
  
  The change and the three results
&lt;/h2&gt;

&lt;p&gt;For this isolated example I chose the values explicitly. That makes the failure easy to repeat without waiting for a particular instrument or quote.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;OnGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;tick&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="n"&gt;MathAbs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;tick&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;MathRound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.000001&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NormalizeDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;100.13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;tick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;rounded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NormalizeDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MathRound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// OnGrid(raw, tick) is false. OnGrid(rounded, tick) is true.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I compiled and ran this check in native MT5. The compiler reported zero errors. The output was:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Expected&lt;/th&gt;
&lt;th&gt;Actual&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Zero price&lt;/td&gt;
&lt;td&gt;Reject&lt;/td&gt;
&lt;td&gt;Rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100.13, tick 0.25&lt;/td&gt;
&lt;td&gt;Off grid&lt;/td&gt;
&lt;td&gt;Off grid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100.25, tick 0.25&lt;/td&gt;
&lt;td&gt;On grid&lt;/td&gt;
&lt;td&gt;On grid&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Nearest rounding is a demonstration here, not a universal order rule. Rounding a buy limit upward can change its intended distance or put it on the wrong side of the market. Choose floor, ceiling or nearest rounding for the specific order rule, then check its relationship to Bid or Ask again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F52dzispo9ay5hr5rn6bq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F52dzispo9ay5hr5rn6bq.png" alt="Native MT5 price-grid run: zero rejected, 100.13 off grid, 100.25 on grid. Chosen 0.25 tick, zero orders." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the same mistake in five minutes
&lt;/h2&gt;

&lt;p&gt;Open one rejected request in your MT5 log. Write down the requested price, symbol tick size, Bid, Ask and order type from that request.&lt;/p&gt;

&lt;p&gt;Divide the requested price by the tick size. If it is far from a whole number, inspect where the price was formed. Check the entry, stop and target separately. A correctly rounded entry does not repair a target calculated on another grid.&lt;/p&gt;

&lt;p&gt;Keep the timestamp with those values. A fresh quote read later cannot explain what the program submitted earlier. Once the grid is correct, check the order side and required distances separately. Passing this arithmetic check does not settle those questions.&lt;/p&gt;

&lt;p&gt;The three cases used chosen inputs and sent zero orders. They did not reproduce server return code 10015 or establish broker acceptance. They show exactly why decimal rounding alone missed one invalid price in my code. I would keep this small check beside the order-building code so a later change cannot quietly bring the same mistake back.&lt;/p&gt;

&lt;p&gt;If you have a small trading-code fault, the &lt;a href="https://stratcorealpha.com/developers?ref=devto" rel="noopener noreferrer"&gt;free bug check&lt;/a&gt; starts with the expected result, actual result and the smallest source excerpt you are allowed to share.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>programming</category>
      <category>trading</category>
      <category>mql5</category>
    </item>
    <item>
      <title>MT4 error 146: why a deferred signal needs a fresh quote</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Wed, 14 Oct 2026 07:47:21 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/mt4-error-146-why-a-deferred-signal-needs-a-fresh-quote-3h93</link>
      <guid>https://dev.to/stratcorealpha/mt4-error-146-why-a-deferred-signal-needs-a-fresh-quote-3h93</guid>
      <description>&lt;p&gt;Your entry can wait: my MT4 test made 0 requests during 1 synthetic busy cycle and accepted a fresh-price simulated retry. I keep the signal pending while the terminal is busy, then check whether the entry still makes sense before sending it. That second check matters as much as waiting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fks3o05wz3inhmdis7yqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fks3o05wz3inhmdis7yqs.png" alt="Native MT4 tester: deferred busy input and fresh-price simulated BUY" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Waiting is a state change
&lt;/h2&gt;

&lt;p&gt;A trader asked, “Is it a problem of broker , my internet connection or EA ?” in an &lt;a href="https://www.mql5.com/en/forum/177910" rel="noopener noreferrer"&gt;MT4 discussion&lt;/a&gt;. Error 146 points to a busy trade context. It can tempt you into a small loop: try, wait, try the same request again.&lt;/p&gt;

&lt;p&gt;But the request is getting older while it waits. The quote may move. The candle that produced the signal may no longer be the candle your rule permits. A pending order might already exist. A retry has to answer those questions before it sends anything.&lt;/p&gt;

&lt;p&gt;I wanted a small check where the waiting decision and the later request were visible separately. I used my tester-only program in native MT4 build 1491, on EURUSD H4 history for 3 May 2021. The fixed tester spread was 20 points.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the test actually did
&lt;/h2&gt;

&lt;p&gt;The first step supplied a synthetic busy input. The program deferred that cycle and logged zero requests. It also checked that a busy input, a pending input and a stale quote blocked the relevant helper paths.&lt;/p&gt;

&lt;p&gt;The native busy-context reading was false. With the later fresh-quote gate allowed, the program refreshed the prices. Ask was 1.2054 and Bid was 1.2034. The simulated BUY used Ask, returned ticket 1 and reported error 0.&lt;/p&gt;

&lt;p&gt;The program selected that simulated ticket and checked its order type was BUY. It then closed the simulated position. The screenshot and Journal show the deferred step, the gates, the fresh-price request and the cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two small gates
&lt;/h2&gt;

&lt;p&gt;These are the exact helpers from that test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool CanSendNow(bool nativeBusy,bool syntheticPending){ return(!nativeBusy &amp;amp;&amp;amp; !syntheticPending); }
bool CanRetryAfter(bool nativeBusyNow,bool quoteFresh){ return(!nativeBusyNow &amp;amp;&amp;amp; quoteFresh); }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The helpers answer narrow questions. They do not store a signal, coordinate several EAs or prove the request was accepted. The second helper only says the native busy reading is clear and the supplied quote-freshness check passes.&lt;/p&gt;

&lt;p&gt;I would keep the actual OrderSend result separate from that permission. A terminal-state check is not a reservation of the trading context. Something can change between the check and the attempted send. Save the real result immediately, then decide whether the signal remains eligible for a later attempt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the original intent, refresh the market facts
&lt;/h2&gt;

&lt;p&gt;The entry rule, side and signal identifier should remain attached to the deferred intent. Refreshing a quote must not quietly turn that intent into a different trade. Recompute the prices and protections required by the agreed rule, then compare the result with the signal's permitted age and conditions.&lt;/p&gt;

&lt;p&gt;I would also distinguish an explicit rejection from an uncertain outcome. If you do not know whether the previous attempt created an order, inspect the terminal's orders and history before another send. Otherwise a network problem can become a duplicate-position problem.&lt;/p&gt;

&lt;p&gt;Keep retries bounded. A loop that keeps asking until something succeeds can execute a signal long after the reason for entering has disappeared. Record why the request waited, when it became eligible again, and why the signal was kept or discarded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your five-minute check
&lt;/h2&gt;

&lt;p&gt;Open one busy-context failure in your Journal and compare the request times from every EA on that terminal with the next retry and its quote time. Find the original signal time too. Does the retry log show a fresh quote, the original signal identity and a current decision to proceed?&lt;/p&gt;

&lt;p&gt;If your bot loses that trail, the &lt;a href="https://stratcorealpha.com/developers?ref=devto" rel="noopener noreferrer"&gt;free Bug Check&lt;/a&gt; starts with the relevant source branch, one failure and the expected result.&lt;/p&gt;

&lt;p&gt;This offline tester case used synthetic busy inputs and one simulated BUY, with zero account orders. It did not reproduce a real 146 rejection, concurrent EAs or current broker execution.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>programming</category>
      <category>mql4</category>
      <category>algotrading</category>
    </item>
    <item>
      <title>Your Python trading bot restarted. Why did it send twice?</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Tue, 13 Oct 2026 07:45:01 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/your-python-trading-bot-restarted-why-did-it-send-twice-2eoe</link>
      <guid>https://dev.to/stratcorealpha/your-python-trading-bot-restarted-why-did-it-send-twice-2eoe</guid>
      <description>&lt;p&gt;Your bot can repeat a trade after a restart even when the first request succeeded. My offline restart check created 1 order intent, then 0 when the same signal came back. The useful part was the second result: the code held the signal for reconciliation instead of preparing another buy.&lt;/p&gt;

&lt;p&gt;The starting question was a real stock-platform brief: finish the existing software so it can execute trades reliably. Before adding another strategy rule, I would look at the gap between sending a request and saving its result. That gap can survive a perfectly good backtest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0xi15u8139y0sjd1d60h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0xi15u8139y0sjd1d60h.png" alt="Four actual offline stock order and restart checks, all passed" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The missing record tells you less than you think
&lt;/h2&gt;

&lt;p&gt;Imagine your program sends an order. The broker accepts it, but the connection drops before your program receives the answer. Or the answer arrives and the process stops before the local write finishes. After restart, your database contains no completed order. That does not tell you whether the broker accepted it.&lt;/p&gt;

&lt;p&gt;A retry can now produce another trade. The dangerous assumption is that no local success record means no external action. A manual position and an automated order whose answer was lost also need different treatment. I would keep an unknown state separate from both, with a clear route to investigate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save the chosen identity first
&lt;/h2&gt;

&lt;p&gt;My example uses SQLite and a stable signal ID. It saves the ID before returning an order intent. The ID is the table's primary key, so the database rejects a second reservation even after the process closes and reopens.&lt;/p&gt;

&lt;p&gt;Here is the actual preparation method from the checked example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AAPL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MSFT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rejected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO orders VALUES (?,?,?,?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pending_reconciliation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntegrityError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;held_for_reconciliation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prepared&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;side&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;offline_only&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The surrounding class creates the table with &lt;code&gt;id TEXT PRIMARY KEY&lt;/code&gt; and commits that setup. It validates the selected symbol and positive integer quantity before reserving an ID. An invalid request therefore does not consume an identity that a corrected request still needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the four checks showed
&lt;/h2&gt;

&lt;p&gt;The first check prepared an AAPL buy intent. The second closed the database connection, reopened the same file and presented the same signal again. It returned &lt;code&gt;held_for_reconciliation&lt;/code&gt; and no intent. The third rejected a zero quantity. The fourth corrected that rejected request and prepared it successfully. All four checks passed.&lt;/p&gt;

&lt;p&gt;That last pair matters. Reserving an ID before checking the quantity can leave a valid correction blocked by a request that never should have reached the send step. Validation belongs before the reservation. The reservation belongs before an external request can leave the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  A database hold still needs a recovery path
&lt;/h2&gt;

&lt;p&gt;This small example deliberately stops at preparation. A real broker integration needs to query the original client order ID, order status and fills. It also needs a policy for pending signals that remain unresolved. Generating a new random ID after each timeout would defeat the duplicate hold even though the database still works correctly.&lt;/p&gt;

&lt;p&gt;I would never turn an unknown acknowledgement into an automatic resend. I would first recover what happened to the original order. If the broker gives a definitive rejection, the next permitted state can follow that result. If the answer remains uncertain, the hold needs to remain visible rather than disappear when the process restarts.&lt;/p&gt;

&lt;p&gt;In five minutes, compare the signal ID before a restart with the ID used afterwards, then check whether a repeated pending ID creates another order intent. Use your program's offline path and inspect the actual returned state. A fresh ID on every retry is a reason to investigate before connecting that retry to an account.&lt;/p&gt;

&lt;p&gt;If your trading bot has a similar state problem, the &lt;a href="https://stratcorealpha.com/developers?ref=devto" rel="noopener noreferrer"&gt;free bug check&lt;/a&gt; starts with the relevant source and the expected result.&lt;/p&gt;

&lt;p&gt;These four local checks cover chosen inputs and SQLite persistence, without broker submission, fills or a live account.&lt;/p&gt;

</description>
      <category>python</category>
      <category>algotrading</category>
    </item>
    <item>
      <title>Why does a smaller MT4 lot size still return error 131?</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Sun, 11 Oct 2026 17:19:15 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/why-does-a-smaller-mt4-lot-size-still-return-error-131-4ok1</link>
      <guid>https://dev.to/stratcorealpha/why-does-a-smaller-mt4-lot-size-still-return-error-131-4ok1</guid>
      <description>&lt;p&gt;Your risk calculation can produce a smaller lot size and still produce an invalid order. In my native MT4 tester case, 0.05 lot returned error 131 while the symbol's minimum and step were both 0.10. The corrected 0.10 request opened a simulated order. I check the symbol's permitted volume before looking for a fault in the entry signal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcmyh7hhjanrdhoz66zfq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcmyh7hhjanrdhoz66zfq.png" alt="Native MT4 tester: 0.05 rejected with 131, 0.10 accepted with error 0" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The size was small, but the request was invalid
&lt;/h2&gt;

&lt;p&gt;It is tempting to read an order error as a strategy problem. The signal fired, the lot calculation ran, and the EA reached OrderSend. Yet there was no position. Lowering the lot size seems like a cautious first correction.&lt;/p&gt;

&lt;p&gt;That correction can move in exactly the wrong direction. A symbol has a minimum volume as well as a volume step. A number below that minimum is invalid even when it is smaller than the size you originally intended to trade. Decimal formatting alone does not change that restriction.&lt;/p&gt;

&lt;p&gt;The case used EURUSD in MT4's Strategy Tester. Its default testing environment reported a minimum lot and step of 0.10. I submitted half the minimum first, then submitted the minimum itself. The first call returned ticket -1 and error 131. The second returned ticket 1 and error 0. The selected order's recorded volume matched the requested 0.10 lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual test calls
&lt;/h2&gt;

&lt;p&gt;These are the calls from the tester-only EA. The surrounding EA rejects startup outside the Strategy Tester. Both calls below are simulated requests, and the successful order is closed during cleanup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;double lots=MarketInfo(Symbol(),MODE_MINLOT);
double step=MarketInfo(Symbol(),MODE_LOTSTEP);
ResetLastError();
int bad=OrderSend(Symbol(),OP_BUY,lots/2.0,Ask,10,0,0,
                  "FIX07 below minimum volume",607,0,clrNONE);
int badError=GetLastError();
ResetLastError();
int good=OrderSend(Symbol(),OP_BUY,lots,Ask,10,0,0,
                   "FIX07 corrected min volume",607,0,clrNONE);
int goodError=GetLastError();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The useful comparison is the two requests beside the symbol properties and the saved errors. A green signal marker does not explain an invalid volume. A zero error from a later unrelated function does not explain it either. Save the error immediately after the failed call, before another operation changes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your own EA in five minutes
&lt;/h2&gt;

&lt;p&gt;Find the volume that reaches OrderSend, rather than the intermediate risk number printed earlier in the calculation. Put that final value beside MODE_MINLOT, MODE_MAXLOT and MODE_LOTSTEP for the exact symbol used by the request. Check both the minimum and the permitted increments.&lt;/p&gt;

&lt;p&gt;Then inspect how your code handles a calculated volume below the minimum. Automatically increasing it to the minimum may increase the money at risk beyond the budget that produced the original number. I would skip the entry and print the reason when the smallest valid size exceeds that budget. A technically accepted order is only useful when its size also respects the intended risk.&lt;/p&gt;

&lt;p&gt;Keep the before-and-after request details in the same short log. If the volume already satisfies the symbol's minimum, maximum and step, the next useful evidence is the exact rejected request and the saved terminal error. Repeatedly reducing the number without that comparison leaves you guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the result establishes
&lt;/h2&gt;

&lt;p&gt;This case demonstrates error 131 and its corrected simulated request in that native tester environment. It used default symbol settings without a trade-server connection; it sent no live or demo account orders. Your broker's symbol properties can differ.&lt;/p&gt;

&lt;p&gt;If your trading bot still rejects a request, my &lt;a href="https://stratcorealpha.com/developers?ref=devto" rel="noopener noreferrer"&gt;free bug check&lt;/a&gt; starts with one small reproducible problem and its log.&lt;/p&gt;

</description>
      <category>trading</category>
    </item>
    <item>
      <title>$176.82 overlooked: when your daily reset starts from the wrong value</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Sat, 10 Oct 2026 07:57:05 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/17682-overlooked-when-your-daily-reset-starts-from-the-wrong-value-2o1o</link>
      <guid>https://dev.to/stratcorealpha/17682-overlooked-when-your-daily-reset-starts-from-the-wrong-value-2o1o</guid>
      <description>&lt;p&gt;Your daily reset can make an open loss vanish from the limit calculation while the position still loses money. Our saved EURUSD case carried a $176.82 loss after midnight. Under its frozen $500 daily loss limit, the correct floor was $10,256.64. Starting from account value including that loss would instead show $10,079.82. That lower floor overlooks money already lost. Check which value your program resets from.&lt;/p&gt;

&lt;h2&gt;
  
  
  One open position, two possible starting values
&lt;/h2&gt;

&lt;p&gt;A trader's question was whether a new day erases an existing open loss. The useful software question follows directly: what does your program store when its day changes? A variable named start balance can hold closed balance, or it can accidentally hold account value including the open trade. The name alone cannot tell you which happened.&lt;/p&gt;

&lt;p&gt;Our saved case used an EURUSD position still open across midnight Berlin on 19 May 2025. The closed balance was $10,756.64 before and after. Account value including that trade was $10,529.82 before midnight and $10,579.82 just after. The open loss became smaller between those readings, but the trade remained open and losing.&lt;/p&gt;

&lt;p&gt;These are saved values from our own simulation under its stated costs. They let us compare the two starting values at the same reset, without changing the trade, adding a deposit or assuming that the market closed at midnight.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistake is hidden in the stored starting value
&lt;/h2&gt;

&lt;p&gt;The frozen rule took its daily allowance from day-start balance. Its $500 allowance therefore left a $10,256.64 floor. The selected reading after midnight had $323.18 above that floor. There was no daily breach at this instant. The position's $176.82 loss was still part of the account value being checked.&lt;/p&gt;

&lt;p&gt;Now compare the mistaken starting value. If the reset stores $10,579.82 instead of the $10,756.64 closed balance, its floor becomes $10,079.82. The saved comparison shows the difference: the lower starting value makes the remaining loss room look larger by the open loss already on the position.&lt;/p&gt;

&lt;p&gt;That is why a daily counter returning to zero is a weak visual check. It can look tidy while the wrong amount feeds the new day's limit. Put the stored starting value, actual balance, open trade result and resulting floor beside one another. A reset needs the right value as well as the right clock.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the price movement separate from the reset
&lt;/h2&gt;

&lt;p&gt;Our chart shows the actual saved account path and daily floor around the reset in Berlin time. The before and after readings have different account values because the price changes. Treating that improvement as a gift from midnight would confuse the market move with the bookkeeping.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvb5wswv999pcn5uq7ng0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvb5wswv999pcn5uq7ng0.png" alt="Saved EURUSD account value and daily floor across midnight Berlin" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use an existing open position when you check this in your own report. An empty account hides the difference because its balance and account value can agree. A loss on the open position exposes which one your reset actually uses. Keep fees and overnight charges inside the value required by the rule, rather than subtracting costs a second time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The saved case has a clear boundary
&lt;/h2&gt;

&lt;p&gt;This was a historical EURUSD simulation with generated price steps from one-minute candle opens, highs, lows and closes. Its selected summer clock mapping placed the server one hour ahead of Berlin. The full run had already breached a daily limit on 6 May; the later midnight example comes from a continued explanatory run. It explains the starting-value error, rather than showing a strategy that passed a challenge.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://stratcorealpha.com/engineering-notes/carried-loss-midnight" rel="noopener noreferrer"&gt;full carried-loss case&lt;/a&gt; keeps the saved amounts, graph and public question together. Before paying for a challenge, compare your own rule with the value your program stores at the reset. Send your rule sheet or trading program for the &lt;a href="https://stratcorealpha.com/challenge-check?ref=devto" rel="noopener noreferrer"&gt;free Challenge Pre-Check&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>backtesting</category>
      <category>trading</category>
    </item>
    <item>
      <title>0.47% a year: why your challenge target needs a different test</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:52:55 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/047-a-year-why-your-challenge-target-needs-a-different-test-3f4l</link>
      <guid>https://dev.to/stratcorealpha/047-a-year-why-your-challenge-target-needs-a-different-test-3f4l</guid>
      <description>&lt;p&gt;Your positive backtest can still miss the job you need it to do. Our saved pre-Fed currency test returned 0.47% a year after costs. On a $10,000 reference, that annual rate represents about $47.15. That tells you something about the historical return. It does not tell you whether your account can reach its challenge target before a loss rule stops it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result that looked useful
&lt;/h2&gt;

&lt;p&gt;The idea was simple. Hold a basket of currencies against the US dollar during the 24 hours before a scheduled Federal Reserve rate announcement. Close at the announcement. Keep the same rule across the whole test.&lt;/p&gt;

&lt;p&gt;Our historical simulation used eleven currency pairs from 2017 through 2023. The total position value was half the reference account size, with a 1.99% stop on each position. Its maximum initial risk was 0.995%. Both sides of the transaction cost were included.&lt;/p&gt;

&lt;p&gt;The result was positive in the development period and in both later validation periods. Doubling costs, adding another 0.01 percentage point of cost, and removing the stop still left both validation periods positive.&lt;/p&gt;

&lt;p&gt;Those are useful checks. They help separate a result that survives small changes from one that depends on a lucky setting. But I still rejected this case for the funded strategy we were testing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F219o46162uu4eay4236g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F219o46162uu4eay4236g.png" alt="The saved 2017-2023 pre-Fed currency simulation, with its small positive account-value curve." width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A small positive rate is still small
&lt;/h2&gt;

&lt;p&gt;The combined annual rate was 0.47%. The largest drop from a previous peak was about 0.40% in this saved run. That can make the chart feel comfortable to read. It also makes it easy to forget the size of the profit required.&lt;/p&gt;

&lt;p&gt;Take an example $10,000 challenge with a $1,000 profit target and a $500 daily loss allowance. Those are example rules to put beside the result, not rules attributed to a particular firm. The annual-rate figure of $47.15 is a geometric average expressed on that same reference amount. It is not a statement that each year earned exactly $47.15.&lt;/p&gt;

&lt;p&gt;Before deciding this chart fits your challenge, you need a different view: the actual sequence of opportunities, trades, open losses and target checks. A smooth annual summary cannot answer how long the next opportunity takes to arrive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The calendar matters
&lt;/h2&gt;

&lt;p&gt;This rule trades around scheduled announcements. It does not produce a new setup every morning. A quiet stretch in the account curve can be a period with no eligible event, rather than a fault in the program.&lt;/p&gt;

&lt;p&gt;That distinction changes what you ask of a trading bot. If your program is meant to act only before the next scheduled event, opening a position today would change the rule. Adding other trades to speed up progress would also change the test.&lt;/p&gt;

&lt;p&gt;I would put the eligible event times next to the challenge's deadline, minimum trading days and reset time. Then I would replay the account from the intended start date. The question is whether that sequence fits the rules you actually bought, including any news restriction at the firm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bigger positions need another test
&lt;/h2&gt;

&lt;p&gt;The obvious response is to increase the position size. But the small loss shown here belongs to the exposure and stop settings used in this run.&lt;/p&gt;

&lt;p&gt;Increasing size changes the loss at the stop, the margin needed, and the room left beneath a daily loss limit. It may also change which orders can be filled. The old chart cannot stand in as evidence for the larger version.&lt;/p&gt;

&lt;p&gt;In our research, the 0.47% result fell below the return threshold set before the test. There were also failures in a comparison with an unrelated calendar window and in the concentration of positive results across pairs. Positive validation results did not erase those failures. The final decision stayed rejected.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can check today
&lt;/h2&gt;

&lt;p&gt;Write down your account size, profit target, daily loss rule, overall loss rule and deadline. Keep the firm's definition of account value and its reset time beside them. Then ask whether your report contains the full account path needed to check each rule.&lt;/p&gt;

&lt;p&gt;For an event strategy, include the days when there is no trade. For a bot with open positions, include their unrealised loss. Test the proposed position size itself. Do not borrow the loss numbers from a smaller version.&lt;/p&gt;

&lt;p&gt;If you want to start with your own rules, the &lt;a href="https://stratcorealpha.com/challenge-check?ref=devto" rel="noopener noreferrer"&gt;free Challenge Pre-Check&lt;/a&gt; is the next step.&lt;/p&gt;

&lt;p&gt;This case is a saved historical simulation, with no customer account, live orders or completed challenge behind its result.&lt;/p&gt;

</description>
      <category>trading</category>
      <category>forex</category>
      <category>algotrading</category>
      <category>testing</category>
    </item>
    <item>
      <title>Your $10,756.64 backtest needs the $10,019.82 open-trade row</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Thu, 08 Oct 2026 08:22:09 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/your-1075664-backtest-needs-the-1001982-open-trade-row-4pc3</link>
      <guid>https://dev.to/stratcorealpha/your-1075664-backtest-needs-the-1001982-open-trade-row-4pc3</guid>
      <description>&lt;p&gt;Your backtest can show a closed balance of $10,756.64 while the account value including an open trade is $10,019.82. In my saved EURUSD run, the daily floor was $10,256.64. The closed balance stayed above it. The open trade took equity below it. When you inspect a challenge report, keep the row from inside the trade beside the row from its close.&lt;/p&gt;

&lt;h2&gt;
  
  
  The row a closed-trade list leaves out
&lt;/h2&gt;

&lt;p&gt;A trader asked for a free way to replay historical gold candles and record entries and exits. That raises a useful report question: what happened to the account while the trade stayed open? It is an easy detail to miss when your report lists only entries, exits and the final balance. Those rows tell you what the trade eventually did. They may leave out the lowest account value on the way there.&lt;/p&gt;

&lt;p&gt;Equity is the account value including the current result of open trades. Balance is the value after closed trades and account charges. A daily rule that uses equity can be crossed before the position closes. The later result cannot remove that earlier crossing from your history.&lt;/p&gt;

&lt;p&gt;I kept both lines in the EURUSD run because that question needed a time inside the trade. A list of winning and losing closes would not answer it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened in the saved run
&lt;/h2&gt;

&lt;p&gt;On 19 May 2025, the saved day-start balance was $10,756.64. The daily allowance in this example was $500, giving a floor of $10,256.64. At the selected moment, one position remained open. Closed balance was still $10,756.64, but equity had fallen to $10,019.82.&lt;/p&gt;

&lt;p&gt;The saved account values already include the commissions and swaps charged by the tester. I used those account values directly. Taking the charges off again would have changed the question and counted the same cost twice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqtugrr6owg0vyn7sjbb1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqtugrr6owg0vyn7sjbb1.png" alt="Native EURUSD tester account rows showing balance, equity and the daily floor" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The graph comes from the saved tester rows. The closed balance line and the equity line share a money scale with the daily floor. Find the point where equity crosses the floor, then look at the balance at that same time. Comparing them at different times can hide the reason the daily rule was crossed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep one short note beside each trade
&lt;/h2&gt;

&lt;p&gt;You do not need to turn your journal into a large software project. Start by saving the time of the deepest open loss, the balance at that time, the equity at that time and the daily floor used for that day. Keep the eventual close as a separate result.&lt;/p&gt;

&lt;p&gt;Then check your report's meaning. Does its drawdown figure follow closed balance or account equity? Does it inspect the price path while a position is open, or only sample the account at a candle close? Write the answer beside the report. A familiar label can cover a different measurement.&lt;/p&gt;

&lt;p&gt;Keep the daily clock there too. Your chart's next candle and your firm's new day can occur at different times. Use the reset time in your own account rules when assigning a saved row to a day. For this run, I kept an empirically aligned server clock with the saved account rows.&lt;/p&gt;

&lt;p&gt;Finally, keep the costs beside the account figures. Record the spread treatment, commission and swap used by your software. If those charges already appear in its account values, use that treatment consistently when comparing the values with the loss limit.&lt;/p&gt;

&lt;p&gt;This was a historical simulation with ticks generated from one-minute candles, an empirically aligned clock and a continued example after the first breach on 6 May; it does not represent a continuing valid challenge account or predict a future result.&lt;/p&gt;

&lt;p&gt;For your own report, the &lt;a href="https://stratcorealpha.com/challenge-check?ref=devto-open-trade" rel="noopener noreferrer"&gt;free Challenge Pre-Check&lt;/a&gt; starts with your platform, account size and daily rule. Keep the open-trade row ready so the check can answer the question your final balance leaves open.&lt;/p&gt;

</description>
      <category>trading</category>
      <category>beginners</category>
      <category>testing</category>
    </item>
    <item>
      <title>MT4 error 130: check the stop's side before its distance</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Tue, 06 Oct 2026 20:09:52 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/mt4-error-130-check-the-stops-side-before-its-distance-1kf4</link>
      <guid>https://dev.to/stratcorealpha/mt4-error-130-check-the-stops-side-before-its-distance-1kf4</guid>
      <description>&lt;p&gt;If your EA keeps returning error 130, check which side of the price holds the stop before making it wider. In my native MT4 tester, a BUY stop at 1.2154 was rejected with error 130. The corrected stop at 1.1926 was accepted with error 0. The important change was where the stop belonged, not a new entry rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmjirtr5fa8imoo8i8fku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmjirtr5fa8imoo8i8fku.png" alt="Native MT4 tester: wrong-side BUY stop rejected with 130, corrected stop accepted with 0, no account order" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The price your EA should compare
&lt;/h2&gt;

&lt;p&gt;A BUY opens at Ask, but its protective stop is checked against Bid. That can be easy to miss when your code uses the opening price for both jobs. In this saved EURUSD case, Bid was 1.2034 and Ask was 1.2054. The requested stop, 1.2154, was above both. Giving a BUY a protective exit above the current Bid did not describe the loss exit the program intended.&lt;/p&gt;

&lt;p&gt;The tester rejected that request. The log records a ticket of -1 and error 130. I then changed the stop to 1.1926, below Bid. The corrected simulated BUY returned ticket 1 and error 0. I also read back the stop stored on that simulated order. It matched 1.1926. That last check matters: a clean return alone would leave open whether the program had submitted the price we meant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write down the request before changing it
&lt;/h2&gt;

&lt;p&gt;When your own EA shows this error, save the symbol, order direction, requested opening price, stop, target, Bid and Ask beside the error. Keep the point size, tick size and stop-distance setting too. Otherwise a code change can appear to fix the error while quietly changing the rule you wanted to trade.&lt;/p&gt;

&lt;p&gt;My saved case used a point size of 0.0001 and a stop-distance setting of 8 points. Those are properties of this particular offline tester environment. Copying its stop price into a different symbol would make little sense. Your EA needs to read the properties for the symbol named in its request, then compare prices in the right units.&lt;/p&gt;

&lt;p&gt;I would first check direction. For a BUY, the protective stop belongs below Bid. For a SELL, it belongs above Ask. Then check the required distance and the permitted price increments. A correct-looking decimal can still sit between permitted prices. Increasing the distance before checking direction can hide the original mistake rather than explain it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the same intended trade
&lt;/h2&gt;

&lt;p&gt;Do not change the entry rule, direction, position size and stop calculation together. You would no longer know which edit changed the result. In this case, I kept the BUY request and showed the rejected stop beside the corrected stop. The log then checked that the accepted simulated order stored the intended price.&lt;/p&gt;

&lt;p&gt;I also closed the simulated order before the case ended. No account order was sent. This is useful when investigating an EA problem: the smallest safe case can show the price mistake without turning a debugging session into an unintended trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can check today
&lt;/h2&gt;

&lt;p&gt;Find the request immediately before error 130 in your log. Put its stop beside the Bid or Ask used for that direction. Ask whether the stop describes the protective exit your written rule actually calls for. Then check distance and price increments for that symbol. Keep the rejected and corrected requests together so that a later edit cannot erase what happened.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://stratcorealpha.com/fix/mt4-error-130-invalid-stops?ref=symb-ws4-devto" rel="noopener noreferrer"&gt;full case, source pattern and official price-distance references&lt;/a&gt; are on the canonical page. This run used an offline MT4 Strategy Tester with default settings; it does not establish acceptance by your broker or a profitable strategy.&lt;/p&gt;

</description>
      <category>trading</category>
      <category>mql4</category>
    </item>
    <item>
      <title>TradingView Strategy Tester No Data: Trace the Missing Stage</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Mon, 05 Oct 2026 14:14:51 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/tradingview-strategy-tester-no-data-trace-the-missing-stage-3oll</link>
      <guid>https://dev.to/stratcorealpha/tradingview-strategy-tester-no-data-trace-the-missing-stage-3oll</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm3dbkc9dbqlt6mm0xy0b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm3dbkc9dbqlt6mm0xy0b.png" alt="Bars, condition, order branch, entry and completed exit. Diagnostic model, not a recorded run." width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A trader adds a strategy to a chart, sees “No data” in the Strategy Tester and asks whether the idea failed. That screen does not yet answer the question. It can mean that the selected setup has not produced a completed-trade sample. Before changing an indicator or comparing percentages, make the missing stage visible.&lt;/p&gt;

&lt;p&gt;TradingView's own support article lists several possible causes: the script may not be a strategy, it may not issue order commands, its conditions may never occur in the selected history, it may lack the capital required for an order, or it may have a runtime error. These are different failures. One screenshot of an empty report cannot choose between them.&lt;/p&gt;

&lt;p&gt;My useful starting point is a small evidence chain. A bar is available. The rule evaluates that bar. An order command is reached. The simulator records an entry. An exit completes a trade. A result belongs at the end of that chain. A debug count or marker belongs beside each earlier stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with an unchanged copy
&lt;/h2&gt;

&lt;p&gt;Save the script version, symbol, timeframe, selected dates and strategy settings before you troubleshoot. Keep the original entry and exit conditions. Otherwise, a change that makes the report nonempty can quietly become a different strategy, and the trader may compare its number with the original idea.&lt;/p&gt;

&lt;p&gt;For example, changing a confirmed-bar condition to an intrabar condition is not just a display fix. It changes when a decision can happen. Removing a filter can create trades, but it does not prove that the filtered rule worked. Treat such changes as separately named experiments, with a reason and an expected effect.&lt;/p&gt;

&lt;p&gt;The first check is basic but decisive: confirm that the script is declared as a strategy rather than an indicator. Then locate the existing entry or order command. Plotting a BUY label is not itself an instruction to the Strategy Tester to create a simulated trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instrument the stages without adding orders
&lt;/h2&gt;

&lt;p&gt;The following Pine-style pseudocode describes the diagnostic shape. It is not a drop-in replacement for a strategy and has not been presented as a recorded run. Place observations around the existing path; do not introduce a second order command simply to test the display.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on each bar allowed by the frozen timing rule:
    bars_evaluated += 1
    record(bar_time, rule_inputs)

    if original_entry_condition:
        entry_conditions_true += 1
        mark the signal bar

        if original_order_branch_is_reached:
            order_branches_reached += 1
            record(entry_id, direction, intended_quantity)
            execute the existing order command once

    observe the simulator's recorded position and completed trades
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These observations separate two questions that often get combined. Did the condition occur? Did the execution path reach the existing order command? A third observation, the simulator's actual position or trade record, is still needed. Reaching a function call is evidence about control flow, not proof that a completed trade exists.&lt;/p&gt;

&lt;p&gt;Record the time of the signal separately from the time of the modeled entry. A closed-bar signal and a later fill are not the same event. Use the strategy's actual calculation and order-processing settings when explaining that difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the smallest table that distinguishes the failures
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Observed stage&lt;/th&gt;
&lt;th&gt;Next inspection&lt;/th&gt;
&lt;th&gt;What it does not prove&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No evaluated bars in the intended interval&lt;/td&gt;
&lt;td&gt;History, dates and the observation gate&lt;/td&gt;
&lt;td&gt;That the trading condition is false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bars exist, but no entry condition is true&lt;/td&gt;
&lt;td&gt;Inputs and the condition on those exact bars&lt;/td&gt;
&lt;td&gt;That another parameter would work later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The condition is true, but the order branch is not reached&lt;/td&gt;
&lt;td&gt;State guards, existing positions and branch logic&lt;/td&gt;
&lt;td&gt;That the broker or simulator rejected an order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The existing order branch is reached, but no entry is recorded&lt;/td&gt;
&lt;td&gt;Capital, quantity, contract settings and runtime errors&lt;/td&gt;
&lt;td&gt;That the strategy has a losing result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An entry is recorded, but no exit is complete&lt;/td&gt;
&lt;td&gt;Exit rule and the end of the selected interval&lt;/td&gt;
&lt;td&gt;That the open position should be closed artificially&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completed trades exist&lt;/td&gt;
&lt;td&gt;Rules, costs and the exact sample&lt;/td&gt;
&lt;td&gt;That historical fills will repeat live&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table is a proposed diagnostic contract. It does not diagnose an unseen script. It tells the developer and trader what evidence to bring to the next check.&lt;/p&gt;

&lt;h2&gt;
  
  
  A counterexample: a visible label with no trade
&lt;/h2&gt;

&lt;p&gt;Suppose the script marks a long signal, but its existing-position guard prevents the order branch from running. The chart now contains a convincing BUY label. The Strategy Tester still contains no new trade. Increasing initial capital would not fix that particular path, because the order branch was never reached.&lt;/p&gt;

&lt;p&gt;Now change the case: the branch is reached, but the configured order size cannot be afforded under the instrument's settings. The same visible label and empty report can occur for a different reason. Here the capital and quantity checks are relevant. The label alone cannot distinguish the two cases.&lt;/p&gt;

&lt;p&gt;This is why I would preserve both the rule marker and the execution-path observation. They answer different questions. A successful diagnosis names the earliest stage that differs from the written expectation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three acceptance cases before calling it fixed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Positive case:&lt;/strong&gt; choose a documented historical bar where the frozen rule should enter. Keep the input values and signal time. Confirm the intended branch is reached once and the simulator records the expected entry. If the exit falls inside the test interval, confirm the completed trade too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Negative case:&lt;/strong&gt; choose a bar where a required condition is false. The marker and branch observation must agree that no entry is intended. A repair that creates an entry here has changed the rule rather than only restoring its execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boundary case:&lt;/strong&gt; put an otherwise valid signal near the end of the available history. State whether the rule can complete an exit within that interval. An open position must not be counted as a completed winner or loser just to make the report look complete.&lt;/p&gt;

&lt;p&gt;Each case needs its expected behavior written before the run. A screenshot can then show whether the actual path matched that expectation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a completed sample looks like
&lt;/h2&gt;

&lt;p&gt;The frozen Supertrend case provides a concrete reference for a completed sample: 984 completed trades, including 394 take-profit exits, with a measured win rate of 40.04% in this simulation under stated costs. That count belongs to its own rules, EURUSD and EURJPY H1 data, period and cost assumptions. It is not a TradingView diagnosis and is not evidence that another script should produce the same trades.&lt;/p&gt;

&lt;p&gt;An empty report cannot be compared with that percentage. First establish what was evaluated and recorded. Then compare only results whose rule and cost contracts are known.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://www.tradingview.com/support/solutions/43000478450-i-ve-successfully-added-a-strategy-to-my-chart-but-it-doesn-t-generate-orders/" rel="noopener noreferrer"&gt;TradingView's no-orders troubleshooting guide&lt;/a&gt; and &lt;a href="https://stratcorealpha.com/tested/tr-supertrend" rel="noopener noreferrer"&gt;the canonical Supertrend case, rules and result&lt;/a&gt;. This is a diagnostic guide and a historical simulation reference, not a profit forecast or challenge-pass claim.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Log the requested filling policy before retrying an MT5 order</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Sun, 04 Oct 2026 14:14:14 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/log-the-requested-filling-policy-before-retrying-an-mt5-order-pdl</link>
      <guid>https://dev.to/stratcorealpha/log-the-requested-filling-policy-before-retrying-an-mt5-order-pdl</guid>
      <description>&lt;p&gt;An EA can reach its entry condition and still open no position. Error 10030, unsupported filling mode, belongs to the order request. Changing the entry signal will not fix that boundary.&lt;/p&gt;

&lt;p&gt;The useful first question is: what exactly did the EA ask the trade server to do?&lt;/p&gt;

&lt;h2&gt;
  
  
  Save a request receipt
&lt;/h2&gt;

&lt;p&gt;Before a retry, keep the symbol, execution mode, supported filling flags, requested filling policy and requested volume together. After sending, keep the result code and any returned order or deal reference. If a Python wrapper returns &lt;code&gt;None&lt;/code&gt;, handle that separately before reading &lt;code&gt;retcode&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This separates three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Was the strategy ready to enter?&lt;/li&gt;
&lt;li&gt;Was its request policy allowed for that symbol and execution mode?&lt;/li&gt;
&lt;li&gt;What happened after the request was sent?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A log saying only "entry failed" cannot answer all three.&lt;/p&gt;

&lt;h2&gt;
  
  
  A successful retry may change the rule
&lt;/h2&gt;

&lt;p&gt;FOK requires the whole requested volume to be filled, or none. IOC allows the available part to be filled and cancels the remainder. Switching from one to the other can therefore change the position size the strategy receives.&lt;/p&gt;

&lt;p&gt;For a challenge EA, "the error disappeared" is a poor acceptance condition. Agree whether a partial position is allowed before changing the policy. This is an order-behaviour decision, not a claim that the EA will pass a challenge.&lt;/p&gt;

&lt;p&gt;The symbol property &lt;code&gt;SYMBOL_FILLING_MODE&lt;/code&gt; is a flag mask. The order field &lt;code&gt;type_filling&lt;/code&gt; uses a different enumeration. Do not copy the raw mask into the order field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three checks before a retry
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Positive:&lt;/strong&gt; the requested policy is allowed for the actual symbol and execution mode. Preserve that intended policy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Negative:&lt;/strong&gt; the requested policy is not allowed. Stop with a clear diagnostic instead of trying a different policy without agreement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boundary:&lt;/strong&gt; the symbol uses Market Execution and the request asks for RETURN. That combination is not allowed for a market deal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pending orders are a separate path. Do not apply a market-deal helper to them simply because the same request structure is available.&lt;/p&gt;

&lt;p&gt;These are acceptance cases to implement and review. They are not a report of a broker fill or a live-account experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the diagnostic beside the source
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://stratcorealpha.com/fix/mt5-error-10030-unsupported-filling-mode?ref=devto" rel="noopener noreferrer"&gt;canonical error-10030 case&lt;/a&gt; contains the MQL5 helper and its scope. The &lt;a href="https://www.mql5.com/en/docs/constants/environment_state/marketinfoconstants#enum_symbol_filling" rel="noopener noreferrer"&gt;official symbol-property reference&lt;/a&gt; describes the execution-mode and filling-policy rules.&lt;/p&gt;

&lt;p&gt;The smallest useful receipt is the intended request, the allowed policies and the observed result. Keep all three before calling a retry a repair.&lt;/p&gt;

</description>
      <category>testing</category>
    </item>
    <item>
      <title>Put the cost threshold beside a backtest win rate</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Thu, 01 Oct 2026 15:33:41 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/put-the-cost-threshold-beside-a-backtest-win-rate-22no</link>
      <guid>https://dev.to/stratcorealpha/put-the-cost-threshold-beside-a-backtest-win-rate-22no</guid>
      <description>&lt;p&gt;Can the win rate in a strategy video be repeated on a continuous data window? That is the question behind the Supertrend plus EMA200 case. For an MT5 EA, a program that applies trading rules, I would ask a second question before changing its settings: what win rate would cover the costs used in this particular simulation?&lt;/p&gt;

&lt;h2&gt;
  
  
  The pair that matters
&lt;/h2&gt;

&lt;p&gt;The frozen base run measured a win rate of &lt;strong&gt;40.04% across 984 trades&lt;/strong&gt;. The recorded break-even win rate was &lt;strong&gt;41.10%&lt;/strong&gt;. In this simulation under stated costs, the measured rate was below that threshold.&lt;/p&gt;

&lt;p&gt;Those are historical model outputs. They do not predict an account result or whether a prop challenge will pass. The video's approximate 46% came from a different experiment. Its timeframe and parameters were not fully stated, so this is not an exact replication.&lt;/p&gt;

&lt;h2&gt;
  
  
  A review checklist before changing the EA
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Keep the cost model beside the result.&lt;/strong&gt; This case uses Dukascopy H1 Bid bars for EURUSD and EURJPY, from 2017-01-02 to 2026-06-26. The fixed spreads are 1.0 and 1.2 pips respectively. Commission is $7 per lot round trip. EURJPY conversion uses a fixed USDJPY assumption of 140. These are stated assumptions, not measured broker costs.&lt;/p&gt;

&lt;p&gt;A screenshot with the win rate but none of those inputs leaves the reader unable to interpret the threshold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Keep the held-out period visible.&lt;/strong&gt; The split was declared before testing: exits before 2024-01-01 are in-sample; exits on or after it are out-of-sample. The pooled base figures were 705 in-sample trades with a win rate of 41.8%, and 279 out-of-sample trades with a win rate of 35.5%.&lt;/p&gt;

&lt;p&gt;In this simulation under stated costs, the later sample did not repeat the earlier rate. Do not erase that difference by showing only the combined row.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Fix the ambiguous-bar rule before the run.&lt;/strong&gt; Signals use closed bars and entries use the next bar's open. If one H1 bar touches both stop and target, the simulation uses stop-first. One base trade was affected. H1 bars do not reveal the real order of those touches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three acceptance checks for the report
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The displayed result, cost threshold and data window all come from the same frozen base run. A threshold from another spread setting fails the check.&lt;/li&gt;
&lt;li&gt;The report shows both sides of the declared date split. A combined percentage alone fails the check.&lt;/li&gt;
&lt;li&gt;An ambiguous bar is identified and its fixed resolution rule is recorded. A result described as an observed execution fails the check because these fills were modeled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a trader with their own challenge EA, that is a useful boundary: first make the historical simulation auditable. Challenge rules and live execution need their own checks. A win rate cannot stand in for them.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://stratcorealpha.com/tested/tr-supertrend?ref=area5-devto" rel="noopener noreferrer"&gt;canonical case study&lt;/a&gt; contains the rule sheet, figures, results and limits. The measurements above are copied from its RESULTS.md, not a new run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update, 2 October: the commission contract
&lt;/h2&gt;

&lt;p&gt;A reader asked how to obtain the commission before opening an order. For an MT5 EA, a program that applies trading rules, I would first separate two records: the assumed charge in the historical simulation and the charge recorded after an actual execution.&lt;/p&gt;

&lt;p&gt;They answer different questions. A simulation can use a declared fee. That does not turn the declaration into a broker receipt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Freeze the meaning before the value
&lt;/h2&gt;

&lt;p&gt;A useful cost record needs these fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CostAssumption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;commissionBasis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;per_lot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;per_order&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;trade_value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;chargingSide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;entry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;round_trip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;spreadUnit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;point&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;price&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;conversionRule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;sourceKind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;declared_assumption&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;observed_charge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a proposed data contract, not a broker integration. The fee amount belongs beside it. Do not let an absent charging side fall back to round trip. Do not treat a price point as a pip because both are numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four acceptance checks for the report
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A complete trade:&lt;/strong&gt; the entry and exit receipts identify which side carries the fee. A round-trip assumption must not be charged once as a round trip at entry and again as a round trip at exit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A rejected order:&lt;/strong&gt; keep the rejection separate from a fill. An attempted order is not evidence of the assumed execution charge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A position still open:&lt;/strong&gt; show whether the simulation has charged entry only or booked a declared round-trip estimate. Do not silently compare that estimate with a realized closed-trade total.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A currency mismatch:&lt;/strong&gt; require the named conversion convention. A USD fee and an account balance in another currency do not share a unit automatically.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The expected result is an auditable cost record for each execution event. These checks test the bookkeeping contract. They do not establish that the chosen fee matches a future broker bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  A frozen example
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://stratcorealpha.com/tested/tr-supertrend#cost-assumptions" rel="noopener noreferrer"&gt;canonical Supertrend case&lt;/a&gt; declares a $7-per-lot round-trip commission and fixed spreads of 1.0 pip on EURUSD and 1.2 pips on EURJPY. Its EURJPY conversion uses fixed USDJPY 140. These are assumptions recorded with the test, not observed live costs.&lt;/p&gt;

&lt;p&gt;The historical simulation uses Dukascopy H1 bid bars from 2017-01-02 to 2026-06-26. In this simulation under stated costs, the reported numbers belong to that exact rule and cost contract. A changed charging side or conversion convention requires a separately recorded run.&lt;/p&gt;

&lt;p&gt;For a prop-challenge comparison, there is another boundary: a closed-trade cost ledger alone is not the intraday account-value path required to check a loss rule. First establish what the report contains. Then decide which question it can answer.&lt;/p&gt;

&lt;p&gt;Source question: &lt;a href="https://substack.com/@businesswithabhaya/note/c-350999846" rel="noopener noreferrer"&gt;the reader's cost question&lt;/a&gt;. The frozen case above contains the data, costs and limits. No account outcome or challenge result is promised.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>python</category>
    </item>
  </channel>
</rss>
