1
+ using Revise
2
+ using Hedgehog2
3
+ using Distributions
4
+ using Random
5
+ using Plots
6
+ using Dates
7
+ using Statistics
8
+ using Printf
9
+
10
+ println (" === Heston Model: Antithetic Variates Variance Reduction Analysis ===" )
11
+
12
+ # --- Market Inputs ---
13
+ reference_date = Date (2020 , 1 , 1 )
14
+
15
+ # Heston parameters (chosen to satisfy Feller condition)
16
+ S0 = 1.0
17
+ V0 = 0.04 # Initial variance (20% vol)
18
+ κ = 1.0 # Mean reversion
19
+ θ = 0.04 # Long-term variance
20
+ σ = 0.2 # Vol-of-vol
21
+ ρ = - 0.5 # Correlation
22
+ r = 0.02 # Risk-free rate
23
+
24
+ market_inputs = HestonInputs (reference_date, r, S0, V0, κ, θ, σ, ρ)
25
+
26
+ # --- Payoff ---
27
+ expiry = reference_date + Year (5 )
28
+ strike = S0 # ATM call
29
+ payoff = VanillaOption (strike, expiry, European (), Call (), Spot ())
30
+
31
+ # --- Dynamics ---
32
+ dynamics = HestonDynamics ()
33
+
34
+ # --- Get reference price using Carr-Madan (Fourier) method ---
35
+ α = 1.0
36
+ boundary = 32.0
37
+ carr_madan_method = CarrMadan (α, boundary, dynamics)
38
+ prob = PricingProblem (payoff, market_inputs)
39
+ carr_madan_solution = solve (prob, carr_madan_method)
40
+ reference_price = carr_madan_solution. price
41
+
42
+ println (" Reference price (Carr-Madan): $reference_price " )
43
+
44
+ # --- Variance Reduction Analysis ---
45
+ # We'll compare standard MC vs antithetic variates
46
+ n_trials = 30 # Number of independent trials
47
+ base_paths = 500 # Base number of paths
48
+ time_steps = 20 # Time steps for each path
49
+ Random. seed! (42 ) # For reproducibility
50
+
51
+ # Arrays to store results
52
+ std_prices = Float64[]
53
+ anti_prices = Float64[]
54
+ std_times = Float64[]
55
+ anti_times = Float64[]
56
+
57
+ # Repeat trials
58
+ for trial in 1 : n_trials
59
+ # Different seed for each trial
60
+ trial_seed = 42 + trial
61
+
62
+ # Standard Monte Carlo (full number of paths)
63
+ std_strategy = HestonBroadieKaya (base_paths, steps= time_steps, seeds= rand (MersenneTwister (trial_seed), 1 : 10 ^ 9 , base_paths))
64
+ std_method = MonteCarlo (dynamics, std_strategy)
65
+
66
+ std_time = @elapsed begin
67
+ std_solution = solve (prob, std_method)
68
+ push! (std_prices, std_solution. price)
69
+ end
70
+ push! (std_times, std_time)
71
+
72
+ # Antithetic Monte Carlo (half the number of paths × 2)
73
+ anti_strategy = HestonBroadieKaya (base_paths ÷ 2 , steps= time_steps, seeds= rand (MersenneTwister (trial_seed), 1 : 10 ^ 9 , base_paths ÷ 2 ), antithetic= true )
74
+ anti_method = MonteCarlo (dynamics, anti_strategy)
75
+
76
+ anti_time = @elapsed begin
77
+ anti_solution = solve (prob, anti_method)
78
+ push! (anti_prices, anti_solution. price)
79
+ end
80
+ push! (anti_times, anti_time)
81
+
82
+ # Print progress
83
+ if trial % 5 == 0
84
+ println (" Completed $trial /$n_trials trials" )
85
+ end
86
+ end
87
+
88
+ # --- Calculate variance reduction statistics ---
89
+ std_mean = mean (std_prices)
90
+ anti_mean = mean (anti_prices)
91
+
92
+ std_var = var (std_prices)
93
+ anti_var = var (anti_prices)
94
+
95
+ std_bias = std_mean - reference_price
96
+ anti_bias = anti_mean - reference_price
97
+
98
+ std_mse = mean ((std_prices .- reference_price). ^ 2 )
99
+ anti_mse = mean ((anti_prices .- reference_price). ^ 2 )
100
+
101
+ var_reduction_ratio = std_var / anti_var
102
+ mse_reduction_ratio = std_mse / anti_mse
103
+ time_ratio = mean (std_times) / mean (anti_times)
104
+
105
+ # Efficiency improvement (variance reduction per unit of computation)
106
+ efficiency_gain = var_reduction_ratio * time_ratio
107
+
108
+ # --- Visualization ---
109
+ # Distribution of prices
110
+ histogram_plot = histogram (
111
+ std_prices,
112
+ alpha= 0.5 ,
113
+ label= " Standard MC" ,
114
+ bins= 15 ,
115
+ title= " Distribution of Price Estimates" ,
116
+ xlabel= " Price" ,
117
+ ylabel= " Frequency"
118
+ )
119
+
120
+ histogram! (
121
+ histogram_plot,
122
+ anti_prices,
123
+ alpha= 0.5 ,
124
+ label= " Antithetic MC" ,
125
+ bins= 15
126
+ )
127
+
128
+ # Add reference price line
129
+ vline! (
130
+ histogram_plot,
131
+ [reference_price],
132
+ label= " Reference Price" ,
133
+ color= :red ,
134
+ linewidth= 2 ,
135
+ linestyle= :dash
136
+ )
137
+
138
+ # --- Print results ---
139
+ println (" \n === Variance Reduction Analysis ===" )
140
+ println (" Number of trials: $n_trials " )
141
+ println (" Paths (standard MC): $base_paths " )
142
+ println (" Paths (antithetic MC): $(base_paths ÷ 2 ) × 2" )
143
+ println ()
144
+ println (" Reference price: $reference_price " )
145
+ println ()
146
+ @printf (" Standard MC mean: %.6f (bias: %.6f)\n " , std_mean, std_bias)
147
+ @printf (" Antithetic MC mean: %.6f (bias: %.6f)\n " , anti_mean, anti_bias)
148
+ println ()
149
+ @printf (" Standard MC variance: %.6e\n " , std_var)
150
+ @printf (" Antithetic variance: %.6e\n " , anti_var)
151
+ @printf (" Variance reduction: %.2fx\n " , var_reduction_ratio)
152
+ println ()
153
+ @printf (" Standard MC MSE: %.6e\n " , std_mse)
154
+ @printf (" Antithetic MC MSE: %.6e\n " , anti_mse)
155
+ @printf (" MSE reduction: %.2fx\n " , mse_reduction_ratio)
156
+ println ()
157
+ @printf (" Avg. time (standard): %.3f seconds\n " , mean (std_times))
158
+ @printf (" Avg. time (antithetic): %.3f seconds\n " , mean (anti_times))
159
+ @printf (" Time ratio: %.2fx\n " , time_ratio)
160
+ println ()
161
+ @printf (" Efficiency gain: %.2fx\n " , efficiency_gain)
162
+
163
+ # --- Visualize a sample path pair ---
164
+ if length (std_prices) > 0 && length (anti_prices) > 0
165
+ Random. seed! (42 ) # Reset seed for consistent visualization
166
+
167
+ # Simulate a single path with antithetic variates for visualization
168
+ vis_strategy = HestonBroadieKaya (1 , steps= time_steps, antithetic= true )
169
+ vis_method = MonteCarlo (dynamics, vis_strategy)
170
+ vis_solution = solve (prob, vis_method)
171
+
172
+ # Extract paths
173
+ original_path = vis_solution. ensemble. solutions[1 ]
174
+ antithetic_path = vis_solution. ensemble. solutions[2 ] # Second path is antithetic
175
+
176
+ # Extract time points
177
+ time_points = original_path. t
178
+
179
+ # Extract spot prices and variances
180
+ original_prices = [exp (p[1 ]) for p in original_path. u]
181
+ antithetic_prices = [exp (p[1 ]) for p in antithetic_path. u]
182
+ original_variance = [p[2 ] for p in original_path. u]
183
+ antithetic_variance = [p[2 ] for p in antithetic_path. u]
184
+
185
+ # Create path plots
186
+ p1 = plot (
187
+ time_points, original_prices,
188
+ label= " Original Path" ,
189
+ linewidth= 2 ,
190
+ title= " Heston Model: Stock Price Paths" ,
191
+ xlabel= " Time (years)" ,
192
+ ylabel= " Stock Price" ,
193
+ legend= :topleft
194
+ )
195
+
196
+ plot! (
197
+ p1,
198
+ time_points, antithetic_prices,
199
+ label= " Antithetic Path" ,
200
+ linewidth= 2 ,
201
+ linestyle= :dash ,
202
+ color= :red
203
+ )
204
+
205
+ p2 = plot (
206
+ time_points, original_variance,
207
+ label= " Original Path" ,
208
+ linewidth= 2 ,
209
+ title= " Heston Model: Variance Paths" ,
210
+ xlabel= " Time (years)" ,
211
+ ylabel= " Variance" ,
212
+ legend= :topleft
213
+ )
214
+
215
+ plot! (
216
+ p2,
217
+ time_points, antithetic_variance,
218
+ label= " Antithetic Path" ,
219
+ linewidth= 2 ,
220
+ linestyle= :dash ,
221
+ color= :red
222
+ )
223
+
224
+ # Combine plots
225
+ path_plot = plot (p1, p2, layout= (2 ,1 ), size= (800 , 600 ))
226
+
227
+ # Calculate price-path correlation
228
+ path_correlation = cor (original_prices, antithetic_prices)
229
+ var_correlation = cor (original_variance, antithetic_variance)
230
+
231
+ println (" \n === Path Correlation Analysis ===" )
232
+ @printf (" Stock price path correlation: %.4f\n " , path_correlation)
233
+ @printf (" Variance path correlation: %.4f\n " , var_correlation)
234
+
235
+ # Display both plots
236
+ display (histogram_plot)
237
+ display (path_plot)
238
+ end
0 commit comments