Add butterfly, iron condor, and covered strategies#73
Conversation
… strategies New strategies implemented: - Long/short call butterfly (3-leg) - Long/short put butterfly (3-leg) - Iron condor and reverse iron condor (4-leg) - Iron butterfly and reverse iron butterfly (4-leg) - Covered call (synthetic, 2-leg) - Protective put (synthetic, 2-leg) Key changes: - Extended core.py to support 3+ leg strategies with proper column renaming - Added quantity multipliers per leg for butterfly strategies (1:2:1 ratio) - Added new rules for butterfly and iron condor strike ordering validation - Updated definitions.py with correct column definitions for multi-leg strategies - Added comprehensive test coverage with new multi-strike test fixture https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX
Updated tests to verify actual P&L calculations: - Long call butterfly: entry=0.40, exit=0.05, pct=-0.88 (at 210/212.5/215) - Long put butterfly: entry=0.40, exit=0.0, pct=-1.0 (puts expired worthless) - Iron condor: entry=-1.90 (credit), exit=-0.025, pct=0.99 (kept premium) - Iron butterfly: entry=-5.00 (credit), exit=-2.475, pct=0.50 - Covered call: entry=1.50, exit=2.45, pct=0.63 - Protective put: entry=8.40, exit=7.525, pct=-0.10 https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX
Added documentation for: - Butterfly strategies (long/short call/put butterfly) - Iron condor and reverse iron condor - Iron butterfly and reverse iron butterfly - Covered call and protective put (synthetic) Also updated pip install version to 2.0.3. https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX
There was a problem hiding this comment.
Pull request overview
This PR adds support for 10 new multi-leg options strategies to expand the library's capabilities beyond existing single and two-leg strategies. The new strategies include butterfly spreads (3 legs), iron condors and iron butterflies (4 legs), and synthetic covered positions.
Changes:
- Added 10 new strategy functions: 4 butterfly strategies, 4 iron condor/butterfly strategies, and 2 covered strategies
- Enhanced the core engine to support leg quantities and improved multi-leg merging logic
- Added validation rules for butterfly and iron condor strike configurations
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| optopsy/strategies.py | Implements all 10 new strategy functions with helper functions to reduce code duplication |
| optopsy/core.py | Enhances strategy engine to support leg quantities and fixes column naming issues for 3+ leg strategies |
| optopsy/rules.py | Adds strike validation rules for butterfly, iron condor, and iron butterfly strategies |
| optopsy/definitions.py | Updates column definitions for 3-leg and 4-leg strategies to use consistent naming |
| optopsy/init.py | Exports all new strategy functions and csv_data |
| tests/test_strategies.py | Adds comprehensive tests for all new strategies covering raw and aggregated outputs |
| tests/conftest.py | Adds multi_strike_data fixture with 5 equidistant strikes for testing multi-leg strategies |
| README.md | Updates documentation to list all supported strategies by leg count and version number |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1. Fix lambda late binding in _apply_ratios (core.py)
- Capture entry_col and exit_col as default arguments to avoid
late binding issues in lambda functions
2. Add dte_range to triple/quadruple strike internal columns (definitions.py)
- Restores dte_range field that was present in original code
- Provides users with DTE range information in raw output
https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
|
|
||
| def _get_leg_quantity(leg: Tuple) -> int: | ||
| """Get quantity for a leg, defaulting to 1 if not specified.""" |
There was a problem hiding this comment.
The function docstring should document the expected structure of the leg tuple parameter, including that element [2] represents quantity and what elements [0] and [1] represent for clarity.
| """Get quantity for a leg, defaulting to 1 if not specified.""" | |
| """ | |
| Get quantity for a leg, defaulting to 1 if not specified. | |
| The leg tuple is expected to have the form ``(side, spec, quantity)``: | |
| - ``leg[0]``: The side/ratio object for the leg (for example, an enum or | |
| similar object whose ``.value`` is later used as a price multiplier). | |
| - ``leg[1]``: Leg-specific metadata or specification (such as the option | |
| type or other selector used elsewhere in the evaluation pipeline). | |
| - ``leg[2]``: (Optional) Integer quantity for this leg. If this element is | |
| omitted, a default quantity of ``1`` is assumed. | |
| """ |
| def _rename_leg_columns( | ||
| data: pd.DataFrame, leg_idx: int, join_on: List[str] | ||
| ) -> pd.DataFrame: | ||
| """Rename columns with leg suffix, excluding join columns.""" | ||
| rename_map = { | ||
| col: f"{col}_leg{leg_idx}" for col in data.columns if col not in join_on | ||
| } | ||
| return data.rename(columns=rename_map) |
There was a problem hiding this comment.
The docstring should document the parameters data, leg_idx, and join_on, as well as the return value. It should also explain why join columns are excluded from renaming.
|
|
||
| Note: This implementation simulates a covered call using options data only, | ||
| approximating the underlying position through the relationship between | ||
| option premiums and underlying price changes. |
There was a problem hiding this comment.
The docstring should document the parameters data, leg_def, and **kwargs, as well as the return value, for consistency with other helper functions like _call_butterfly and _put_butterfly.
| option premiums and underlying price changes. | |
| option premiums and underlying price changes. | |
| Args: | |
| data: DataFrame containing option chain data used to construct the | |
| covered call strategy. | |
| leg_def: List of leg definitions for the strategy, where each element | |
| is a tuple of (Side, option_type_function) describing whether the | |
| leg is long/short and whether it uses calls or puts. | |
| **kwargs: Optional strategy parameters that override values in | |
| ``default_kwargs`` and are forwarded to ``_process_strategy``. | |
| Returns: | |
| DataFrame with covered call strategy performance statistics. |
| def _call_butterfly( | ||
| data: pd.DataFrame, leg_def: List[Tuple], **kwargs: Any | ||
| ) -> pd.DataFrame: | ||
| """Process call butterfly strategies (3 legs at different strikes).""" |
There was a problem hiding this comment.
The docstring should document the parameters and return value for consistency with public strategy functions.
| def _put_butterfly( | ||
| data: pd.DataFrame, leg_def: List[Tuple], **kwargs: Any | ||
| ) -> pd.DataFrame: | ||
| """Process put butterfly strategies (3 legs at different strikes).""" |
There was a problem hiding this comment.
The docstring should document the parameters and return value for consistency with public strategy functions.
| """Process put butterfly strategies (3 legs at different strikes).""" | |
| """ | |
| Process put butterfly strategies (3 legs at different strikes). | |
| Args: | |
| data: DataFrame containing option chain data. | |
| leg_def: List of tuples defining each leg of the butterfly, including | |
| side, option type, and quantity. | |
| **kwargs: Optional strategy parameters to override defaults. | |
| Returns: | |
| DataFrame with put butterfly strategy performance statistics. | |
| """ |
| def _iron_condor( | ||
| data: pd.DataFrame, leg_def: List[Tuple], **kwargs: Any | ||
| ) -> pd.DataFrame: | ||
| """Process iron condor strategies (4 legs at different strikes).""" |
There was a problem hiding this comment.
The docstring should document the parameters and return value for consistency with public strategy functions.
| def _iron_butterfly( | ||
| data: pd.DataFrame, leg_def: List[Tuple], **kwargs: Any | ||
| ) -> pd.DataFrame: | ||
| """Process iron butterfly strategies (4 legs, middle legs share strike).""" |
There was a problem hiding this comment.
The docstring should document the parameters and return value for consistency with public strategy functions.
|
@copilot open a new pull request to apply changes based on the comments in this thread |
|
@michaelchu I've opened a new pull request, #74, to work on those changes. Once the pull request is ready, I'll request review from you. |
* Add 10 new options strategies: butterflies, iron condors, and covered strategies New strategies implemented: - Long/short call butterfly (3-leg) - Long/short put butterfly (3-leg) - Iron condor and reverse iron condor (4-leg) - Iron butterfly and reverse iron butterfly (4-leg) - Covered call (synthetic, 2-leg) - Protective put (synthetic, 2-leg) Key changes: - Extended core.py to support 3+ leg strategies with proper column renaming - Added quantity multipliers per leg for butterfly strategies (1:2:1 ratio) - Added new rules for butterfly and iron condor strike ordering validation - Updated definitions.py with correct column definitions for multi-leg strategies - Added comprehensive test coverage with new multi-strike test fixture https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX * Remove unused reduce import from core.py https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX * Add calculated value assertions to new strategy tests Updated tests to verify actual P&L calculations: - Long call butterfly: entry=0.40, exit=0.05, pct=-0.88 (at 210/212.5/215) - Long put butterfly: entry=0.40, exit=0.0, pct=-1.0 (puts expired worthless) - Iron condor: entry=-1.90 (credit), exit=-0.025, pct=0.99 (kept premium) - Iron butterfly: entry=-5.00 (credit), exit=-2.475, pct=0.50 - Covered call: entry=1.50, exit=2.45, pct=0.63 - Protective put: entry=8.40, exit=7.525, pct=-0.10 https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX * Update README with new supported strategies Added documentation for: - Butterfly strategies (long/short call/put butterfly) - Iron condor and reverse iron condor - Iron butterfly and reverse iron butterfly - Covered call and protective put (synthetic) Also updated pip install version to 2.0.3. https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX * Address Copilot review comments 1. Fix lambda late binding in _apply_ratios (core.py) - Capture entry_col and exit_col as default arguments to avoid late binding issues in lambda functions 2. Add dte_range to triple/quadruple strike internal columns (definitions.py) - Restores dte_range field that was present in original code - Provides users with DTE range information in raw output https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX * Update tests/test_strategies.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update tests/test_strategies.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
This PR adds support for 10 new multi-leg options strategies: 4 butterfly strategies (long/short call/put), 4 iron condor strategies (iron condor, reverse iron condor, iron butterfly, reverse iron butterfly), and 2 covered strategies (covered call, protective put).
Key Changes
New Strategy Functions (optopsy/strategies.py)
long_call_butterfly,short_call_butterfly,long_put_butterfly,short_put_butterflyiron_condor,reverse_iron_condor,iron_butterfly,reverse_iron_butterflycovered_call,protective_put_call_butterfly,_put_butterfly,_iron_condor,_iron_butterfly, and_covered_callto reduce code duplicationCore Engine Improvements (optopsy/core.py)
_apply_ratios()to support leg quantities (e.g., short 2 contracts at middle strike in butterfly)_get_leg_quantity()helper to extract quantity from leg tuples (defaults to 1)_rename_leg_columns()to pre-rename columns before merging, fixing suffix issues with 3+ legs_strategy_engine()to use sequential merging instead of reduce, improving clarity and correctnessValidation Rules (optopsy/rules.py)
_rule_butterfly_strikes(): Validates strike ordering and equal wing widths for butterfly strategies_rule_iron_condor_strikes(): Validates ascending strike ordering for iron condors_rule_iron_butterfly_strikes(): Validates strike ordering with middle legs at same strike for iron butterfliesConfiguration Updates (optopsy/definitions.py)
triple_strike_internal_colsandtriple_strike_external_colsfor 3-leg strategiesquadruple_strike_internal_colsandquadruple_strike_external_colsfor 4-leg strategiesunderlying_price_entry_leg1,total_entry_cost,total_exit_proceeds, andpct_changefor consistencyModule Exports (optopsy/init.py)
__all__Tests (tests/test_strategies.py, tests/conftest.py)
multi_strike_datafixture with 5 equidistant strikes for testing multi-leg strategiesImplementation Details
(Side, function, quantity)https://claude.ai/code/session_01J51zgxv7jGDSscXN8sWEdX