Skip to content

Commit 12b3a3f

Browse files
committed
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
1 parent 7ce49b6 commit 12b3a3f

File tree

1 file changed

+77
-6
lines changed

1 file changed

+77
-6
lines changed

tests/test_strategies.py

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020

2121
def test_long_call_butterfly_raw(multi_strike_data):
22-
"""Test long call butterfly returns correct structure and leg types."""
22+
"""Test long call butterfly returns correct structure and calculated values."""
2323
results = long_call_butterfly(multi_strike_data, raw=True)
2424
assert list(results.columns) == triple_strike_internal_cols
2525
# All legs should be calls
@@ -29,6 +29,17 @@ def test_long_call_butterfly_raw(multi_strike_data):
2929
# Strikes should be in ascending order with equal width
3030
assert results.iloc[0]["strike_leg1"] < results.iloc[0]["strike_leg2"]
3131
assert results.iloc[0]["strike_leg2"] < results.iloc[0]["strike_leg3"]
32+
# Check calculated values for butterfly at strikes 210, 212.5, 215
33+
# Entry: long 210 call (4.95) + short 2x 212.5 calls (-6.10) + long 215 call (1.55) = 0.40
34+
# Exit: 5.00 + (-5.00) + 0.05 = 0.05
35+
row = results[
36+
(results["strike_leg1"] == 210.0)
37+
& (results["strike_leg2"] == 212.5)
38+
& (results["strike_leg3"] == 215.0)
39+
].iloc[0]
40+
assert round(row["total_entry_cost"], 2) == 0.40
41+
assert round(row["total_exit_proceeds"], 2) == 0.05
42+
assert round(row["pct_change"], 2) == -0.88
3243

3344

3445
def test_long_call_butterfly(multi_strike_data):
@@ -47,13 +58,25 @@ def test_short_call_butterfly_raw(multi_strike_data):
4758

4859

4960
def test_long_put_butterfly_raw(multi_strike_data):
50-
"""Test long put butterfly returns correct structure and leg types."""
61+
"""Test long put butterfly returns correct structure and calculated values."""
5162
results = long_put_butterfly(multi_strike_data, raw=True)
5263
assert list(results.columns) == triple_strike_internal_cols
5364
# All legs should be puts
5465
assert results.iloc[0]["option_type_leg1"] == "put"
5566
assert results.iloc[0]["option_type_leg2"] == "put"
5667
assert results.iloc[0]["option_type_leg3"] == "put"
68+
# Check calculated values for butterfly at strikes 210, 212.5, 215
69+
# All puts expired worthless since underlying at 215
70+
row = results[
71+
(results["strike_leg1"] == 210.0)
72+
& (results["strike_leg2"] == 212.5)
73+
& (results["strike_leg3"] == 215.0)
74+
].iloc[0]
75+
# Entry: long 210 put (1.45) + short 2x 212.5 puts (-6.10) + long 215 put (5.05) = 0.40
76+
assert round(row["total_entry_cost"], 2) == 0.40
77+
# Exit: all puts worthless = 0.025 + (-0.05) + 0.025 = 0.0
78+
assert round(row["total_exit_proceeds"], 2) == 0.0
79+
assert round(row["pct_change"], 2) == -1.0
5780

5881

5982
def test_long_put_butterfly(multi_strike_data):
@@ -77,7 +100,7 @@ def test_short_put_butterfly_raw(multi_strike_data):
77100

78101

79102
def test_iron_condor_raw(multi_strike_data):
80-
"""Test iron condor returns correct structure and leg types."""
103+
"""Test iron condor returns correct structure and calculated values."""
81104
results = iron_condor(multi_strike_data, raw=True)
82105
assert list(results.columns) == quadruple_strike_internal_cols
83106
# Leg 1 and 2 should be puts, leg 3 and 4 should be calls
@@ -89,6 +112,19 @@ def test_iron_condor_raw(multi_strike_data):
89112
assert results.iloc[0]["strike_leg1"] < results.iloc[0]["strike_leg2"]
90113
assert results.iloc[0]["strike_leg2"] < results.iloc[0]["strike_leg3"]
91114
assert results.iloc[0]["strike_leg3"] < results.iloc[0]["strike_leg4"]
115+
# Check calculated values for iron condor at strikes 207.5, 210, 215, 217.5
116+
# Underlying moved from 212.5 to 215, staying within the condor range
117+
row = results[
118+
(results["strike_leg1"] == 207.5)
119+
& (results["strike_leg2"] == 210.0)
120+
& (results["strike_leg3"] == 215.0)
121+
& (results["strike_leg4"] == 217.5)
122+
].iloc[0]
123+
# Net credit received at entry (negative cost = credit)
124+
assert round(row["total_entry_cost"], 2) == -1.90
125+
# Nearly all options expired worthless, kept most of premium
126+
assert round(row["total_exit_proceeds"], 3) == -0.025
127+
assert round(row["pct_change"], 2) == 0.99
92128

93129

94130
def test_iron_condor(multi_strike_data):
@@ -113,7 +149,7 @@ def test_reverse_iron_condor_raw(multi_strike_data):
113149

114150

115151
def test_iron_butterfly_raw(multi_strike_data):
116-
"""Test iron butterfly returns correct structure and leg types."""
152+
"""Test iron butterfly returns correct structure and calculated values."""
117153
results = iron_butterfly(multi_strike_data, raw=True)
118154
assert list(results.columns) == quadruple_strike_internal_cols
119155
# Leg 1 and 2 should be puts, leg 3 and 4 should be calls
@@ -123,6 +159,19 @@ def test_iron_butterfly_raw(multi_strike_data):
123159
assert results.iloc[0]["option_type_leg4"] == "call"
124160
# Middle legs should share the same strike
125161
assert results.iloc[0]["strike_leg2"] == results.iloc[0]["strike_leg3"]
162+
# Check calculated values for iron butterfly at strikes 207.5, 212.5, 212.5, 217.5
163+
# Middle strike at 212.5 (ATM), wings at 207.5 and 217.5
164+
row = results[
165+
(results["strike_leg1"] == 207.5)
166+
& (results["strike_leg2"] == 212.5)
167+
& (results["strike_leg3"] == 212.5)
168+
& (results["strike_leg4"] == 217.5)
169+
].iloc[0]
170+
# Net credit received at entry
171+
assert round(row["total_entry_cost"], 2) == -5.00
172+
# Underlying moved to 215, short straddle lost value
173+
assert round(row["total_exit_proceeds"], 3) == -2.475
174+
assert round(row["pct_change"], 2) == 0.50
126175

127176

128177
def test_iron_butterfly(multi_strike_data):
@@ -149,12 +198,22 @@ def test_reverse_iron_butterfly_raw(multi_strike_data):
149198

150199

151200
def test_covered_call_raw(multi_strike_data):
152-
"""Test covered call returns correct structure."""
201+
"""Test covered call returns correct structure and calculated values."""
153202
results = covered_call(multi_strike_data, raw=True)
154203
assert list(results.columns) == double_strike_internal_cols
155204
# Both legs should be calls
156205
assert results.iloc[0]["option_type_leg1"] == "call"
157206
assert results.iloc[0]["option_type_leg2"] == "call"
207+
# Check calculated values for covered call at strikes 212.5, 215
208+
# Long deep ITM call (synthetic stock) + short OTM call
209+
row = results[
210+
(results["strike_leg1"] == 212.5) & (results["strike_leg2"] == 215.0)
211+
].iloc[0]
212+
# Entry: long 212.5 call (3.05) + short 215 call (-1.55) = 1.50
213+
assert round(row["total_entry_cost"], 2) == 1.50
214+
# Exit: long call worth 2.50 + short call expired worthless (-0.05) = 2.45
215+
assert round(row["total_exit_proceeds"], 2) == 2.45
216+
assert round(row["pct_change"], 2) == 0.63
158217

159218

160219
def test_covered_call(multi_strike_data):
@@ -164,12 +223,24 @@ def test_covered_call(multi_strike_data):
164223

165224

166225
def test_protective_put_raw(multi_strike_data):
167-
"""Test protective put returns correct structure."""
226+
"""Test protective put returns correct structure and calculated values."""
168227
results = protective_put(multi_strike_data, raw=True)
169228
assert list(results.columns) == double_strike_internal_cols
170229
# Leg 1 should be call (synthetic stock), leg 2 should be put
171230
assert results.iloc[0]["option_type_leg1"] == "call"
172231
assert results.iloc[0]["option_type_leg2"] == "put"
232+
# Check calculated values for protective put at strikes 207.5, 210
233+
# Long call (synthetic stock) + long put for protection
234+
# Note: strike_leg1 < strike_leg2 due to non-overlapping rule
235+
row = results[
236+
(results["strike_leg1"] == 207.5) & (results["strike_leg2"] == 210.0)
237+
].iloc[0]
238+
# Entry: long 207.5 call (6.95) + long 210 put (1.45) = 8.40
239+
assert round(row["total_entry_cost"], 2) == 8.40
240+
# Exit: call worth 7.50 + put expired worthless (0.025) = 7.525
241+
assert round(row["total_exit_proceeds"], 3) == 7.525
242+
# Small loss due to time decay
243+
assert round(row["pct_change"], 2) == -0.10
173244

174245

175246
def test_protective_put(multi_strike_data):

0 commit comments

Comments
 (0)