<?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: Štefan Belopotočan</title>
    <description>The latest articles on DEV Community by Štefan Belopotočan (@bfexplorer).</description>
    <link>https://dev.to/bfexplorer</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2665025%2Ff7d7673a-a10e-475d-a55f-b954bd54bddf.png</url>
      <title>DEV Community: Štefan Belopotočan</title>
      <link>https://dev.to/bfexplorer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bfexplorer"/>
    <language>en</language>
    <item>
      <title>F# DSLs: what they are, why they matter, and how they improve Betfair BotTrigger strategies</title>
      <dc:creator>Štefan Belopotočan</dc:creator>
      <pubDate>Fri, 16 Jan 2026 14:28:28 +0000</pubDate>
      <link>https://dev.to/bfexplorer/f-dsls-what-they-are-why-they-matter-and-how-they-improve-betfair-bottrigger-strategies-3b3p</link>
      <guid>https://dev.to/bfexplorer/f-dsls-what-they-are-why-they-matter-and-how-they-improve-betfair-bottrigger-strategies-3b3p</guid>
      <description>&lt;p&gt;A domain‑specific language (DSL) is a small, focused language that expresses the concepts of a particular problem area directly. In F#, DSLs are often embedded inside the host language using computation expressions, records, and functions. This approach gives you the clarity of a custom language while keeping full access to the F# type system, tooling, and libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use an F# DSL?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Express intent, not plumbing.&lt;/strong&gt; A DSL lets you write code that reads like the strategy you want to execute, not the mechanics of how to execute it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better composability.&lt;/strong&gt; Steps can be chained, reused, and combined safely, which helps avoid duplicated logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stronger correctness.&lt;/strong&gt; Types guide you toward valid combinations and make invalid states harder to represent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved testability.&lt;/strong&gt; Strategy steps can be evaluated separately, with deterministic inputs and outputs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How this helps with Betfair BotTrigger strategies
&lt;/h2&gt;

&lt;p&gt;In BotTrigger workflows, &lt;code&gt;Execute()&lt;/code&gt; is called on every market update. That means you want the per‑tick work to be minimal and the strategy definition to be clear and reusable.&lt;/p&gt;

&lt;p&gt;By introducing a computation expression DSL, the strategy becomes a pipeline of &lt;em&gt;what&lt;/em&gt; to do rather than &lt;em&gt;how&lt;/em&gt; to do it each time. The example below defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;TriggerContext&lt;/code&gt; that packages market and selection data once.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Trigger&amp;lt;'a&amp;gt;&lt;/code&gt; type that describes a step in the strategy.&lt;/li&gt;
&lt;li&gt;A computation expression builder (&lt;code&gt;trigger&lt;/code&gt;) that sequences steps and short‑circuits when a step fails.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This leads to a clean separation between &lt;strong&gt;strategy definition&lt;/strong&gt; and &lt;strong&gt;execution&lt;/strong&gt;, and it allows you to reuse the same strategy pipeline across updates without rebuilding it each time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the DSL
&lt;/h2&gt;

&lt;p&gt;Let's start by defining our core types and the computation expression builder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AutoOpen&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;]&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;BotTriggerDSL&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;

    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;TriggerContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Market&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Market&lt;/span&gt;
            &lt;span class="nc"&gt;Selection&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Selection&lt;/span&gt;
            &lt;span class="nc"&gt;BotName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
            &lt;span class="nc"&gt;Parameters&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BotTriggerParameters&lt;/span&gt;
            &lt;span class="nc"&gt;MyBfexplorer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;IMyBfexplorer&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TriggerContext&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;TriggerBuilder&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="o"&gt;_.&lt;/span&gt;&lt;span class="nc"&gt;Return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="o"&gt;_.&lt;/span&gt;&lt;span class="nc"&gt;ReturnFrom&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;
        &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="o"&gt;_.&lt;/span&gt;&lt;span class="nc"&gt;Bind&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
                &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;
                &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;

        &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="o"&gt;_.&lt;/span&gt;&lt;span class="nc"&gt;Zero&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="o"&gt;_.&lt;/span&gt;&lt;span class="nc"&gt;Combine&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="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
                &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;
                &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;

        &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="o"&gt;_.&lt;/span&gt;&lt;span class="nc"&gt;Delay&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;unit&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;trigger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TriggerBuilder&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key insight here is that &lt;code&gt;Trigger&amp;lt;'a&amp;gt;&lt;/code&gt; is just a function from &lt;code&gt;TriggerContext&lt;/code&gt; to &lt;code&gt;option&amp;lt;'a&amp;gt;&lt;/code&gt;. This simple definition gives us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composability&lt;/strong&gt;: Steps can be chained using &lt;code&gt;let!&lt;/code&gt; in the computation expression.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short‑circuiting&lt;/strong&gt;: If any step returns &lt;code&gt;None&lt;/code&gt;, the pipeline stops immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pure functions&lt;/strong&gt;: Each step is deterministic and testable in isolation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Helper functions for common operations
&lt;/h2&gt;

&lt;p&gt;Now we add helper functions that work within our DSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fallback&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetParameter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;defaultArg&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;fallback&lt;/span&gt;
            &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;favouriteSelectionInRange&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromPrice&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toPrice&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Selection&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;getFavouriteSelections&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Market&lt;/span&gt;
            &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tryFind&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;mySelection&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mySelection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LastPriceTraded&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="n"&gt;fromPrice&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;toPrice&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;toTriggerResult&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Trigger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onSome&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;TriggerResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onNone&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TriggerResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TriggerContext&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;TriggerResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onSome&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onNone&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These helpers demonstrate how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract parameters safely (&lt;code&gt;param&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Filter selections based on criteria (&lt;code&gt;favouriteSelectionInRange&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Convert our DSL result to Betfair's &lt;code&gt;TriggerResult&lt;/code&gt; type (&lt;code&gt;toTriggerResult&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Here's the complete BotTrigger implementation using our DSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;BotTrigger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;

    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;MyStrategyBotTrigger&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Market&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Selection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;botName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;botTriggerParameters&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BotTriggerParameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myBfexplorer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;IMyBfexplorer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;inherit&lt;/span&gt; &lt;span class="nc"&gt;BotTriggerBase&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;botName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;botTriggerParameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myBfexplorer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nn"&gt;TriggerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Market&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt;
                &lt;span class="nn"&gt;TriggerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Selection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt;
                &lt;span class="nn"&gt;TriggerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BotName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;botName&lt;/span&gt;
                &lt;span class="nn"&gt;TriggerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Parameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;botTriggerParameters&lt;/span&gt;
                &lt;span class="nn"&gt;TriggerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MyBfexplorer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myBfexplorer&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;trigger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;fromPrice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s2"&gt;"FromPrice"&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;toPrice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="s2"&gt;"ToPrice"&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;mySelection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;favouriteSelectionInRange&lt;/span&gt; &lt;span class="n"&gt;fromPrice&lt;/span&gt; &lt;span class="n"&gt;toPrice&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mySelection&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;runStrategy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;toTriggerResult&lt;/span&gt;                                
                &lt;span class="n"&gt;strategy&lt;/span&gt;
                &lt;span class="nn"&gt;TriggerResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ExecuteActionBotOnSelection&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;TriggerResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EndExecutionWithMessage&lt;/span&gt; &lt;span class="s2"&gt;"No selection fulfills my criteria."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                

        &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IBotTrigger&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;

            &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Execute&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                &lt;span class="n"&gt;runStrategy&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;

            &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="p"&gt;_&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EndExecution&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                &lt;span class="bp"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the strategy reads like a high‑level description:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the &lt;code&gt;fromPrice&lt;/code&gt; parameter (default 2.5)&lt;/li&gt;
&lt;li&gt;Get the &lt;code&gt;toPrice&lt;/code&gt; parameter (default 3.0)&lt;/li&gt;
&lt;li&gt;Find a favourite selection in that price range&lt;/li&gt;
&lt;li&gt;Return that selection&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;strong&gt;key performance benefit&lt;/strong&gt; is that &lt;code&gt;ctx&lt;/code&gt;, &lt;code&gt;strategy&lt;/code&gt;, and &lt;code&gt;runStrategy&lt;/code&gt; are all constructed once when the trigger instance is created. Each time &lt;code&gt;Execute()&lt;/code&gt; is called (on every market update), we simply run the pre‑built pipeline. No allocations, no rebuilding—just execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;An F# DSL lets you model Betfair strategies as small, composable building blocks. In the BotTrigger scenario, it improves readability, reduces repeated work per market update, and keeps the intent of the strategy front‑and‑center. The result is a more maintainable and performant strategy layer that scales as your trading logic grows.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>dotnet</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Hello Betfair: The Same Strategy in F#, C#, VB.NET, and an AI Prompt (bfexplorer)</title>
      <dc:creator>Štefan Belopotočan</dc:creator>
      <pubDate>Thu, 15 Jan 2026 22:55:22 +0000</pubDate>
      <link>https://dev.to/bfexplorer/hello-betfair-the-same-strategy-in-f-c-vbnet-and-an-ai-prompt-bfexplorer-af</link>
      <guid>https://dev.to/bfexplorer/hello-betfair-the-same-strategy-in-f-c-vbnet-and-an-ai-prompt-bfexplorer-af</guid>
      <description>&lt;p&gt;bfexplorer lets you automate trading strategies in a few different “shapes”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;F# script (or DLL)&lt;/strong&gt;: run a BotTrigger directly from &lt;code&gt;.fsx&lt;/code&gt; source (and yes, you can also compile F# to a DLL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C# / VB.NET plugin&lt;/strong&gt;: compile a DLL and load it into bfexplorer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI prompt&lt;/strong&gt;: describe the logic in natural language and let an LLM execute it via bfexplorer’s MCP integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post uses the same “Hello Betfair” strategy idea in all approaches with one main goal: &lt;strong&gt;help you see the code differences&lt;/strong&gt; (syntax, interop, and packaging) while keeping the trading logic constant.&lt;/p&gt;

&lt;h2&gt;
  
  
  The strategy logic
&lt;/h2&gt;

&lt;p&gt;From the repo’s HelloBetfair overview, the core rule is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Place a back bet (or execute an action bot) if a selection price is within a given range.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the code examples below the default range is &lt;strong&gt;2.5 to 3.0&lt;/strong&gt; (inclusive). The trigger looks for a suitable selection (favourites in these examples) and either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;executes an action on that selection, or&lt;/li&gt;
&lt;li&gt;ends with a message if nothing matches.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where the examples live
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Overview: ../../src/HelloBetfair/README.md&lt;/li&gt;
&lt;li&gt;F# script: ../../src/HelloBetfair/FSharp/MyStrategyBotTrigger.fsx&lt;/li&gt;
&lt;li&gt;C# trigger: ../../src/HelloBetfair/CSharp/MyStrategyBotTrigger/MyStrategyBotTrigger.cs&lt;/li&gt;
&lt;li&gt;VB.NET trigger: ../../src/HelloBetfair/VisualBasic/MyStrategyBotTrigger/MyStrategyBotTrigger.vb&lt;/li&gt;
&lt;li&gt;AI prompt: ../../src/HelloBetfair/AI/MyStrategy.md&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1) F# — shortest code, runs from source
&lt;/h2&gt;

&lt;p&gt;bfexplorer is built with F#, and it supports &lt;strong&gt;F# scripting&lt;/strong&gt;. That means you can execute a BotTrigger directly from the &lt;code&gt;.fsx&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you prefer a compiled workflow, you can also package an F# trigger as a DLL assembly (similar to the C#/VB.NET examples). The nice part is that you get to choose: script for iteration speed, DLL for deployment/versioning.&lt;/p&gt;

&lt;p&gt;Here’s the HelloBetfair F# example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;BotTrigger&lt;/span&gt;

&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s2"&gt;"C:&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;Program Files&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;BeloSoft&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;Bfexplorer&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;

#r "&lt;/span&gt;&lt;span class="nn"&gt;BeloSoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dll&lt;/span&gt;&lt;span class="s2"&gt;"
#r "&lt;/span&gt;&lt;span class="nn"&gt;BeloSoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Bfexplorer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dll&lt;/span&gt;&lt;span class="s2"&gt;"
#r "&lt;/span&gt;&lt;span class="nn"&gt;BeloSoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Bfexplorer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Core&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dll&lt;/span&gt;&lt;span class="s2"&gt;"
#r "&lt;/span&gt;&lt;span class="nn"&gt;BeloSoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Bfexplorer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Trading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dll&lt;/span&gt;&lt;span class="s2"&gt;"

open BeloSoft.Bfexplorer.Domain
open BeloSoft.Bfexplorer.Trading

type MyStrategyBotTrigger (market : Market, selection : Selection, botName : string, botTriggerParameters : BotTriggerParameters, myBfexplorer : IMyBfexplorer) =
    inherit BotTriggerBase (market, selection, botName, botTriggerParameters, myBfexplorer)

    let getMySelection () =
        let fromPrice = defaultArg (botTriggerParameters.GetParameter&amp;lt;float&amp;gt; ("&lt;/span&gt;&lt;span class="n"&gt;fromPrice&lt;/span&gt;&lt;span class="s2"&gt;")) 2.5
        let toPrice = defaultArg (botTriggerParameters.GetParameter&amp;lt;float&amp;gt; ("&lt;/span&gt;&lt;span class="n"&gt;toPrice&lt;/span&gt;&lt;span class="s2"&gt;")) 3.0

        getFavouriteSelections market
        |&amp;gt; List.tryFind (fun mySelection -&amp;gt; 
                let price = mySelection.LastPriceTraded

                price &amp;gt;= fromPrice &amp;amp;&amp;amp; price &amp;lt;= toPrice
            )

    interface IBotTrigger with

        member _this.Execute () =
            match getMySelection () with
            | Some mySelection -&amp;gt; TriggerResult.ExecuteActionBotOnSelection mySelection
            | None -&amp;gt; TriggerResult.EndExecutionWithMessage "&lt;/span&gt;&lt;span class="nc"&gt;No&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt; &lt;span class="n"&gt;fulfills&lt;/span&gt; &lt;span class="n"&gt;my&lt;/span&gt; &lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"

        member _this.EndExecution () =
            ()
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I like about this one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No DLL build&lt;/strong&gt;: iterate fast&lt;/li&gt;
&lt;li&gt;the selection filtering reads cleanly (&lt;code&gt;tryFind&lt;/code&gt; + pattern matching)&lt;/li&gt;
&lt;li&gt;parameter defaults are one-liners (&lt;code&gt;defaultArg ... 2.5&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2) C# — same logic, packaged as a DLL
&lt;/h2&gt;

&lt;p&gt;In C#, you implement the same &lt;code&gt;IBotTrigger&lt;/code&gt; interface, but you compile the project to a DLL.&lt;/p&gt;

&lt;p&gt;HelloBetfair C# trigger:&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;BeloSoft.Bfexplorer.Domain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;BeloSoft.Bfexplorer.Trading&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.FSharp.Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyStrategyBotTrigger&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyStrategyBotTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Market&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Selection&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;botName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BotTriggerParameters&lt;/span&gt; &lt;span class="n"&gt;botTriggerParameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IMyBfexplorer&lt;/span&gt; &lt;span class="n"&gt;myBfexplorer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IBotTrigger&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&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;FromPrice&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;ToPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;GetParameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fromPriceOption&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;botTriggerParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetParameter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"FromPrice"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;toPriceOption&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;botTriggerParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetParameter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"ToPrice"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fromPrice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FSharpOption&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_IsSome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromPriceOption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;fromPriceOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;toPrice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FSharpOption&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_IsSome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toPriceOption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;toPriceOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toPrice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Selection&lt;/span&gt; &lt;span class="nf"&gt;GetMySelection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetParameters&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;MarketExtensionsModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFavouriteSelections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastPriceTraded&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;fromPrice&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastPriceTraded&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;toPrice&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;TriggerResult&lt;/span&gt; &lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mySelection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetMySelection&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;mySelection&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&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;TriggerResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewExecuteActionBotOnSelection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mySelection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&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;TriggerResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewEndExecutionWithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No selection fulfills my criteria."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;EndExecution&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&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;Two very “real-world” details show up here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bfexplorer APIs expose F# &lt;code&gt;option&lt;/code&gt;, so you’ll see &lt;code&gt;FSharpOption&amp;lt;T&amp;gt;&lt;/code&gt; interop&lt;/li&gt;
&lt;li&gt;the parameter names are &lt;code&gt;FromPrice&lt;/code&gt; / &lt;code&gt;ToPrice&lt;/code&gt; (PascalCase)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3) Visual Basic — same DLL workflow, different syntax
&lt;/h2&gt;

&lt;p&gt;VB.NET is functionally the same as the C# approach: you build a DLL and load it.&lt;/p&gt;

&lt;p&gt;HelloBetfair VB.NET trigger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vb"&gt;&lt;code&gt;&lt;span class="k"&gt;Imports&lt;/span&gt; &lt;span class="nn"&gt;BeloSoft.Bfexplorer.Domain&lt;/span&gt;
&lt;span class="k"&gt;Imports&lt;/span&gt; &lt;span class="nn"&gt;BeloSoft.Bfexplorer.Trading&lt;/span&gt;
&lt;span class="k"&gt;Imports&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.FSharp.Core&lt;/span&gt;

&lt;span class="k"&gt;Public&lt;/span&gt; &lt;span class="k"&gt;Class&lt;/span&gt; &lt;span class="nc"&gt;MyStrategyBotTrigger&lt;/span&gt;
    &lt;span class="k"&gt;Implements&lt;/span&gt; &lt;span class="n"&gt;IBotTrigger&lt;/span&gt;

    &lt;span class="k"&gt;Private&lt;/span&gt; &lt;span class="k"&gt;ReadOnly&lt;/span&gt; &lt;span class="n"&gt;_market&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;Market&lt;/span&gt;
    &lt;span class="k"&gt;Private&lt;/span&gt; &lt;span class="k"&gt;ReadOnly&lt;/span&gt; &lt;span class="n"&gt;_selection&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;Selection&lt;/span&gt;
    &lt;span class="k"&gt;Private&lt;/span&gt; &lt;span class="k"&gt;ReadOnly&lt;/span&gt; &lt;span class="n"&gt;_botName&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;Private&lt;/span&gt; &lt;span class="k"&gt;ReadOnly&lt;/span&gt; &lt;span class="n"&gt;_botTriggerParameters&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;BotTriggerParameters&lt;/span&gt;
    &lt;span class="k"&gt;Private&lt;/span&gt; &lt;span class="k"&gt;ReadOnly&lt;/span&gt; &lt;span class="n"&gt;_myBfexplorer&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;IMyBfexplorer&lt;/span&gt;

    &lt;span class="k"&gt;Public&lt;/span&gt; &lt;span class="k"&gt;Sub&lt;/span&gt; &lt;span class="nf"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;Market&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;Selection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;botName&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;botTriggerParameters&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;BotTriggerParameters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myBfexplorer&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;IMyBfexplorer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;_market&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt;
        &lt;span class="n"&gt;_selection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;selection&lt;/span&gt;
        &lt;span class="n"&gt;_botName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;botName&lt;/span&gt;
        &lt;span class="n"&gt;_botTriggerParameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;botTriggerParameters&lt;/span&gt;
        &lt;span class="n"&gt;_myBfexplorer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myBfexplorer&lt;/span&gt;
    &lt;span class="k"&gt;End&lt;/span&gt; &lt;span class="k"&gt;Sub&lt;/span&gt;

    &lt;span class="k"&gt;Private&lt;/span&gt; &lt;span class="k"&gt;Function&lt;/span&gt; &lt;span class="nf"&gt;GetParameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FromPrice&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToPrice&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;fromPriceOption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_botTriggerParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Of&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="s"&gt;"FromPrice"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;toPriceOption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_botTriggerParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Of&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="s"&gt;"ToPrice"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;fromPrice&lt;/span&gt; &lt;span class="o"&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;OptionModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromPriceOption&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;fromPriceOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;toPrice&lt;/span&gt; &lt;span class="o"&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;OptionModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toPriceOption&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;toPriceOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;Return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;End&lt;/span&gt; &lt;span class="k"&gt;Function&lt;/span&gt;

    &lt;span class="k"&gt;Private&lt;/span&gt; &lt;span class="k"&gt;Function&lt;/span&gt; &lt;span class="nf"&gt;GetMySelection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;Selection&lt;/span&gt;
        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetParameters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;fromPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromPrice&lt;/span&gt;
        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;toPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToPrice&lt;/span&gt;

        &lt;span class="k"&gt;Return&lt;/span&gt; &lt;span class="n"&gt;MarketExtensionsModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getFavouriteSelections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_market&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Function&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastPriceTraded&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;fromPrice&lt;/span&gt; &lt;span class="ow"&gt;AndAlso&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastPriceTraded&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;toPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;End&lt;/span&gt; &lt;span class="k"&gt;Function&lt;/span&gt;

    &lt;span class="k"&gt;Public&lt;/span&gt; &lt;span class="k"&gt;Function&lt;/span&gt; &lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="n"&gt;TriggerResult&lt;/span&gt; &lt;span class="k"&gt;Implements&lt;/span&gt; &lt;span class="n"&gt;IBotTrigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Execute&lt;/span&gt;
        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;mySelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetMySelection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;If&lt;/span&gt; &lt;span class="n"&gt;mySelection&lt;/span&gt; &lt;span class="ow"&gt;IsNot&lt;/span&gt; &lt;span class="k"&gt;Nothing&lt;/span&gt; &lt;span class="k"&gt;Then&lt;/span&gt;
            &lt;span class="k"&gt;Return&lt;/span&gt; &lt;span class="n"&gt;TriggerResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewExecuteActionBotOnSelection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mySelection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;Else&lt;/span&gt;
            &lt;span class="k"&gt;Return&lt;/span&gt; &lt;span class="n"&gt;TriggerResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEndExecutionWithMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No selection fulfills my criteria."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;End&lt;/span&gt; &lt;span class="k"&gt;If&lt;/span&gt;
    &lt;span class="k"&gt;End&lt;/span&gt; &lt;span class="k"&gt;Function&lt;/span&gt;

    &lt;span class="k"&gt;Public&lt;/span&gt; &lt;span class="k"&gt;Sub&lt;/span&gt; &lt;span class="nf"&gt;EndExecution&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;Implements&lt;/span&gt; &lt;span class="n"&gt;IBotTrigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EndExecution&lt;/span&gt;
    &lt;span class="k"&gt;End&lt;/span&gt; &lt;span class="k"&gt;Sub&lt;/span&gt;
&lt;span class="k"&gt;End&lt;/span&gt; &lt;span class="k"&gt;Class&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4) AI prompt — describe the intent, let bfexplorer/MCP execute
&lt;/h2&gt;

&lt;p&gt;The AI example in the repo is a prompt file. It’s not trying to be a full programming language replacement; it’s a fast way to express intent and run it on the active market.&lt;/p&gt;

&lt;p&gt;HelloBetfair AI prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;On the currently active Betfair market, sort the market selections by price in ascending order. 

Immediately execute the 'Bet 10 Euro' strategy on the first selection whose price is between 2.5 and 3.0 (inclusive). 

If there is no selection in this price range, do not execute any bet. 

Do not ask for further user confirmation—proceed automatically according to these criteria.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice this one intentionally differs from the code examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it sorts selections by price&lt;/li&gt;
&lt;li&gt;it uses the same range ($[2.5, 3.0]$)&lt;/li&gt;
&lt;li&gt;it executes a named strategy (“Bet 10 Euro”) on the first match&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s actually a feature: prompts are great for quickly trying an idea, then “promoting” it into an F#/C#/VB trigger once it proves useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical notes (things that bite people)
&lt;/h2&gt;

&lt;p&gt;Here are the real “interop” details you’ll notice when you compare the three codebases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parameters are just string keys&lt;/strong&gt;: &lt;code&gt;botTriggerParameters.GetParameter&amp;lt;T&amp;gt;(name)&lt;/code&gt; looks up a string in a dictionary. Pick names you like (for example &lt;code&gt;FromPrice&lt;/code&gt;/&lt;code&gt;ToPrice&lt;/code&gt;) and keep them consistent across languages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;F# discriminated unions (DU) show up as generated classes in C#/VB&lt;/strong&gt;: &lt;code&gt;TriggerResult&lt;/code&gt; is an F# DU. In F# you return DU cases directly (e.g. &lt;code&gt;TriggerResult.ExecuteActionBotOnSelection mySelection&lt;/code&gt;). In C#/VB you construct the DU using generated helpers (e.g. &lt;code&gt;TriggerResult.NewExecuteActionBotOnSelection(mySelection)&lt;/code&gt; / &lt;code&gt;NewEndExecutionWithMessage(...)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;F# &lt;code&gt;option&lt;/code&gt; types require interop&lt;/strong&gt;: when you read optional values from F#, C#/VB will see &lt;code&gt;FSharpOption&amp;lt;T&amp;gt;&lt;/code&gt; (or use &lt;code&gt;OptionModule.IsSome(...)&lt;/code&gt; in VB). That’s why the C#/VB examples look a bit more “ceremonial” around parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trigger vs execution separation is intentional&lt;/strong&gt;: these examples return &lt;code&gt;ExecuteActionBotOnSelection&lt;/code&gt; rather than placing orders inline. In bfexplorer, that separation often makes strategies easier to reuse and test (selection logic in the trigger, execution rules in the action bot).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;The point of this “Hello Betfair” example is the side-by-side comparison.&lt;/p&gt;

&lt;p&gt;When you scan the three implementations, focus on these differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expression of the same rule&lt;/strong&gt;: F# uses &lt;code&gt;List.tryFind&lt;/code&gt; + pattern matching; C#/VB use LINQ-style &lt;code&gt;FirstOrDefault&lt;/code&gt; + &lt;code&gt;if&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;F# DU / option interop&lt;/strong&gt;: &lt;code&gt;TriggerResult&lt;/code&gt; and &lt;code&gt;option&lt;/code&gt; feel native in F#, but become generated classes/helpers in C#/VB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Packaging and workflow&lt;/strong&gt;: &lt;code&gt;.fsx&lt;/code&gt; is great for iteration; DLLs are great when you want a compiled artifact (and you can choose either approach in F#).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI prompt as a contrast&lt;/strong&gt;: it’s not about syntax at all—it’s about describing intent and letting bfexplorer/MCP drive execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want, tell me how you build/load DLL triggers in your setup (or point me to the solution files), and I’ll add a short “Build &amp;amp; load” section with the exact steps.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Trading involves risk. Test in a safe environment first. This post is for educational purposes only.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>mcp</category>
    </item>
    <item>
      <title>FSI First: Why AI Should Query Types Directly When Vibe Coding</title>
      <dc:creator>Štefan Belopotočan</dc:creator>
      <pubDate>Sat, 27 Dec 2025 17:44:15 +0000</pubDate>
      <link>https://dev.to/bfexplorer/fsi-first-why-ai-should-query-types-directly-when-vibe-coding-1ebb</link>
      <guid>https://dev.to/bfexplorer/fsi-first-why-ai-should-query-types-directly-when-vibe-coding-1ebb</guid>
      <description>&lt;h2&gt;
  
  
  Background: Discovering FSI MCP
&lt;/h2&gt;

&lt;p&gt;My app uses F# scripting extensively, so I regularly read F# Advent articles organized by Sergey Tihon at the end of the year to check for new ideas. Since my app already uses MCP (Model Context Protocol), I was particularly inspired by jvaneyck's article: &lt;a href="https://jvaneyck.wordpress.com/2025/12/04/fsi-mcp-injecting-ai-coding-agents-into-my-f-repl-workflow/" rel="noopener noreferrer"&gt;FSI MCP: Injecting AI Coding Agents into My F# REPL Workflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article opened my eyes to the possibilities of FSI MCP in my use case, and I decided to explore how it could improve my AI-assisted workflow. What I discovered was both enlightening and concerning—AI assistants don't always use the best tools available to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Incident
&lt;/h2&gt;

&lt;p&gt;I asked my AI assistant a simple question while working on a football betting script:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What properties can I use to create rules for the FootballMatch type?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI responded with a comprehensive list of properties... but got the types wrong. It said &lt;code&gt;HomeScore&lt;/code&gt; was &lt;code&gt;int16&lt;/code&gt; when it's actually &lt;code&gt;Byte&lt;/code&gt;. It searched documentation and made assumptions instead of checking the actual source.&lt;/p&gt;

&lt;p&gt;When I pushed back with "Really do check all types," the AI finally used FSI (F# Interactive) to inspect the actual assembly and gave me the correct answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Went Wrong
&lt;/h2&gt;

&lt;p&gt;The AI fell into a common trap when working with .NET types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Searched documentation first&lt;/strong&gt; - Found older/incomplete info&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Made assumptions&lt;/strong&gt; - Guessed at types based on similar code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provided plausible but wrong answers&lt;/strong&gt; - Looked correct but wasn't accurate&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the opposite of what should happen when you have FSI MCP tools available.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should Have Happened
&lt;/h2&gt;

&lt;p&gt;When I asked about &lt;code&gt;FootballMatch&lt;/code&gt; properties, the AI should have &lt;strong&gt;immediately&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="nc"&gt;I&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s2"&gt;"C:&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;Program Files&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;BeloSoft&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;Bfexplorer&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;;;
#r "&lt;/span&gt;&lt;span class="nn"&gt;BeloSoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Bfexplorer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;FootballScoreProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dll&lt;/span&gt;&lt;span class="s2"&gt;";;

open System.Reflection;;
open BeloSoft.Bfexplorer.FootballScoreProvider.Models;;

let footballMatchType = typeof&amp;lt;FootballMatch&amp;gt;;;
let properties = footballMatchType.GetProperties(
    BindingFlags.Public ||| BindingFlags.Instance);;

properties |&amp;gt; Array.iter (fun p -&amp;gt; 
    printfn "&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="s2"&gt;" p.Name p.PropertyType.Name);;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the &lt;strong&gt;authoritative, accurate, current&lt;/strong&gt; answer directly from the loaded assembly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Correct Answer
&lt;/h2&gt;

&lt;p&gt;Using FSI revealed the actual types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// CORRECT (via FSI):&lt;/span&gt;
&lt;span class="nc"&gt;HomeScore&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Byte&lt;/span&gt;          &lt;span class="c1"&gt;// Not int16!&lt;/span&gt;
&lt;span class="nc"&gt;AwayScore&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Byte&lt;/span&gt;
&lt;span class="nc"&gt;ScoreDifference&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SByte&lt;/span&gt;   &lt;span class="c1"&gt;// Signed byte, not int16&lt;/span&gt;
&lt;span class="nc"&gt;Goals&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Byte&lt;/span&gt;
&lt;span class="nc"&gt;GoalBeingScored&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="c1"&gt;// Bonus property I missed!&lt;/span&gt;
&lt;span class="nc"&gt;MatchTime&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int32&lt;/span&gt;
&lt;span class="nc"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="c1"&gt;// ... etc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why FSI First Matters for "Vibe Coding"
&lt;/h2&gt;

&lt;p&gt;"Vibe coding" with AI means working fluidly, asking questions, and letting the AI figure out implementation details. But &lt;strong&gt;accuracy matters&lt;/strong&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Documentation-First Approach
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Documentation can be outdated&lt;/li&gt;
&lt;li&gt;Assumptions lead to subtle bugs&lt;/li&gt;
&lt;li&gt;Wrong types cause runtime errors&lt;/li&gt;
&lt;li&gt;Wastes time fixing mistakes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ FSI-First Approach
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authoritative&lt;/strong&gt;: Queries the actual loaded assembly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Current&lt;/strong&gt;: Always reflects the real code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complete&lt;/strong&gt;: Shows all members, even undocumented ones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast&lt;/strong&gt;: Instant feedback from REPL&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The FSI-First Rule for AI Assistants
&lt;/h2&gt;

&lt;p&gt;When a user asks about .NET types in a workspace with FSI MCP tools:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ALWAYS:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use FSI to inspect the type first&lt;/li&gt;
&lt;li&gt;Get the actual properties/methods/types&lt;/li&gt;
&lt;li&gt;Then provide the answer with confidence&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;NEVER:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search documentation first&lt;/li&gt;
&lt;li&gt;Assume types based on similar code&lt;/li&gt;
&lt;li&gt;Guess at property names or types&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Lessons for Non-Developers
&lt;/h2&gt;

&lt;p&gt;If you're working with an AI to explore F#/.NET code:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Demand FSI Verification
&lt;/h3&gt;

&lt;p&gt;When asking about types, explicitly request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use FSI to show me the actual properties of [TypeName]"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Question Assumptions
&lt;/h3&gt;

&lt;p&gt;If the AI provides type information without showing FSI output, ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Did you check this with FSI, or are you guessing?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Trust But Verify
&lt;/h3&gt;

&lt;p&gt;Even experienced AIs can fall into documentation traps. FSI is your ground truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Principle
&lt;/h2&gt;

&lt;p&gt;This incident reveals a key insight about AI-assisted development:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools exist for a reason.&lt;/strong&gt; When you have FSI MCP tools, they're not just for convenience—they're for &lt;strong&gt;accuracy&lt;/strong&gt;. The AI should prioritize direct type inspection over documentation search, every time.&lt;/p&gt;

&lt;p&gt;Think of it like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation = "Someone told me about this"&lt;/li&gt;
&lt;li&gt;FSI = "Let me look at the actual source code right now"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which would you trust more?&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Example: Building Better Filters
&lt;/h2&gt;

&lt;p&gt;With the correct FSI-verified types, I can write accurate filters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Now I know Goals is Byte, not int16&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;isHighScoring&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FootballMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Goals&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;uy&lt;/span&gt;  &lt;span class="c1"&gt;// uy suffix for Byte, not y for int16&lt;/span&gt;

&lt;span class="c1"&gt;// And I discovered GoalBeingScored exists!&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;isLiveGoal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FootballMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GoalBeingScored&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MatchTime&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;

&lt;span class="c1"&gt;// Correct type for ScoreDifference (SByte)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;isCloseMatch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FootballMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;abs&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ScoreDifference&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;  &lt;span class="c1"&gt;// y suffix for SByte&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When vibe coding with AI in F#/.NET environments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;FSI First&lt;/strong&gt; - Always query types directly with FSI MCP tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust the REPL&lt;/strong&gt; - It's the authoritative source&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document Later&lt;/strong&gt; - Use docs for concepts, FSI for implementation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Call Out Mistakes&lt;/strong&gt; - When AI doesn't use FSI, push back&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The FSI MCP tools exist to eliminate guesswork. Use them first, not as a fallback.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/StefanBelo/BetfairAiTrading/blob/267d4e18f01bd4873bdebfc14c06d47dda818303/docs/Using_FSI_MCP_Tools_for_FSharp_Code.md" rel="noopener noreferrer"&gt;Using FSI MCP Tools to Create Better F# Code for Non-Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/StefanBelo/BetfairAiTrading/blob/267d4e18f01bd4873bdebfc14c06d47dda818303/docs/Posts/FSI_MCP_Tools_For_NonCoders.md" rel="noopener noreferrer"&gt;FSI MCP Tools For NonCoders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jvaneyck.wordpress.com/2025/12/04/fsi-mcp-injecting-ai-coding-agents-into-my-f-repl-workflow/" rel="noopener noreferrer"&gt;FSI MCP: Injecting AI Coding Agents into My F# REPL Workflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; When AI has FSI tools, it should use them FIRST for type inspection, not search docs and guess. FSI = truth. Docs = hints.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tooling</category>
      <category>agents</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
