<?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>Eight Session-Boundary Tests I Run Before Trusting a Trading Bot</title>
      <dc:creator>Arnold Holm</dc:creator>
      <pubDate>Sat, 15 Aug 2026 01:56:19 +0000</pubDate>
      <link>https://dev.to/stratcorealpha/eight-session-boundary-tests-i-run-before-trusting-a-trading-bot-ofj</link>
      <guid>https://dev.to/stratcorealpha/eight-session-boundary-tests-i-run-before-trusting-a-trading-bot-ofj</guid>
      <description>&lt;p&gt;A session reset looks simple until a platform reloads data, reconnects or sends the same timestamp twice.&lt;/p&gt;

&lt;p&gt;If a bot resets state from the calendar date alone, a restart can unlock a risk control early. Or the same session can reset twice. I keep two facts separate before I touch entry logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a new bar&lt;/li&gt;
&lt;li&gt;a new trading session&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most new bars are not session starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small classifier first
&lt;/h2&gt;

&lt;p&gt;The first pass can be read-only. It receives the current timestamp and the platform's native session marker. It returns one observation class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Observation&lt;/span&gt; &lt;span class="nf"&gt;Classify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;nativeSessionStart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;hasPrevious&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;hasPrevious&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&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;Observation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;First&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;previous&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;Observation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reordered&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;previous&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;nativeSessionStart&lt;/span&gt;
            &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Observation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DuplicateSessionMarker&lt;/span&gt;
            &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Observation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duplicate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&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;nativeSessionStart&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Observation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBarAndSession&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Observation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBar&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 does not decide when trading is allowed. It only makes platform events observable. The business reset stays separate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The eight tests
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First observation.&lt;/strong&gt; Initialize without inventing a previous bar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forward bar.&lt;/strong&gt; Advance once without a session event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate update.&lt;/strong&gt; Keep state when the timestamp has not changed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate session marker.&lt;/strong&gt; Suppress the same marker at the same timestamp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reordered data.&lt;/strong&gt; Report a backward timestamp instead of treating it as forward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reordered session marker.&lt;/strong&gt; Reject the marker when its timestamp moves backward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native session start.&lt;/strong&gt; Report it exactly once on the platform signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit reset.&lt;/strong&gt; Clear observation state without claiming that the market began a new session.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Put restart behavior in the contract
&lt;/h2&gt;

&lt;p&gt;A pure classifier is only the start. If the bot carries a daily loss lock, trade counter or opening range across bars, choose the recovery model before implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;restore the last trusted state from persistent storage;&lt;/li&gt;
&lt;li&gt;rebuild it from authorized account and chart history;&lt;/li&gt;
&lt;li&gt;stay locked until the next verified native session boundary;&lt;/li&gt;
&lt;li&gt;require an explicit operator decision in a demo or test environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Leaving this undefined is what creates restart-only bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the evidence packet small
&lt;/h2&gt;

&lt;p&gt;For each run I record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source hash and platform version;&lt;/li&gt;
&lt;li&gt;instrument, bar type and trading-hours template;&lt;/li&gt;
&lt;li&gt;ordered timestamps and native session markers;&lt;/li&gt;
&lt;li&gt;expected and observed class for each test;&lt;/li&gt;
&lt;li&gt;reload and restart result;&lt;/li&gt;
&lt;li&gt;what was not tested.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is enough to separate a code defect from a chart-template mismatch. It also keeps a later test from quietly using different source or settings.&lt;/p&gt;

&lt;p&gt;The business rule should still fit in one sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reset &lt;code&gt;named state&lt;/code&gt; when &lt;code&gt;observable event&lt;/code&gt; occurs, provided &lt;code&gt;state condition&lt;/code&gt;; otherwise keep the prior state until &lt;code&gt;fallback event&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then add one case where the reset must happen and one where it must not.&lt;/p&gt;

&lt;p&gt;For the longer reasoning behind calendar midnight and native session events, see &lt;a href="https://medium.com/@stratcorealpha/a-trading-session-is-an-event-not-just-a-date-c3ffd3b2668f" rel="noopener noreferrer"&gt;A Trading Session Is an Event, Not Just a Date&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is about software behavior and testing. It is not investment advice and does not promise profitability or trading performance.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>csharp</category>
      <category>automation</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
