<?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: Amol Godbole</title>
    <description>The latest articles on DEV Community by Amol Godbole (@the_sensible_trader).</description>
    <link>https://dev.to/the_sensible_trader</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%2F4205573%2F9359fa21-1121-42b2-b61e-35ca52e6a9c7.png</url>
      <title>DEV Community: Amol Godbole</title>
      <link>https://dev.to/the_sensible_trader</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/the_sensible_trader"/>
    <language>en</language>
    <item>
      <title>Checkout my latest blog that turns email conformations into an event stream using Meta Muse !</title>
      <dc:creator>Amol Godbole</dc:creator>
      <pubDate>Sun, 25 Oct 2026 17:04:45 +0000</pubDate>
      <link>https://dev.to/the_sensible_trader/checkout-my-latest-blog-that-turns-email-conformations-into-an-event-stream-using-meta-muse--an</link>
      <guid>https://dev.to/the_sensible_trader/checkout-my-latest-blog-that-turns-email-conformations-into-an-event-stream-using-meta-muse--an</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/the_sensible_trader/how-i-turned-broker-emails-into-a-self-updating-trading-journal-4pd4" class="crayons-story__hidden-navigation-link"&gt;How I Turned Broker Emails Into a Self-Updating Trading Journal&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/the_sensible_trader" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F4205573%2F9359fa21-1121-42b2-b61e-35ca52e6a9c7.png" alt="the_sensible_trader profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/the_sensible_trader" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Amol Godbole
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Amol Godbole
                
                
              
              &lt;div id="story-author-preview-content-4912683" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/the_sensible_trader" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F4205573%2F9359fa21-1121-42b2-b61e-35ca52e6a9c7.png" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Amol Godbole&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/the_sensible_trader/how-i-turned-broker-emails-into-a-self-updating-trading-journal-4pd4" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/the_sensible_trader/how-i-turned-broker-emails-into-a-self-updating-trading-journal-4pd4" id="article-link-4912683"&gt;
          How I Turned Broker Emails Into a Self-Updating Trading Journal
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/architecture"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;architecture&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/automation"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;automation&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/backend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;backend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/the_sensible_trader/how-i-turned-broker-emails-into-a-self-updating-trading-journal-4pd4#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>automation</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I Turned Broker Emails Into a Self-Updating Trading Journal</title>
      <dc:creator>Amol Godbole</dc:creator>
      <pubDate>Sun, 25 Oct 2026 17:01:27 +0000</pubDate>
      <link>https://dev.to/the_sensible_trader/how-i-turned-broker-emails-into-a-self-updating-trading-journal-4pd4</link>
      <guid>https://dev.to/the_sensible_trader/how-i-turned-broker-emails-into-a-self-updating-trading-journal-4pd4</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclosure: &lt;a href="//the-sensible-trader.com"&gt;The Sensible Trader&lt;/a&gt;, the product discussed in this article implements an agentic solution for broker-email automation. This is an engineering account of how I built and tested the workflow, not investment advice or a performance claim.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Broker confirmations already contain the fills. The engineering challenge is turning those emails into trustworthy position state without duplicating trades, guessing at ambiguous events, or mishandling rolls and assignments.&lt;/p&gt;

&lt;p&gt;TL;DRI built an early-access system that reads broker-confirmation emails and updates The Sensible Trader automatically. The useful part was not extracting a ticker and price. It was building a state machine around messy real-world events: multiple fills, partial closes, rolls, assignments, duplicate notifications, and confirmations the parser cannot safely understand. This article explains the architecture, the safeguards, the failures that shaped it, and why uncertain events go to a human review queue instead of being guessed.&lt;/p&gt;

&lt;h2&gt;
  
  
  I did not set out to build an email parser.
&lt;/h2&gt;

&lt;p&gt;I wanted an honest answer to a simple question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What do I actually hold right now?&lt;/em&gt;&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%2Fvmdm30beymgvz7q33240.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%2Fvmdm30beymgvz7q33240.png" alt=" " width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That sounds trivial until an options position has been opened in several fills, partially closed, rolled to another strike, assigned, or spread across more than one brokerage account. A spreadsheet can preserve every row and still lose the meaning of the position.That was the failure I kept running into. My transaction history was complete, but my position state was not. A buy and a sell could appear as unrelated entries. A rolled covered call could look like a realized loss plus a new naked call. An assignment could close an option while leaving the underlying shares unchanged. The rows were technically present, yet the answer to “what remains open?” was wrong.&lt;/p&gt;

&lt;p&gt;So I built a different workflow for The Sensible Trader: let broker-confirmation emails become the event stream, then reconstruct position state from those events.The system is in early access, and I use it on my own journal. It is not finished, and it does not pretend to understand every broker message. The interesting engineering lesson has been this: &lt;strong&gt;automation becomes useful only after it learns when not to act.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The inbox is already an event stream
&lt;/h2&gt;

&lt;p&gt;Every executed order creates a broker record. For many retail brokers, a confirmation also lands in email with the details needed to journal the event: symbol, option type, strike, expiration, action, quantity, execution price, and time.That made the inbox a practical integration surface. It avoided collecting broker passwords and did not require a separate manual-entry habit. The user connects the Gmail inbox that actually receives broker confirmations; it may be different from the address used to create the journal account.The watcher uses read-only Gmail access. It first narrows the mailbox with sender and subject rules, then retrieves only matching messages.&lt;/p&gt;

&lt;p&gt;The pipeline runs every few minutes during the US market-day window. Frequency is not the hard part. Correctness is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing a fill is easier than understanding it
&lt;/h2&gt;

&lt;p&gt;The first version extracted fields from a confirmation and posted them to the journal. That worked for a clean, single-leg opening trade. It was not enough for real trading.A parser can tell me that an email says “sold two calls.” The journal still has to decide whether that event:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;opens a covered call against shares;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;opens a short call against a long call, as in a poor man’s covered call;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;adds to an existing short position;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;partially closes a long-call position;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;closes the old leg of a roll;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;represents an assignment rather than an ordinary sale.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those decisions require current position state, not just text extraction.&lt;/p&gt;

&lt;p&gt;I ended up separating the system into two layers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A registry-driven parser converts each supported broker format into a normalized execution event.&lt;/p&gt;

&lt;p&gt;A second layer matches that event against the user’s open positions and applies the appropriate state transition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The registry is global because broker formats are global. User trades, processed-message IDs, review items, and analytics remain isolated per user. When a broker changes its confirmation format, the reusable knowledge is the pattern—not anyone’s trade data.&lt;/p&gt;

&lt;p&gt;The normal form: one execution, explicit meaning&lt;/p&gt;

&lt;p&gt;Every recognized message is converted into a small event with explicit semantics:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{ "broker": "recognized-broker", "asset_type": "option", "symbol": "XYZ", &lt;br&gt;
 "action": "sell_to_close", "quantity": 2, "strike": 100, "right": "call", &lt;br&gt;
 "expiration": "YYYY-MM-DD", "execution_price": 4.25, &lt;br&gt;
 "executed_at": "broker-reported timestamp"}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The example is deliberately generic. The important part is that the system does not let free-form email language leak into journal logic. “Sold,” “closed,” “assigned,” and “rolled” are not interchangeable actions.&lt;/p&gt;

&lt;p&gt;Normalization also makes validation possible. Quantity must be positive. The expiration must be a valid date. An option needs a strike and a right. Price units must match the journal’s convention. A close must match a compatible open position.If those checks fail, the event does not become a trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deduplication is a financial control, not housekeeping
&lt;/h2&gt;

&lt;p&gt;A watcher that runs every few minutes will see the same confirmation repeatedly. Retries also happen after network failures. Email providers can return the same message in more than one search. Some brokers send several fills with nearly identical text.Posting twice is worse than missing a dashboard refresh. It changes position size and P&amp;amp;L.&lt;/p&gt;

&lt;p&gt;The system therefore keeps two dedup keys:&lt;/p&gt;

&lt;p&gt;Gmail message ID -  prevents the same email from being processed twice.&lt;/p&gt;

&lt;p&gt;Execution fingerprint-  prevents the same execution from being posted again through a duplicate notification or replay.The fingerprint uses normalized execution fields plus the message identity. That last piece matters: two legitimate fills can have the same ticker, contract, quantity, price, and minute. Collapsing them into one would be as wrong as posting one twice.A retry must be safe. If a write succeeds but the worker loses the response, the next run should converge on the same journal state rather than create another trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple fills must stay multiple fills
&lt;/h2&gt;

&lt;p&gt;Brokerage interfaces often summarize several fills into one average price. That is useful for a quick position view, but it is not enough for an audit trail. If eight contracts were accumulated across seven executions, the journal should preserve seven entry events. The position can still show a weighted average cost, but the underlying fills must remain available for reconstruction, charting, and debugging.&lt;/p&gt;

&lt;p&gt;This distinction exposed several bugs in my own reporting. A recap chart looked plausible while plotting one average entry instead of seven real executions. The fix was not to move a marker. It was to make individual broker fills the source of truth all the way through the pipeline. That rule now applies to ingestion as well as visualization:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;never invent a synthetic fill merely because an average is easier to display.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rolls are campaigns, not isolated rows
&lt;/h2&gt;

&lt;p&gt;A roll is where transaction-first journals get into trouble.Suppose I buy back a short covered call and sell another call at a later expiration. If the system treats those as unrelated records, it can produce two dangerous distortions:&lt;/p&gt;

&lt;p&gt;the new short call appears naked even though shares or a long call still cover it;&lt;/p&gt;

&lt;p&gt;the debit on the closed leg is presented as the final result even though the replacement leg remains open.&lt;/p&gt;

&lt;p&gt;The state model links both legs to the same campaign. The old leg closes; the replacement leg opens with its correct coverage. The underlying shares are not sold merely because the option strike changed.For reporting, realized P&amp;amp;L on the closed leg can be calculated, but the roll campaign should not be described as finished while the replacement remains open. That is a product-language rule backed by accounting state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assignments require two state changes
&lt;/h2&gt;

&lt;p&gt;Assignment notices taught me another version of the same lesson. An assigned short call is not only an option close. Shares are called away. An assigned short put is not only an option close either. Shares are put into the account. The agent handles a recognized assignment notice as two linked actions:It also checks whether another system has already recorded either action before writing anything. That avoids the particularly ugly failure mode where an assignment sweep and an email watcher both sell the same shares.Warnings about possible early assignment are not assignments. They are ignored rather than converted into trades.&lt;/p&gt;

&lt;p&gt;The close guard catches plausible-looking mistakes. Closing a position is where a parser’s confidence can become expensive.Before posting a close, the pipeline checks the action, instrument, contract, quantity, execution price, covering position, and original premium. A close must match an actual open position, and its execution price must agree with the broker confirmation within a small tolerance.One production incident revealed a subtle exception. An older open record had a zero premium because of bad historical data. The original guard rejected the close, leaving the option incorrectly open. That stale state later exposed the position to incorrect assignment handling.The safer rule is now:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the broker confirms a valid close but the open premium is zero or unknown, record the close at the verified execution price and flag it for review. Do not leave a closed position open merely because historical cost data is incomplete.The journal may not be able to calculate final P&amp;amp;L until the opening premium is corrected, but the position state remains truthful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Uncertainty goes to a review queue
&lt;/h2&gt;

&lt;p&gt;When a message is unknown, ambiguous, or cannot be matched confidently, the agent creates a review item. The user can approve, edit and approve, or dismiss it in the journal UI. Nothing is silently dropped, and nothing financially meaningful is guessed.That queue also makes the system extensible. A new parser can be added after a format is understood, while the original email remains the evidence for resolving the current event.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credentials should not be part of the parser
&lt;/h2&gt;

&lt;p&gt;The watcher needs two connections: read-only access to the broker-email inbox and authenticated access to the journal API.The journal key is collected through a secure connector rather than chat, source code, environment variables, or log files. The agent receives a short-lived surrogate that the platform replaces only when the request goes to the approved API host. The raw key is never available to the parsing code.That separation follows the broader principle in [OWASP’s guidance on protecting cryptographic keys] (sensitive keys belong in managed storage with access control and auditing, not embedded in application logic.&lt;/p&gt;

&lt;p&gt;The Gmail connection is also scoped to the inbox that receives confirmations. It does not need the user’s broker login.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in my product thinking
&lt;/h2&gt;

&lt;p&gt;I started with a feature idea: remove manual trade entry.I ended up with a reliability problem: reconstruct the truth from an imperfect stream of external events.That changed my priorities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First state beats activity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A ledger of fills is useful only if it can answer what remains open, what is covered, and what has actually been realized.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, every automation needs an abstain path.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A review queue is not evidence that the agent failed. It is evidence that the system refused to manufacture certainty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third, incidents should become invariants.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a roll is mislabeled, an assignment duplicates a share sale, or multiple fills collapse into one marker, the durable fix belongs in the parser, state model, or template—not in one corrected record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fourth, observability must verify outcomes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A worker saying “posted” is not proof that a journal record exists. The source of truth is the resulting journal state, backed by execution IDs and API responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Passing
&lt;/h2&gt;

&lt;p&gt;The surprising part was not that email could replace a form. It was that a mundane inbox could become a reliable event source—once deduplication, state transitions, review, and failure handling were treated as the product rather than implementation details.&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%2Fe6llwft5hv2fbv3oic7v.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%2Fe6llwft5hv2fbv3oic7v.png" alt=" " width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>automation</category>
      <category>backend</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
