1
+ using Test
2
+ using Hedgehog
3
+ using Dates
4
+ using Random
5
+ using Statistics
6
+
7
+ @testset " LSM vs Binomial Tree Agreement for American Options" begin
8
+
9
+ @testset " American Put Option Agreement" begin
10
+ # Setup common parameters
11
+ strike = 100.0
12
+ reference_date = Date (2020 , 1 , 1 )
13
+ expiry = reference_date + Year (1 )
14
+ rate = 0.05
15
+ spot = 100.0
16
+ sigma = 0.2
17
+
18
+ # Create American put option
19
+ american_put = VanillaOption (strike, expiry, American (), Put (), Spot ())
20
+ market_inputs = BlackScholesInputs (reference_date, rate, spot, sigma)
21
+ prob = PricingProblem (american_put, market_inputs)
22
+
23
+ # Binomial tree method (high number of steps for accuracy)
24
+ crr_steps = 1000
25
+ crr_method = CoxRossRubinsteinMethod (crr_steps)
26
+ crr_solution = solve (prob, crr_method)
27
+
28
+ # LSM method with fixed seeds for reproducibility
29
+ trajectories = 50_000
30
+ steps = 100
31
+ rng = Xoshiro (12345 )
32
+ seeds = rand (rng, UInt64, trajectories)
33
+
34
+ dynamics = LognormalDynamics ()
35
+ strategy = BlackScholesExact ()
36
+ config = SimulationConfig (trajectories; steps= steps, seeds= seeds, variance_reduction= Antithetic ())
37
+ degree = 5
38
+ lsm_method = LSM (dynamics, strategy, config, degree)
39
+
40
+ lsm_solution = solve (prob, lsm_method)
41
+
42
+ # Test agreement with reasonable tolerance
43
+ # LSM has Monte Carlo error, so we allow larger tolerance
44
+ relative_error = abs (lsm_solution. price - crr_solution. price) / crr_solution. price
45
+
46
+ println (" American Put Pricing Comparison:" )
47
+ println (" Binomial Tree ($(crr_steps) steps): $(crr_solution. price) " )
48
+ println (" LSM ($(trajectories) paths): $(lsm_solution. price) " )
49
+ println (" Relative Error: $(relative_error * 100 ) %" )
50
+
51
+ @test isapprox (lsm_solution. price, crr_solution. price; rtol= 0.02 )
52
+ end
53
+
54
+ @testset " American Call Option Agreement (High Dividend Yield)" begin
55
+ # For American calls to have early exercise value, we need high dividend yield
56
+ # We simulate this by using a high rate scenario
57
+ strike = 100.0
58
+ reference_date = Date (2020 , 1 , 1 )
59
+ expiry = reference_date + Year (1 )
60
+ rate = 0.15 # High rate to make early exercise attractive
61
+ spot = 120.0 # ITM call
62
+ sigma = 0.3
63
+
64
+ # Create American call option
65
+ american_call = VanillaOption (strike, expiry, American (), Call (), Spot ())
66
+ market_inputs = BlackScholesInputs (reference_date, rate, spot, sigma)
67
+ prob = PricingProblem (american_call, market_inputs)
68
+
69
+ # Binomial tree method
70
+ crr_steps = 800
71
+ crr_method = CoxRossRubinsteinMethod (crr_steps)
72
+ crr_solution = solve (prob, crr_method)
73
+
74
+ # LSM method
75
+ trajectories = 30_000
76
+ steps = 100
77
+ rng = Xoshiro (54321 )
78
+ seeds = rand (rng, UInt64, trajectories)
79
+
80
+ dynamics = LognormalDynamics ()
81
+ strategy = BlackScholesExact ()
82
+ config = SimulationConfig (trajectories; steps= steps, seeds= seeds, variance_reduction= Antithetic ())
83
+ degree = 5
84
+ lsm_method = LSM (dynamics, strategy, config, degree)
85
+
86
+ lsm_solution = solve (prob, lsm_method)
87
+
88
+ relative_error = abs (lsm_solution. price - crr_solution. price) / crr_solution. price
89
+
90
+ println (" \n American Call Pricing Comparison (High Rate Scenario):" )
91
+ println (" Binomial Tree ($(crr_steps) steps): $(crr_solution. price) " )
92
+ println (" LSM ($(trajectories) paths): $(lsm_solution. price) " )
93
+ println (" Relative Error: $(relative_error * 100 ) %" )
94
+
95
+ @test isapprox (lsm_solution. price, crr_solution. price; rtol= 0.03 )
96
+ end
97
+
98
+ @testset " Multiple Strike Agreement Test" begin
99
+ # Test agreement across different moneyness levels
100
+ reference_date = Date (2020 , 1 , 1 )
101
+ expiry = reference_date + Month (6 ) # Shorter maturity
102
+ rate = 0.05
103
+ spot = 100.0
104
+ sigma = 0.25
105
+
106
+ strikes = [80.0 , 90.0 , 100.0 , 110.0 , 120.0 ] # OTM to ITM puts
107
+
108
+ println (" \n Multiple Strike Agreement Test (American Puts, 6M maturity):" )
109
+ println (" Strike | Binomial | LSM | Rel Error" )
110
+ println (" -------|----------|----------|----------" )
111
+
112
+ for strike in strikes
113
+ # Create American put option
114
+ american_put = VanillaOption (strike, expiry, American (), Put (), Spot ())
115
+ market_inputs = BlackScholesInputs (reference_date, rate, spot, sigma)
116
+ prob = PricingProblem (american_put, market_inputs)
117
+
118
+ # Binomial tree method
119
+ crr_steps = 500
120
+ crr_method = CoxRossRubinsteinMethod (crr_steps)
121
+ crr_solution = solve (prob, crr_method)
122
+
123
+ # LSM method (smaller number of paths for speed)
124
+ trajectories = 20_000
125
+ steps = 50
126
+ rng = Xoshiro (Int (strike) * 1000 ) # Different seed per strike
127
+ seeds = rand (rng, UInt64, trajectories)
128
+
129
+ dynamics = LognormalDynamics ()
130
+ strategy = BlackScholesExact ()
131
+ config = SimulationConfig (trajectories; steps= steps, seeds= seeds, variance_reduction= Antithetic ())
132
+ degree = 4
133
+ lsm_method = LSM (dynamics, strategy, config, degree)
134
+
135
+ lsm_solution = solve (prob, lsm_method)
136
+
137
+ relative_error = abs (lsm_solution. price - crr_solution. price) / crr_solution. price
138
+
139
+ @printf (" %6.1f | %8.4f | %8.4f | %7.2f%%\n " ,
140
+ strike, crr_solution. price, lsm_solution. price, relative_error * 100 )
141
+
142
+ # More lenient tolerance for OTM options (lower absolute prices)
143
+ tolerance = strike < spot ? 0.05 : 0.03
144
+ @test isapprox (lsm_solution. price, crr_solution. price; rtol= tolerance)
145
+ end
146
+ end
147
+
148
+ @testset " Early Exercise Premium Consistency" begin
149
+ # Compare early exercise premium: American - European
150
+ strike = 110.0
151
+ reference_date = Date (2020 , 1 , 1 )
152
+ expiry = reference_date + Year (1 )
153
+ rate = 0.03
154
+ spot = 100.0
155
+ sigma = 0.3
156
+
157
+ # American put
158
+ american_put = VanillaOption (strike, expiry, American (), Put (), Spot ())
159
+ market_inputs = BlackScholesInputs (reference_date, rate, spot, sigma)
160
+ american_prob = PricingProblem (american_put, market_inputs)
161
+
162
+ # European put
163
+ european_put = VanillaOption (strike, expiry, European (), Put (), Spot ())
164
+ european_prob = PricingProblem (european_put, market_inputs)
165
+
166
+ # Prices using both methods
167
+ crr_method = CoxRossRubinsteinMethod (800 )
168
+ bs_method = BlackScholesAnalytic ()
169
+
170
+ american_crr = solve (american_prob, crr_method)
171
+ european_bs = solve (european_prob, bs_method)
172
+ early_exercise_premium_crr = american_crr. price - european_bs. price
173
+
174
+ # LSM for American
175
+ trajectories = 40_000
176
+ steps = 100
177
+ rng = Xoshiro (98765 )
178
+ seeds = rand (rng, UInt64, trajectories)
179
+
180
+ dynamics = LognormalDynamics ()
181
+ strategy = BlackScholesExact ()
182
+ config = SimulationConfig (trajectories; steps= steps, seeds= seeds, variance_reduction= Antithetic ())
183
+ degree = 5
184
+ lsm_method = LSM (dynamics, strategy, config, degree)
185
+
186
+ american_lsm = solve (american_prob, lsm_method)
187
+ early_exercise_premium_lsm = american_lsm. price - european_bs. price
188
+
189
+ println (" \n Early Exercise Premium Consistency:" )
190
+ println (" European Put (BS): $(european_bs. price) " )
191
+ println (" American Put (Binomial): $(american_crr. price) " )
192
+ println (" American Put (LSM): $(american_lsm. price) " )
193
+ println (" Early Ex Premium (Binomial): $(early_exercise_premium_crr) " )
194
+ println (" Early Ex Premium (LSM): $(early_exercise_premium_lsm) " )
195
+
196
+ # Both methods should agree that American >= European
197
+ @test american_crr. price >= european_bs. price
198
+ @test american_lsm. price >= european_bs. price
199
+
200
+ # Early exercise premiums should be similar
201
+ @test isapprox (early_exercise_premium_lsm, early_exercise_premium_crr; rtol= 0.04 )
202
+ end
203
+ end
0 commit comments