<?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: Valerii Sakara</title>
    <description>The latest articles on DEV Community by Valerii Sakara (@arakas4488cmd).</description>
    <link>https://dev.to/arakas4488cmd</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%2F4076876%2F331b07a6-6bcf-400b-9fa3-6d37807ffe8a.png</url>
      <title>DEV Community: Valerii Sakara</title>
      <link>https://dev.to/arakas4488cmd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arakas4488cmd"/>
    <language>en</language>
    <item>
      <title>The 2:1 Setup That Paid Out at 0.75:1</title>
      <dc:creator>Valerii Sakara</dc:creator>
      <pubDate>Sun, 30 Aug 2026 19:07:34 +0000</pubDate>
      <link>https://dev.to/arakas4488cmd/the-21-setup-that-paid-out-at-0751-2hll</link>
      <guid>https://dev.to/arakas4488cmd/the-21-setup-that-paid-out-at-0751-2hll</guid>
      <description>&lt;p&gt;This is one from a bot I run myself, not a client audit — but the same shape shows up often enough in code I review that it's worth writing up on its own. "Backtest looked fine, live looks worse" usually gets diagnosed as slippage or a broken exit. This one wasn't either. The stop-loss and take-profit fired exactly where they were supposed to. The problem was one line above that: what "reward" and "risk" actually meant once real costs were counted.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the setup was supposed to be
&lt;/h2&gt;

&lt;p&gt;The stop-loss was ATR-based — sized to the instrument's recent volatility, not a fixed dollar amount. The take-profit was set to roughly double that distance, a standard 2:1 target. Checking the actual price levels on every trade in the sample against entry, stop, and target confirmed the designed reward:risk ratio really was there in price terms: &lt;strong&gt;≈1.94:1&lt;/strong&gt;, almost exactly as intended. Nothing wrong with the setup on paper.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the fills actually showed
&lt;/h2&gt;

&lt;p&gt;Pulling realized PnL from the broker's own commission reports — not derived, not estimated, read directly off the fills — and comparing it to that same price-based risk and reward told a different story. Average realized win: &lt;strong&gt;$1.57&lt;/strong&gt;, slightly &lt;em&gt;above&lt;/em&gt; the average designed reward of $1.41 — so the win side wasn't the issue, if anything it was doing a little better than planned. The losing side was where the gap showed up: average realized loss exceeded the pure price-based stop-loss risk by &lt;strong&gt;$1.37&lt;/strong&gt; on average (median $1.60), on every losing trade in the sample. That's not a rounding error — it's a consistent, one-directional tax on every loser and none of the winners.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the missing dollars went
&lt;/h2&gt;

&lt;p&gt;Round-trip commission on this setup ran &lt;strong&gt;$0.92–$1.78&lt;/strong&gt; per trade — and that range fully accounts for the gap on its own, no other explanation needed. The mechanism is arithmetic, not behavioral: commission is close to a fixed dollar amount per round trip, regardless of how far price moves. An ATR-based stop keeps the loss side &lt;em&gt;narrow&lt;/em&gt; in dollar terms by design — that's the point of volatility-based sizing. A take-profit twice that distance is correspondingly &lt;em&gt;wide&lt;/em&gt;. The same roughly-fixed commission is a large fraction of a narrow loss and a small fraction of a wide win. It barely touches the winners and eats disproportionately into every loser — quietly dragging the realized ratio down from a designed ~2:1 toward under 1:1, with zero bugs in the stop or target logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the fix isn't the exit logic
&lt;/h2&gt;

&lt;p&gt;The natural first move is to tighten the exits — add a trailing stop, take partial profit earlier, tune the take-profit distance. None of that addresses the actual cause, because the exit levels were never the problem. The three levers that actually move this number:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Widen the stop.&lt;/strong&gt; A wider ATR-based stop makes fewer trades "commission-marginal" — the same fixed cost becomes a smaller fraction of a bigger price-based risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size up per trade.&lt;/strong&gt; The same commission spread over a larger dollar risk has less relative drag — this only works if your risk-per-trade rules have room for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price it in up front.&lt;/strong&gt; Subtract expected round-trip commission from your minimum edge threshold before a signal is approved, instead of finding out about it in the PnL after the fact.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to check this in your own numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pull PnL from actual fills or commission reports — not the backtest's internal accounting — and compare it trade-by-trade to the price-based risk and reward implied by your stop and target. Don't assume they match.&lt;/li&gt;
&lt;li&gt;Compute your round-trip commission in dollars for your typical size, and compare it to your typical stop-loss distance in dollars. If commission is a double-digit percentage of that distance, your realized reward:risk will measurably underperform your designed reward:risk — every time, not occasionally.&lt;/li&gt;
&lt;li&gt;Check whether most backtest engines even model your real commission schedule by default. Plenty default to zero or a flat generic value that has nothing to do with your actual broker's tier — which means the price-based ratio your backtest reports can be correct and still not be the ratio you'll realize.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Nothing about this required a bug. The stop fired where it should have, the target fired where it should have, and the price-based math behind both was right. The gap was entirely in what "reward" and "risk" meant once a mostly-fixed dollar cost got applied to a narrow side and a wide side unevenly. A backtest that only checks prices — not fills, not commission — can be completely honest about the setup and still hand you a ratio you will not actually get.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://honest-backtest.com/blog/reward-risk-commission-gap.html" rel="noopener noreferrer"&gt;Honest Backtest&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The request.security() Value That Isn't There Yet</title>
      <dc:creator>Valerii Sakara</dc:creator>
      <pubDate>Sat, 29 Aug 2026 15:55:13 +0000</pubDate>
      <link>https://dev.to/arakas4488cmd/the-requestsecurity-value-that-isnt-there-yet-6k1</link>
      <guid>https://dev.to/arakas4488cmd/the-requestsecurity-value-that-isnt-there-yet-6k1</guid>
      <description>&lt;p&gt;"Profitable in the Strategy Tester, losing money live" usually gets blamed on slippage. One specific, mechanically checkable cause is worth ruling out first — and it's documented behavior, not a rare edge case.&lt;/p&gt;

&lt;p&gt;This one isn't from a client audit — it's a platform mechanic every Pine Script strategy that pulls in a higher timeframe should be checked against, because it's easy to introduce by accident and TradingView's own documentation explicitly warns about it. If your backtest and your live or paper results have quietly stopped agreeing and you've already ruled out fees and slippage, this is the next thing to check.&lt;/p&gt;

&lt;h2&gt;
  
  
  What request.security() actually does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;request.security()&lt;/code&gt; pulls a value computed on a different timeframe (or symbol) into your script's current context — the standard way to check, say, the 1-hour trend from a 15-minute chart. It takes a &lt;code&gt;lookahead&lt;/code&gt; parameter that controls how it fills in values on historical bars. As of the current Pine Script version, its documented default is &lt;code&gt;barmerge.lookahead_off&lt;/code&gt; — meaning out of the box, it does not leak information from the future into the past.&lt;/p&gt;

&lt;p&gt;The bug shows up when someone explicitly sets &lt;code&gt;lookahead=barmerge.lookahead_on&lt;/code&gt;. People usually do this on purpose, not by accident — it removes what looks like an annoying one-bar lag on the higher-timeframe value, and plenty of forum answers recommend it for exactly that reason. TradingView's own docs carry a direct warning about this setting:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"When using barmerge.lookahead_on without an offset, ensure that it does not compromise the integrity of the script's logic by leaking future data into historical chart bars."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The naive pattern
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//@version=6
strategy("HTF trend filter")
htfClose = request.security(syminfo.tickerid, "60", close,
     lookahead=barmerge.lookahead_on)

if close &amp;gt; htfClose
    strategy.entry("Long", strategy.long)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On historical bars, this compares the current price against the &lt;em&gt;closed&lt;/em&gt; 1-hour value — including, for intraday bars still inside that hour, a close that in a live sense hasn't happened yet. The backtest engine has the full historical record, so it fills in that value without complaint. Every entry signal in the Strategy Tester is quietly using information that wasn't actually available at the moment a live bot would have had to act on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it doesn't show up while you're watching it live
&lt;/h2&gt;

&lt;p&gt;This is the part that makes the bug hard to catch by eyeballing the chart. TradingView's docs describe exactly this: on the currently-forming realtime bar, a lookahead series and a no-lookahead series plot the &lt;em&gt;same&lt;/em&gt; value — there's no future data to leak yet, because the bar hasn't closed. The divergence only appears retroactively, once that realtime bar becomes historical and gets recalculated with the full record available. The docs call this &lt;strong&gt;repainting&lt;/strong&gt;: the value you watched happen live and the value the same bar shows after a reload are two different numbers, and only the second one is what your backtest actually ran on.&lt;/p&gt;

&lt;p&gt;So a live paper-trading session can look like it's confirming the strategy works — the signals you watch fire in real time match what the backtest predicted — right up until you reload the chart and the historical bars quietly repaint into a different story.&lt;/p&gt;

&lt;h2&gt;
  
  
  A second, easy-to-miss variant
&lt;/h2&gt;

&lt;p&gt;The same function has a related but separate gotcha when the requested timeframe is at or below your chart's own resolution — for example, requesting 1-minute data on a 1-minute chart, or any case where the "higher" timeframe you're pulling isn't actually higher. TradingView's docs are specific about what you get back in that case:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"When calling this function to access a lower timeframe, it will evaluate the expression from the LTF context. However, it returns the results from only a single intrabar (LTF bar) on each chart bar."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which intrabar you get depends on the same &lt;code&gt;lookahead&lt;/code&gt; setting — the first intrabar of the period with &lt;code&gt;lookahead_on&lt;/code&gt;, the last with &lt;code&gt;lookahead_off&lt;/code&gt;. It's a different mechanism from the repainting issue above, but the practical effect is the same shape of problem: the value your logic reacts to in a backtest isn't necessarily the value a live bot would have seen at that moment, and the mismatch is invisible unless you go looking for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check in your own script
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Grep every &lt;code&gt;request.security()&lt;/code&gt; call in your script for the &lt;code&gt;lookahead&lt;/code&gt; argument. If it's not set, you're on the safe default. If it's set to &lt;code&gt;barmerge.lookahead_on&lt;/code&gt;, that call needs a second look.&lt;/li&gt;
&lt;li&gt;For any call using &lt;code&gt;lookahead_on&lt;/code&gt;, check whether the requested series has an explicit historical offset (a trailing &lt;code&gt;[1]&lt;/code&gt;) applied. Without one, you're very likely leaking data into your backtest that a live bot would not have had.&lt;/li&gt;
&lt;li&gt;If you're requesting a timeframe at or below your chart's own resolution, confirm which intrabar you're actually getting back and whether that's the one your logic assumes.&lt;/li&gt;
&lt;li&gt;Reload the script after watching a live/realtime session and compare: did any of the values on the most recently closed bars change from what you watched happen? That's the direct, observable symptom of repainting.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;None of this requires an exotic setup — a higher-timeframe trend filter is one of the most common things people add to a strategy, and &lt;code&gt;lookahead_on&lt;/code&gt; is a documented, legitimate parameter with real uses. The failure isn't in wanting the feature; it's that the difference between "correct on historical bars" and "correct at the moment a live bot has to decide" is easy to miss unless you know specifically where to look. It's the same underlying lesson as the audit bugs I've written about before: what a bot's numbers show and what was actually knowable at the time are two separate claims, and a backtest alone won't tell you which one you're looking at.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is one failure shape out of several I check for when a bot's live results stop matching its backtest — I run a technical audit service, &lt;a href="https://honest-backtest.com" rel="noopener noreferrer"&gt;Honest Backtest&lt;/a&gt;, for exactly this kind of gap. Originally published at &lt;a href="https://honest-backtest.com/blog/repainting-request-security.html" rel="noopener noreferrer"&gt;honest-backtest.com/blog/repainting-request-security.html&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>security</category>
      <category>fintech</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Safety Net That Isn't There</title>
      <dc:creator>Valerii Sakara</dc:creator>
      <pubDate>Thu, 13 Aug 2026 23:49:58 +0000</pubDate>
      <link>https://dev.to/arakas4488cmd/the-safety-net-that-isnt-there-1cb</link>
      <guid>https://dev.to/arakas4488cmd/the-safety-net-that-isnt-there-1cb</guid>
      <description>&lt;p&gt;&lt;em&gt;Two more bugs from live trading bot audits — where the protection looked real everywhere except in the one place that mattered.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A stop-loss that fires and a stop-loss that logs "fired" are not the same thing. Neither are a circuit breaker that trips and a circuit breaker that mathematically can't move. Both bugs below share a shape I keep running into: the protective mechanism runs, updates its own bookkeeping, tells the operator it did its job — and never actually reaches the part of the system that would have made it real.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Protective code paths — stop-losses, kill switches, daily loss limits, emergency exits — get exercised far less often than the code that opens positions. An entry function runs on every single trade; a kill switch might fire once in weeks of live running, if ever. That asymmetry means bugs in the protective path survive far longer before anyone notices, because nothing forces them to prove themselves the way constant use forces bugs in the hot path to surface.&lt;/p&gt;

&lt;p&gt;Two live audits from the last few weeks turned up the same underlying failure, in two different disguises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 1: the exit that logs "closed" and stops there
&lt;/h2&gt;

&lt;p&gt;In one bot I reviewed — real capital, real markets — the entry path was solid: build an order, route it through the exchange's real order-submission client, wait for confirmation, record the actual fill. The automatic exit logic (stop-loss, take-profit, and a maximum-holding-time rule) looked, at a glance, like it followed the same pattern. It didn't.&lt;/p&gt;

&lt;p&gt;When an exit condition triggered, the code wrote a trade record tagged &lt;code&gt;SIMULATED&lt;/code&gt;, with a &lt;code&gt;dry_run&lt;/code&gt; flag hardcoded &lt;code&gt;True&lt;/code&gt; regardless of whether the bot was actually running live — then deleted the position from its own tracking table. Nothing in that path built an order. Nothing called the exchange. The function that actually submits and confirms orders — the same one the entry logic used — was never referenced anywhere in the exit code.&lt;/p&gt;

&lt;p&gt;The practical effect: the position that was supposed to be closed is still open, on the real exchange, with real exposure — and the bot no longer knows it exists, because it just deleted its own only record of it. Logs, dashboard, and any alerting all say "closed." The one system that would show otherwise — the exchange itself — was never asked.&lt;/p&gt;

&lt;p&gt;Worth noting: this bot's documentation advertised a fully-built exit system with six named strategies, including an explicit kill-switch meant to "force exit all positions." That code existed, tested, in the repository — just never imported anywhere outside its own test file. A correct, well-tested kill switch that nothing in the live code path ever calls provides exactly as much protection as no kill switch at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 2: the risk limit that's mathematically incapable of triggering
&lt;/h2&gt;

&lt;p&gt;A different bot, a different failure mode, same root shape. This one ran a cross-exchange hedge strategy — open a position on one exchange, immediately hedge it on another — with a daily loss limit meant to halt trading if losses crossed a threshold.&lt;/p&gt;

&lt;p&gt;The loss-limit check itself was fine: it compared an accumulated running total against a configured maximum and blocked new trades if breached. The problem was one level up. The only place in the entire codebase that updated that running total was a single function call after every successful fill — and that call never passed a PnL argument. The function's own signature gave it a default value of zero. Every single trade, win or loss, added exactly &lt;code&gt;0&lt;/code&gt; to the total.&lt;/p&gt;

&lt;p&gt;The running total was therefore mathematically incapable of ever becoming negative enough to trip the limit — not "unlikely to," but structurally unable to, by construction. The same permanently-zero number also fed the bot's own status display, so the operator-facing dashboard read "today's P&amp;amp;L: 0.00" continuously, whether the bot was flat or bleeding — indistinguishable from the outside. This particular strategy carries a specific extra risk: if one leg of the hedge fails to fill while the other goes through, the position is briefly — or not so briefly — directional and unhedged. That's exactly the scenario the loss limit exists to catch, and exactly the scenario it was structurally blind to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this survives so long
&lt;/h2&gt;

&lt;p&gt;Neither of these bugs is subtle once you're looking at the right ten lines. Both survived because nobody was forced to look at those ten lines under real conditions. A stale-price bug fires on every trade — it's loud, eventually, just by volume. A dead kill-switch or a zeroed-out loss counter is quiet by nature: it only needed to work once, in the one moment things went wrong, and by then it's too late to notice the check that should have stopped it never could have.&lt;/p&gt;

&lt;p&gt;Standard unit tests don't reliably catch this either. A unit test that calls the risk function directly with a real loss value would pass fine — the bug isn't in what the function does with a real argument, it's that the one real call site in production never supplies one. That's a wiring problem, not a logic problem, and wiring problems only show up when you trace a value from where it originates to where it's actually used, not when you test each function in isolation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check in your own bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For every stop-loss, kill-switch, or emergency-exit path, trace it to an actual order-submission call — the same one your entry logic uses. If the exit path reaches a different, thinner function (or none), that's worth a hard look.&lt;/li&gt;
&lt;li&gt;For every risk counter that's supposed to accumulate over time (daily P&amp;amp;L, loss streak, drawdown), find every call site that updates it and confirm each one actually passes a real, non-default value — not just that the accumulator logic itself is correct in isolation.&lt;/li&gt;
&lt;li&gt;Don't trust "well-tested" as a proxy for "connected." A correctly implemented safety class that's never imported into the live path is just documentation.&lt;/li&gt;
&lt;li&gt;If you can, deliberately trigger your emergency path once in a controlled setting and confirm — from the exchange's own records, not your bot's logs — that an order actually happened. Your bot's own logs aren't an independent witness; if the bug is that they lie, they'll lie convincingly.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Both of these were live when I found them, not abandoned side projects or paper-trading demos. That's usually the case: the protective code doesn't get skipped, it gets written, tested in isolation, wired in mostly right, and then quietly fails to do the one thing it exists for. I wrote about a related pattern — stale prices undermining stop-losses — in an earlier post; this is the same lesson from a different angle. What a bot logs about its own safety and what its safety actually does are two separate claims, and only one of them is checkable from the outside.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://honest-backtest.com/blog/safety-net-that-isnt-there.html" rel="noopener noreferrer"&gt;Honest Backtest blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>fintech</category>
      <category>cryptocurrency</category>
    </item>
    <item>
      <title>Stale Price Bug: Why Your Stop-Loss Might Not Protect You</title>
      <dc:creator>Valerii Sakara</dc:creator>
      <pubDate>Thu, 13 Aug 2026 23:48:02 +0000</pubDate>
      <link>https://dev.to/arakas4488cmd/stale-price-bug-why-your-stop-loss-might-not-protect-you-2ap1</link>
      <guid>https://dev.to/arakas4488cmd/stale-price-bug-why-your-stop-loss-might-not-protect-you-2ap1</guid>
      <description>&lt;p&gt;&lt;em&gt;A pattern found across three independent trading bots — including one I run myself.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A stop-loss at -7.5% sounds like a hard guarantee. It isn't, unless the price it's measured &lt;em&gt;from&lt;/em&gt; is the price your order actually executed at — not the price the bot glanced at a few seconds, or a few minutes, earlier.&lt;/p&gt;

&lt;p&gt;This gap — between the price a bot &lt;em&gt;decides&lt;/em&gt; on and the price it actually &lt;em&gt;gets&lt;/em&gt; — is one of the most common bugs I run into auditing live trading systems. It's not exotic. It doesn't look like a bug when you read the code casually. It only shows up when you trace one specific value across the full path from signal to fill.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Almost every bot has a moment where it reads a price, makes a decision, and then &lt;em&gt;waits&lt;/em&gt; — for an order to route, a swap to confirm on-chain, a limit order to fill. The wait can be 200ms or it can be five minutes. Whatever price the bot used to set its protective stop-loss/take-profit was captured &lt;em&gt;before&lt;/em&gt; that wait, not after.&lt;/p&gt;

&lt;p&gt;If nothing re-reads the price once the position is actually open, the "protection" is anchored to a number that may no longer describe reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 1: the swap that outran its own stop-loss
&lt;/h2&gt;

&lt;p&gt;In a Solana DEX trading bot I reviewed, the stop-loss/take-profit levels were computed from a candle price captured right before a swap was submitted. The swap itself wasn't instant — it polled for on-chain confirmation inside a nested retry loop, worst case around five minutes between "price read" and "swap actually landed."&lt;/p&gt;

&lt;p&gt;The bot's own swap function &lt;em&gt;knew&lt;/em&gt; the real fill price — it computed and logged it — but only logged it. The function returned a plain &lt;code&gt;True&lt;/code&gt;/&lt;code&gt;False&lt;/code&gt;, so the caller never saw the number and fell back to the stale pre-swap price for the stop-loss reference.&lt;/p&gt;

&lt;p&gt;Concretely: signal at $100 → intended stop at $92.50. While the swap sits in flight, the market drops to $90 and the swap fills there. The bot believes it entered at $100 with $7.50 of room. It actually entered at $90, already most of the way to a stop that was never recalculated from the real entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 2: an argument shift that silently swapped market-close for a slow limit order
&lt;/h2&gt;

&lt;p&gt;A different bug, same root cause — the exit path wasn't tested end-to-end. In a futures bot, the function that closes a position on stop-loss/take-profit had grown new leading parameters over time. The call sites that trigger exits still passed the old, shorter argument list — every argument landed one slot to the right of where it was supposed to.&lt;/p&gt;

&lt;p&gt;The practical effect: a parameter that defaults to &lt;code&gt;100&lt;/code&gt; (truthy) ended up bound to the internal flag that decides whether to close the position immediately at market, or place a passive limit order and slowly chase the price instead. Every stop-loss and take-profit exit fired the slow path — with no configuration change needed to trigger it — exactly in the scenario (a fast move) where an immediate close was the entire point of having a stop-loss.&lt;/p&gt;

&lt;p&gt;Nothing crashed. Nothing logged an error. The log line even printed "stop-loss triggered." The order just didn't behave the way the code around it assumed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 3: it happened in my own bot too
&lt;/h2&gt;

&lt;p&gt;I hold my own systems to the same standard I audit others against, so here's an honest one: in an equity swing-trading bot I run on Interactive Brokers, I found the identical bug class in a live trade. Stop-loss and take-profit were computed from the &lt;em&gt;signal&lt;/em&gt; price, then sent as an atomic bracket order — but the entry used a limit order that could sit unfilled for up to the order timeout window while price kept moving.&lt;/p&gt;

&lt;p&gt;One real trade: signal at $16.87 → SL at $16.77 (10¢ risk), TP at $17.06 (19¢ target). The limit order actually filled at $17.03. Measured from the real entry, risk was 26¢ and the target was 3¢ away — the risk/reward ratio was almost inverted, and the take-profit was nearly already hit at the moment the position opened.&lt;/p&gt;

&lt;p&gt;I didn't catch this by reading the code and spotting it abstractly. I caught it by pulling one specific live trade from the audit log and checking the signal price against the actual fill. That's the only way this class of bug reliably surfaces — code review alone gets you 80% of the way; tracing a real execution gets you the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this keeps happening
&lt;/h2&gt;

&lt;p&gt;None of these are careless bugs. Every one sits in code that's otherwise well-structured, with retry logic, error handling, logging. The gap survives because most testing — and most code review — focuses on the entry/signal logic, where the interesting strategy decisions live. The exit path, where money actually gets protected, gets audited far less rigorously, even though it's the part doing the actual risk management.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check in your own bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Trace the exact variable your stop-loss/take-profit is computed from, from the moment it's read to the moment the protective order is placed. Is anything re-read after a fill, or is it the same value the whole way through?&lt;/li&gt;
&lt;li&gt;If an order function computes a real execution price internally, confirm that price actually gets returned to the caller — not just logged.&lt;/li&gt;
&lt;li&gt;Check every call site of your position-close function for argument order, especially after any signature change. A shifted keyword argument won't raise an exception; it'll just quietly change behavior.&lt;/li&gt;
&lt;li&gt;Pull one real trade from your logs and manually verify signal price → decision → fill price → protective order levels, end to end. Reading the code is not the same as checking what actually happened.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;One of the bugs above — reported through the same review process, in a public repository — was confirmed and merged as a fix within 24 hours: &lt;a href="https://github.com/0xfnzero/sol-trade-sdk/pull/112" rel="noopener noreferrer"&gt;github.com/0xfnzero/sol-trade-sdk/pull/112&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://honest-backtest.com/blog/stale-price-bug.html" rel="noopener noreferrer"&gt;Honest Backtest blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
