Skip to content

almost finished schwab, testing #742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Instructions for Copilot

Important: remember to add unit tests for any new functionality you add.
This is mission critical code, so we need to ensure that it works as expected and doesn't break anything.

You can find the tests in the `tests` directory and be compatible with pytest.
You can run the tests with `pytest` and check the coverage with `pytest --cov`.

We need a high level of test coverage, so please make sure to add tests for any new functionality you add. Additionally, ensure that all tests are well-documented and follow best practices.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ data/SRNE_Minute.csv
*.tmp
.env
token.json
schwab_token.json
367 changes: 292 additions & 75 deletions docsrc/brokers.schwab.rst

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions docsrc/entities.chains.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
Chains Entity
=============

``Chains`` represents the complete option-chain snapshot returned by
``Strategy.get_chains()``. It behaves like a mapping but adds convenience
helpers so you can focus on trading logic instead of dictionary plumbing.

At a glance
-----------

.. code-block:: python

spy_chains = self.get_chains(Asset("SPY")) # -> Chains

# Quick introspection
print(spy_chains) # <Chains exchange=SMART multiplier=100 expirations=30 calls=5000 puts=5000>

# Basic data access -------------------------------------------------
expiries = spy_chains.expirations() # list[str] of CALL expirations
put_dates = spy_chains.expirations("PUT") # list[str] of PUT expirations
strikes_atm = spy_chains.strikes(expiries[0]) # strikes list for first expiry, CALL side by default

# Calculate an at-the-money strike
underlying_px = self.get_last_price("SPY")
atm = min(strikes_atm, key=lambda s: abs(s - underlying_px))

# Pick the matching PUT strike for a credit spread
put_strikes = spy_chains.strikes(expiries[0], "PUT")
otm_put = max(s for s in put_strikes if s < underlying_px)

# Build an Asset object
contract = Asset(
symbol="SPY",
asset_type=Asset.AssetType.OPTION,
expiration=expiries[0],
strike=otm_put,
right=Asset.OptionRight.PUT,
)

Design notes
------------

* ``Chains`` **inherits from** ``dict`` so any existing code that used the raw
mapping (``chains["Chains"]["PUT"]`` …) keeps working.
* All helper methods return lightweight Python types – no Pandas dependency.
* Attribute summary:

======== ===============================================================
Property Description
======== ===============================================================
``calls`` Mapping ``{expiry: [strike, …]}`` for CALLs
``puts`` Mapping ``{expiry: [strike, …]}`` for PUTs
``multiplier`` Contract multiplier (typically 100)
``exchange`` Primary routing exchange returned by the API
======== ===============================================================

API reference
-------------

.. autoclass:: lumibot.entities.chains.Chains
:members:
:undoc-members:
:show-inheritance:
3 changes: 2 additions & 1 deletion docsrc/entities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ Here's a list of the main entities in Lumibot:
entities.data
entities.order
entities.position
entities.trading_fee
entities.trading_fee
entities.chains
11 changes: 9 additions & 2 deletions lumibot/backtesting/backtesting_broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,17 @@ def cash_settle_options_contract(self, position, strategy):
profit_loss = 0 # Short position can't gain more than the cash collected

# Add the profit/loss to the cash position
new_cash = strategy.get_cash() + profit_loss
current_cash = strategy.get_cash()
if current_cash is None:
# self.strategy.logger.warning("strategy.get_cash() returned None during cash_settle_options_contract. Defaulting to 0.")
current_cash = Decimal(0)
else:
current_cash = Decimal(str(current_cash)) # Ensure it's Decimal

new_cash = current_cash + Decimal(str(profit_loss))

# Update the cash position
strategy._set_cash_position(new_cash)
strategy._set_cash_position(float(new_cash)) # _set_cash_position expects float

# Set the side
if position.quantity > 0:
Expand Down
Loading
Loading