<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Sergei Algo]]></title><description><![CDATA[Sergei Algo]]></description><link>https://sergeialgo.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 29 Oct 2026 04:34:34 GMT</lastBuildDate><atom:link href="https://sergeialgo.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A Good Fill Price. What About the Fee?]]></title><description><![CDATA[A limit order. You bought at exactly the price you specified, or even a little cheaper. So far, so good. But there is another line worth checking: what did…]]></description><link>https://sergeialgo.hashnode.dev/a-good-fill-price-what-about-the-fee</link><guid isPermaLink="true">https://sergeialgo.hashnode.dev/a-good-fill-price-what-about-the-fee</guid><category><![CDATA[trading, ]]></category><category><![CDATA[Backtesting]]></category><category><![CDATA[Cryptocurrency]]></category><dc:creator><![CDATA[Sergei B]]></dc:creator><pubDate>Tue, 27 Oct 2026 12:54:58 GMT</pubDate><content:encoded><![CDATA[<p>A limit order. You bought at exactly the price you specified, or even a little cheaper. So far, so good. But there is another line worth checking: what did the exchange charge for that fill?</p>
<p>You can get a good price and still pay the taker fee. There is no contradiction.</p>
<p>I develop software for automated trading. In the comments on my <a href="https://dev.to/sergei_b/a-good-price-is-not-yet-a-trade-1mab">previous article</a>, a reader corrected our explanation: a fill better than the limit can mean that the order took an offer already resting in the book. I agreed. Then I wanted to check the next question: what if the fill is not better, but exactly at the limit? How often do we still pay as takers?</p>
<p>For a bot, this is more than a terminology issue. It buys and sells repeatedly. Get the fee assumption wrong in a backtest, and the same mistake is repeated with every trade.</p>
<h2>I Got My Price. Why Am I the Taker?</h2>
<p>Suppose I am willing to buy at no more than 100. A seller is already offering 99 in the order book, the list of other participants' orders. My limit order buys from that seller at 99.</p>
<p>The price is fine: I was willing to pay up to 100 and got 99. But I did not post an order and wait for someone to trade against it. I traded against someone who was already there. That makes me the taker in this fill. The maker was the participant whose offer was waiting for me.</p>
<p>Now replace 99 with 100. The seller is already offering 100, and I buy at 100. The fill matches my limit, but my role has not changed. I am still taking an existing offer.</p>
<p><strong>The limit says, "Don't pay more than this." It does not say, "Charge me the lower fee."</strong></p>
<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ryjtjlqo5zul6ca2dszt.jpg" alt="A limit caps the price, not the fee: buying from a seller already in the book at 99 or at 100 makes you the taker either way" style="display:block;margin:0 auto" />

<h2>Instead of Debating the Terms, Check the Records</h2>
<p>I took two exports from live accounts running our bots: Bybit perpetual futures, from August 29 to the morning of October 25, 2026, in UTC. Not demo trades or a backtest.</p>
<p>They contain 6,237 individual trade executions. Of these, 327 came from market orders and 5,910 from limit orders. The latter are what matters here. One order could fill in several parts, so we are counting fills, not orders.</p>
<p>The sources differ slightly. In the first, the exchange records each fill's role with the <code>isMaker</code> field. The second is a spreadsheet export without that flag, so I classified fills by their fee rate. Only two rates appeared: the account's 0.036% maker rate and 0.1% taker rate. That is a fee-based classification, not an explicit exchange flag. On the first account I also checked the fee rate against the exchange flag: there were no mismatches.</p>
<p>For a buy, a price below the limit was better; for a sell, a price above it was better. I compared against the order price in the exchange record, not an assumed market quote.</p>
<table>
<thead>
<tr>
<th align="left">Fill price</th>
<th align="right">Maker</th>
<th align="right">Taker</th>
<th align="right">Taker share</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Better than limit</td>
<td align="right">0</td>
<td align="right">1,218</td>
<td align="right">100%</td>
</tr>
<tr>
<td align="left">At the limit</td>
<td align="right">1,257</td>
<td align="right">3,435</td>
<td align="right">73.21%</td>
</tr>
<tr>
<td align="left"><strong>All limit-order fills</strong></td>
<td align="right"><strong>1,257</strong></td>
<td align="right"><strong>4,653</strong></td>
<td align="right"><strong>78.73%</strong></td>
</tr>
</tbody></table>
<p>All 1,218 fills better than the limit were classified as taker under these two methods. But here is the row I wanted to investigate: of the 4,692 fills exactly at the limit, 3,435 were taker fills. Roughly three out of four.</p>
<p>The accounts show a similar pattern separately: 72.57% on the first, using the exchange's explicit flag, and 73.88% on the second, using fee-rate classification.</p>
<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/h9pgvany724x3or9vexl.jpg" alt="Fills exactly at the limit: 73.21% taker, 26.79% maker; all 1,218 fills better than the limit were taker" style="display:block;margin:0 auto" />

<p>That is not a proportion for bots in general. It is what we found on two accounts over this period. The exports do not explain exactly how the bots produced that mix: they contain no order-book snapshot from when an order was submitted.</p>
<h2>Now for the Money</h2>
<p>To calculate the average fee, I added the trading fees paid and divided by the total traded value of purchases and sales in USDT. Not by the deposit or the number of trades: a small fill should not carry the same weight as a large one.</p>
<table>
<thead>
<tr>
<th align="left">Included executions</th>
<th align="right">Average fee on executed turnover</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Limit-order fills only</td>
<td align="right">0.07918%</td>
</tr>
<tr>
<td align="left">All fills, including market orders</td>
<td align="right">0.08022%</td>
</tr>
</tbody></table>
<p>There is real money behind those small percentages. The exchange charged <strong>483.56 USDT</strong> for the two accounts' limit-order fills. Apply the 0.036% maker rate to exactly the same turnover and the bill would be <strong>219.85 USDT</strong>. The difference is <strong>263.71 USDT</strong>.</p>
<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/qgiasv7113xrngawn2sd.jpg" alt="Fees on limit-order fills: 483.56 USDT charged vs 219.85 USDT at the maker rate on the same turnover, a 263.71 USDT difference" style="display:block;margin:0 auto" />

<p>This is why the check matters. Seeing Limit in a bot's settings is not enough reason to put the maker rate into a backtest. Otherwise, the savings exist only on paper.</p>
<p>This compares fees on fills that already happened. It is not a loss of account capital or the result of another backtest. A different order-placement method could also change which trades happened.</p>
<p>Our earlier backtests used 0.06%, not 0.036%. The maker-only assumption here is a hypothetical example. Those rates also apply to the accounts examined; another account's rates need to be checked separately.</p>
<h2>Why Not Just Rule Out Taker Fills?</h2>
<p>That is what Post-Only is for. On Bybit, such an order is cancelled if it would immediately take an existing offer when placed. It does not become a cheaper filled order: instead of a purchase, you may get a cancellation.</p>
<p>Trying again requires another order. Meanwhile, the market may move away. So "turn on Post-Only and keep the same trades at lower fees" is still a claim to test. The comparison needs prices, partial fills and trades that never happened, not just the fee rate.</p>
<p>I did not test that change here. We counted how the existing orders filled, rather than establishing that they should be placed differently.</p>
<p>You can check your own account without another backtest: take the execution export, check the roles and rates, add the trading fees and divide by the total traded value. Choosing between ordinary limit orders and Post-Only takes a separate test.</p>
<p>A good fill price is still a good fill price. Execution just has two separate characteristics: the price you got and what the exchange charged. One does not tell you the other.</p>
<p>The limit order did its job: it capped the price. It never promised the maker fee.</p>
<p><em>Method note: funding, the separate payments associated with holding perpetual positions, was excluded. The exports do not reconstruct order-revision history or time spent in the queue. No fills worse than the recorded limit were found; that is a consistency check, not evidence of zero slippage relative to the market. Bybit documents</em> <code>isMaker</code> <em>in its</em> <a href="https://bybit-exchange.github.io/docs/v5/order/execution"><em>execution-history reference</em></a> <em>and Post-Only in its</em> <a href="https://www.bybit.com/en/help-center/article/Post-Only-Order"><em>help centre</em></a><em>.</em></p>
<p><em>Prepared with AI assistance; all figures checked against exchange records.</em></p>
<p><em>Originally published on</em> <a href="https://dev.to/sergei_b/a-good-fill-price-what-about-the-fee-41mh"><em>DEV Community</em></a><em>.</em></p>
]]></content:encoded></item><item><title><![CDATA[The backtest used a 0.06% fee. What was my account actually paying?]]></title><description><![CDATA[I develop trading bots. Our historical tests used a fee of 0.06% per side. When I checked the executions from one of my live accounts, though, the…]]></description><link>https://sergeialgo.hashnode.dev/the-backtest-used-a-0-06-fee-what-was-my-account-actually-paying</link><guid isPermaLink="true">https://sergeialgo.hashnode.dev/the-backtest-used-a-0-06-fee-what-was-my-account-actually-paying</guid><category><![CDATA[Backtesting]]></category><category><![CDATA[trading, ]]></category><category><![CDATA[Cryptocurrency]]></category><dc:creator><![CDATA[Sergei B]]></dc:creator><pubDate>Tue, 27 Oct 2026 06:30:00 GMT</pubDate><content:encoded><![CDATA[<p>I develop trading bots. Our historical tests used a fee of 0.06% per side. When I checked the executions from one of my live accounts, though, the volume-weighted fee rate actually paid was about 0.0801%.</p>
<p>That looks like a small difference. But fees apply to every fill, and a bot can generate a lot of them. I wanted to answer two questions: where did the different rate come from, and how much would it change one backtest?</p>
<h2>What the account actually paid</h2>
<p>I took a Bybit crypto futures execution export covering August 29 to October 25, 2026. It contained 3,204 trade executions. Those are fills, not completed trades: a single order can fill in several parts.</p>
<p>The executed notional was about 323,731.67 USDT. Trading fees totalled about 259.44 USDT.</p>
<p>To calculate the average rate, I divided the fees paid by the executed notional:</p>
<p><strong>259.44 / 323,731.67 × 100% ≈ 0.0801%.</strong></p>
<p>Funding payments for holding futures positions were excluded from that calculation. I used the fees charged on exchange executions, rather than adding up selected fee-related fields in the bot's database.</p>
<p>This account had two rates: 0.036% for maker fills and 0.1% for taker fills. Maker executions add liquidity to the order book; taker executions remove it. Roughly 31% of the executed notional was maker and 69% taker. That mix produced the average fee rate.</p>
<p>A limit order does not necessarily pay the maker rate. If it executes immediately against orders already resting in the book, it can be a taker execution.</p>
<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/0cwizw3j27n780mkdr73.png" alt="One Bybit account's fee schedule and its separately calculated volume-weighted fee rate" style="display:block;margin:0 auto" />

<p><em>Unchanged excerpt from my Bybit fee page, English interface, October 26, 2026: futures, standard crypto, Regular User. The average rate below was calculated separately from executions, August 29 – October 25. MNT discount display is off.</em></p>
<h2>One account is not the whole exchange</h2>
<p>Bybit says that rates may vary by region and VIP level, and directs users to their own fee page after identity verification. <a href="https://www.bybit.com/en/help-center/article/Trading-Fee-Structure">Bybit's fee schedule and explanation</a>.</p>
<p>One more detail. Bybit's fee page shows rates in two places. The "My fee level" card for futures shows 0.036% maker and 0.1% taker for my account — the same rates recorded on the trade executions in the period I checked. But the general fee table in the English interface lists 0.02% and 0.055% for the same "Regular User" tier. It is easy to look at the wrong place, which is why I check my account's rate against executions.</p>
<p>My result does not mean that every customer pays 0.0801%. Another account, with a different fee schedule and execution mix, may pay less.</p>
<p>Freqtrade had not calculated some incorrect global average, either. We had explicitly supplied 0.06%, and it used that value. Custom backtest fees are documented in the <a href="https://www.freqtrade.io/en/2026.8/backtesting/#supplying-custom-fee-value">Freqtrade manual</a>. The question was whether the assumption matched the account, not whether the software could do the arithmetic.</p>
<h2>One strategy, three fee rates</h2>
<p>For the experiment, I used one strategy, a starting balance of 2,500 USDT and the same historical dataset with one-minute detail. The window ran from January 1, 2024 to December 31, 2024 at 00:00 UTC. The final day of December was not included.</p>
<p>Only the fee changed between scenarios. The 0.08% rate approximated the measured average for my account. I also tested 0.044% as a lower-cost scenario, not as a claim about the rate paid by everyone else. That is roughly what the same 31/69 split gives under Bybit's base futures table: 0.02% maker and 0.055% taker.</p>
<table>
<thead>
<tr>
<th>Fee per side</th>
<th align="right">Backtest return</th>
<th align="right">Difference from the baseline</th>
<th align="right">Closed trades</th>
</tr>
</thead>
<tbody><tr>
<td>0.044%</td>
<td align="right">+175.94%</td>
<td align="right">+60.82 USDT</td>
<td align="right">4,070</td>
</tr>
<tr>
<td>0.06%</td>
<td align="right">+173.51%</td>
<td align="right">Baseline</td>
<td align="right">3,936</td>
</tr>
<tr>
<td>0.08%</td>
<td align="right">+156.29%</td>
<td align="right">−430.34 USDT</td>
<td align="right">3,790</td>
</tr>
</tbody></table>
<p>Return here means the test's final profit divided by its starting balance. The USDT column is the difference between final backtest results, not simply the extra fees charged. These percentages describe the stated historical window; they are not a forecast.</p>
<p>I also reran the original 0.06% case to check the baseline itself. All 3,936 trade records matched the old result exactly, including the nested order records.</p>
<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/vclxk3zgqw8n90tvey5v.png" alt="Returns and closed-trade counts for three backtests with different fee assumptions" style="display:block;margin:0 auto" />

<p><em>All bars start at zero. The balance, data and settings were unchanged; only the flat fee changed. These are historical backtest returns, not live-account returns.</em></p>
<p>For completeness, the engine-reported drawdowns were 10.38%, 11.11% and 10.85%, respectively. That is its <code>max_drawdown_account</code> metric, not a separately reconstructed mark-to-market drawdown including all open positions. I am not using those three figures as a measure of the strategy's full risk.</p>
<h2>Why subtracting extra fees was not enough</h2>
<p>On a fixed list of fills, changing the fee rate gives a straightforward cost recalculation. But that answers a narrow question: what would those same fills have cost at another rate?</p>
<p>In our reruns, the trade set changed too: one scenario produced 3,790 closed trades and another 4,070.</p>
<p>So, in this experiment, subtracting a different fee bill from the old result would not have reproduced the new run. I did not trace why each individual trade changed here. The summary figures therefore do not establish, for example, that trades all closed later.</p>
<h2>What to check in your own test</h2>
<p>Start with the fee schedule for your account and instrument. Then check the fees actually charged on executions and compare them with the rate supplied to the backtest. Weight the average by executed notional; do not simply average the maker and taker rates.</p>
<p>If the rates differ, keep two checks separate: repricing the old fill list and rerunning the strategy. They answer different questions.</p>
<p>This experiment has limits. It covers one strategy and one historical window. Each run used a flat fee rather than a maker/taker classification for every simulated fill. The 0.08% scenario therefore does not recreate my account in every detail. It did not test slippage or order-book queue position. The strategy code is private, so readers cannot fully reproduce these particular runs without it.</p>
<p>The practical conclusion is specific: <strong>a 0.06% assumption is not automatically wrong because my account pays more, and my account's rate is not universal. What matters is whether the test's assumptions match the conditions of the account.</strong></p>
<p><em>Prepared with AI assistance; all figures checked against exchange records and backtest outputs.</em></p>
<p><em>Originally published on</em> <a href="https://dev.to/sergei_b/the-backtest-used-a-006-fee-what-was-my-account-actually-paying-flb"><em>DEV Community</em></a> <em>on October 26, 2026.</em></p>
]]></content:encoded></item><item><title><![CDATA[What we check before a trading machine starts]]></title><description><![CDATA[Before a PelagiQuant machine starts, the launch page runs a few checks. Some can stop the launch. Others are shown as advice, and the decision stays with…]]></description><link>https://sergeialgo.hashnode.dev/what-we-check-before-a-trading-machine-starts</link><guid isPermaLink="true">https://sergeialgo.hashnode.dev/what-we-check-before-a-trading-machine-starts</guid><category><![CDATA[algorithmic trading]]></category><category><![CDATA[crypto]]></category><category><![CDATA[risk management]]></category><category><![CDATA[trading bots]]></category><dc:creator><![CDATA[Sergei B]]></dc:creator><pubDate>Thu, 22 Oct 2026 10:54:44 GMT</pubDate><content:encoded><![CDATA[<p>Before a PelagiQuant machine starts, the launch page runs a few checks. Some can stop the launch. Others are shown as advice, and the decision stays with you.</p>
<p>For closed beta participants, machines currently run only in demo mode, so the live-account checks in part 3 don't apply to them yet.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ad9d3570284f2dbd6b823d5/a80753f1-a9ef-4ce8-8052-d6e4379cb32b.png" alt="The launch page in demo mode. Personal details are blurred." style="display:block;margin:0 auto" />

<h2>1. We check the amount you chose</h2>
<p>You choose how much the machine will trade with. The server checks whether that amount fits the selected model and confirms the amount and the size of the first entry. Until then, the launch doesn't go ahead.</p>
<h2>2. The market colour</h2>
<p>The page shows the current market state as a colour: normal, heightened caution, unfavourable, or "data is stale". This is advice, not a ban. If the market is unfavourable, you're asked to confirm that you understand the risk before launching.</p>
<p>We call this layer Launch Guard. We explain what it covers, but keep its detailed rules and thresholds private.</p>
<h2>3. The API key and the account (live mode)</h2>
<p>A live machine trades on your own exchange account through an API key. Before it starts, the page checks six things:</p>
<ul>
<li><strong>Withdrawals are disabled on the key.</strong> A key that allows withdrawals is refused.</li>
<li><strong>The balance covers the deposit you chose.</strong> If the account holds less, the machine doesn't start.</li>
<li><strong>The key is bound to an IP address.</strong> IP restrictions limit where the key can be used. Without one, you get a warning, not a block.</li>
<li><strong>The key isn't already used by another machine.</strong> In PelagiQuant, one API key can be assigned to one machine at a time.</li>
<li><strong>No open positions in this machine's coins.</strong></li>
<li><strong>No open orders in this machine's coins.</strong></li>
</ul>
<p>Positions or orders in <em>other</em> coins lead to a separate confirmation. The machine won't touch them, but they use the same funds in the account, and you're asked to confirm that you understand this before launching.</p>
<h2>Checks first, payment second</h2>
<img src="https://cdn.hashnode.com/uploads/covers/6ad9d3570284f2dbd6b823d5/b00d5942-6df9-417e-a19c-a1e9d9027433.png" alt="Checks first, payment second: the planned order of steps. The paid version is not open yet." style="display:block;margin:0 auto" />

<p>The paid version isn't open yet. The planned flow is simple: you choose a model and the capital it will trade with, we run the launch checks, and you review the results and confirm. Only then do we ask for payment.</p>
<p>After payment, we run the checks again. Your subscription period starts when the machine has actually started and the first records of its operation appear in its log. The wait between payment and the confirmed start doesn't count toward your subscription.</p>
<p>If a blocking check fails before payment, we don't issue an invoice. We'd rather wait than charge you for a machine that isn't ready to start.</p>
<p>Your trading capital stays in your own exchange account. You don't transfer it to PelagiQuant. We accept only API keys with withdrawals disabled.</p>
<h2>What a passed check means</h2>
<p>The checks help find problems before the first order, but they can't tell in advance whether the next trade will be profitable. The market keeps changing after launch, and leveraged futures trading can lose money.</p>
<hr />
<p>I work on <a href="https://pelagiquant.com">PelagiQuant</a>.</p>
]]></content:encoded></item></channel></rss>