Skip to content

v4.4.54 - Per-contract trading fee accounting#974

Merged
grzesir merged 1 commit intodevfrom
version/4.4.54
Mar 9, 2026
Merged

v4.4.54 - Per-contract trading fee accounting#974
grzesir merged 1 commit intodevfrom
version/4.4.54

Conversation

@grzesir
Copy link
Contributor

@grzesir grzesir commented Mar 9, 2026

What

Adds per-contract fee support to TradingFee and applies it in backtesting trade-cost calculations so option commission modeling matches broker per-contract pricing.

Why

Backtests were undercharging commissions for strategies that model option fees as per-contract costs (for example, $0.65/contract).

Risk

Low-to-moderate: affects commission calculations and therefore PnL metrics in backtests where per_contract_fee is configured. No impact when it is left at default 0.

Tests run

  • python3 -m pytest tests/test_tradingfee.py -q

Notes

  • Includes changelog updates for 4.4.54.

Summary by CodeRabbit

Release Notes - Version 4.4.54

  • New Features

    • Added per-contract fee support for more flexible fee configuration.
  • Changed

    • Fee fields now use Decimal for improved decimal precision and stability.
  • Bug Fixes

    • Fixed backtesting to correctly apply per-contract fees scaled by order quantity.
  • Tests

    • Added regression tests for per-contract fee initialization and trade-cost calculations.

@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93e989f5-ea98-450b-a8ce-7cebbe020d33

📥 Commits

Reviewing files that changed from the base of the PR and between 593b5bd and 721fab1.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • lumibot/backtesting/backtesting_broker.py
  • lumibot/entities/trading_fee.py
  • tests/test_tradingfee.py

📝 Walkthrough

Walkthrough

This PR introduces per_contract_fee support to TradingFee, enabling per-unit costs for contracts like options. Decimal coercion is standardized across all fee fields. Backtesting broker applies per_contract_fee scaled by order quantity alongside existing flat and percent fees. Comprehensive regression tests validate the new calculations.

Changes

Cohort / File(s) Summary
Per-Contract Fee Implementation
lumibot/entities/trading_fee.py, lumibot/backtesting/backtesting_broker.py
Added per_contract_fee parameter to TradingFee with Decimal(str(...)) coercion; standardized flat_fee and percent_fee to use same Decimal pattern. BacktestingBroker now includes per_contract_fee scaled by quantity in trade cost calculations for both taker and maker order paths.
Tests & Documentation
tests/test_tradingfee.py, CHANGELOG.md
Added comprehensive regression tests validating per_contract_fee calculations in isolation and combined with flat fees across various order types. Documented feature in 4.4.54 release notes with examples of per-contract fee initialization and trade-cost application.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes


A rabbit hops with glee,
New fees per contract, now you'll see!
Decimals precise, calculations bright,
Options trading done just right! 🐰✨

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch version/4.4.54

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Pylint (4.0.5)
lumibot/entities/trading_fee.py

************* Module pylintrc
pylintrc:1:0: F0011: error while parsing the configuration: File contains no section headers.
file: 'pylintrc', line: 1
'known-third-party=lumibot' (config-parse-error)
[
{
"type": "convention",
"module": "lumibot.entities.trading_fee",
"obj": "",
"line": 12,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "lumibot/entities/trading_fee.py",
"symbol": "line-too-long",
"message": "Line too long (114/100)",
"message-id": "C0301"
},
{
"type": "convention",
"module": "lumibot.entities.trading_fee",
"obj": "",
"line": 14,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "lumibot/entities/trading_fee.py",
"symbol": "line-too-long",
"message": "Line too long (140/100)",
"message-id": "C0301"
},
{
"type": "convention",
"module": "lumibot.en

... [truncated 686 characters] ...

13"
},
{
"type": "refactor",
"module": "lumibot.entities.trading_fee",
"obj": "TradingFee.init",
"line": 7,
"column": 4,
"endLine": 7,
"endColumn": 16,
"path": "lumibot/entities/trading_fee.py",
"symbol": "too-many-positional-arguments",
"message": "Too many positional arguments (6/5)",
"message-id": "R0917"
},
{
"type": "refactor",
"module": "lumibot.entities.trading_fee",
"obj": "TradingFee",
"line": 4,
"column": 0,
"endLine": 4,
"endColumn": 16,
"path": "lumibot/entities/trading_fee.py",
"symbol": "too-few-public-methods",
"message": "Too few public methods (0/2)",
"message-id": "R0903"
}
]

tests/test_tradingfee.py

************* Module pylintrc
pylintrc:1:0: F0011: error while parsing the configuration: File contains no section headers.
file: 'pylintrc', line: 1
'known-third-party=lumibot' (config-parse-error)
[
{
"type": "convention",
"module": "tests.test_tradingfee",
"obj": "",
"line": 33,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "tests/test_tradingfee.py",
"symbol": "line-too-long",
"message": "Line too long (103/100)",
"message-id": "C0301"
},
{
"type": "convention",
"module": "tests.test_tradingfee",
"obj": "",
"line": 1,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "tests/test_tradingfee.py",
"symbol": "missing-module-docstring",
"message": "Missing module docstring",
"message-id": "C0114"
},
{
"type": "convention",
"module": "tests.test_tradingfee",

... [truncated 3502 characters] ...

"line": 76,
    "column": 8,
    "endLine": 76,
    "endColumn": 76,
    "path": "tests/test_tradingfee.py",
    "symbol": "import-outside-toplevel",
    "message": "Import outside toplevel (lumibot.backtesting.backtesting_broker.BacktestingBroker)",
    "message-id": "C0415"
},
{
    "type": "convention",
    "module": "tests.test_tradingfee",
    "obj": "TestPerContractFeeCalculation.test_old_flat_fee_behavior_unchanged",
    "line": 88,
    "column": 8,
    "endLine": 88,
    "endColumn": 76,
    "path": "tests/test_tradingfee.py",
    "symbol": "import-outside-toplevel",
    "message": "Import outside toplevel (lumibot.backtesting.backtesting_broker.BacktestingBroker)",
    "message-id": "C0415"
}

]

lumibot/backtesting/backtesting_broker.py

************* Module pylintrc
pylintrc:1:0: F0011: error while parsing the configuration: File contains no section headers.
file: 'pylintrc', line: 1
'known-third-party=lumibot' (config-parse-error)
[
{
"type": "convention",
"module": "lumibot.backtesting.backtesting_broker",
"obj": "",
"line": 20,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "lumibot/backtesting/backtesting_broker.py",
"symbol": "line-too-long",
"message": "Line too long (149/100)",
"message-id": "C0301"
},
{
"type": "convention",
"module": "lumibot.backtesting.backtesting_broker",
"obj": "",
"line": 154,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "lumibot/backtesting/backtesting_broker.py",
"symbol": "line-too-long",
"message": "Line too long (108/100)",
"message-id": "C0301"
},
{
"type": "c

... [truncated 175044 characters] ...

   "module": "lumibot.backtesting.backtesting_broker",
    "obj": "",
    "line": 19,
    "column": 0,
    "endLine": 19,
    "endColumn": 81,
    "path": "lumibot/backtesting/backtesting_broker.py",
    "symbol": "unused-import",
    "message": "Unused TradingFee imported from lumibot.entities",
    "message-id": "W0611"
},
{
    "type": "warning",
    "module": "lumibot.backtesting.backtesting_broker",
    "obj": "",
    "line": 20,
    "column": 0,
    "endLine": 20,
    "endColumn": 149,
    "path": "lumibot/backtesting/backtesting_broker.py",
    "symbol": "unused-import",
    "message": "Unused build_price_ladder imported from lumibot.tools.smart_limit_utils",
    "message-id": "W0611"
}

]

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@grzesir grzesir merged commit 99fcfa6 into dev Mar 9, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant