1
+ using Hedgehog2
2
+ using Dates
3
+ using Random
4
+ using Statistics
5
+ using Plots
6
+
7
+ function analyze_bk_antithetic_paths (num_paths= 10 , seed= 42 )
8
+ # Set random seed for reproducibility
9
+ Random. seed! (seed)
10
+
11
+ # Set up common parameters
12
+ reference_date = Date (2020 , 1 , 1 )
13
+ expiry = reference_date + Year (1 )
14
+ spot = 100.0
15
+ strike = 100.0
16
+
17
+ # Create payoff
18
+ payoff = VanillaOption (strike, expiry, European (), Call (), Spot ())
19
+
20
+ # Heston parameters - can be adjusted to see different behaviors
21
+ rate_heston = 0.03
22
+ V0 = 0.04
23
+ κ = 2.0
24
+ θ = 0.04
25
+ σ = 0.3
26
+ ρ = - 0.7
27
+
28
+ # Create the market inputs
29
+ heston_market = HestonInputs (reference_date, rate_heston, spot, V0, κ, θ, σ, ρ)
30
+ prob = PricingProblem (payoff, heston_market)
31
+
32
+ # Generate seeds for paths
33
+ path_seeds = rand (1 : 10 ^ 9 , num_paths ÷ 2 )
34
+
35
+ # Create the Broadie-Kaya method with antithetic sampling
36
+ bk_strategy = HestonBroadieKaya (num_paths ÷ 2 , steps= 20 )
37
+ bk_method = MonteCarlo (HestonDynamics (), bk_strategy)
38
+
39
+ # Update with seeds and antithetic flag
40
+ antithetic_method = @set bk_method. strategy. seeds = path_seeds
41
+ antithetic_method = @set antithetic_method. strategy. kwargs = merge (antithetic_method. strategy. kwargs, (antithetic= true ,))
42
+
43
+ # Solve the problem
44
+ solution = solve (prob, antithetic_method)
45
+
46
+ # Return the solution for further analysis
47
+ return solution, prob
48
+ end
49
+
50
+ function plot_bk_path_pair (solution, prob, path_index= 1 ; apply_exp= true )
51
+ half_paths = length (solution. ensemble. solutions) ÷ 2
52
+
53
+ # Get the original and antithetic paths
54
+ original_path = solution. ensemble. solutions[path_index]
55
+ antithetic_path = solution. ensemble. solutions[half_paths + path_index]
56
+
57
+ # Extract time points
58
+ time_points = original_path. t
59
+
60
+ # Extract stock prices (log to normal scale if requested)
61
+ if apply_exp
62
+ original_prices = [exp (u[1 ]) for u in original_path. u]
63
+ antithetic_prices = [exp (u[1 ]) for u in antithetic_path. u]
64
+ ylabel_text = " Stock Price"
65
+ else
66
+ original_prices = [u[1 ] for u in original_path. u]
67
+ antithetic_prices = [u[1 ] for u in antithetic_path. u]
68
+ ylabel_text = " Log Price"
69
+ end
70
+
71
+ # Create price plot
72
+ p1 = plot (
73
+ time_points, original_prices,
74
+ label= " Original Path" ,
75
+ linewidth= 2 ,
76
+ title= " Heston Broadie-Kaya: $(apply_exp ? " Stock Price" : " Log Price" ) Paths" ,
77
+ xlabel= " Time (years)" ,
78
+ ylabel= ylabel_text,
79
+ legend= :topleft
80
+ )
81
+
82
+ plot! (
83
+ p1,
84
+ time_points, antithetic_prices,
85
+ label= " Antithetic Path" ,
86
+ linewidth= 2 ,
87
+ linestyle= :dash ,
88
+ color= :red
89
+ )
90
+
91
+ # Calculate price correlation
92
+ price_corr = cor (original_prices, antithetic_prices)
93
+ annotate! (p1, [(0.5 , minimum (original_prices),
94
+ text (" Path correlation: $(round (price_corr, digits= 4 )) " , 10 , :left ))])
95
+
96
+ return p1
97
+ end
98
+
99
+ function calculate_bk_path_statistics (solution, prob)
100
+ half_paths = length (solution. ensemble. solutions) ÷ 2
101
+
102
+ price_correlations = Float64[]
103
+ terminal_products = Float64[]
104
+ discount_factor = df (prob. market. rate, prob. payoff. expiry)
105
+
106
+ original_terminals = Float64[]
107
+ antithetic_terminals = Float64[]
108
+
109
+ # Calculate statistics for each path pair
110
+ for i in 1 : half_paths
111
+ original_path = solution. ensemble. solutions[i]
112
+ antithetic_path = solution. ensemble. solutions[half_paths + i]
113
+
114
+ # Extract full paths for correlation
115
+ original_prices = [exp (u[1 ]) for u in original_path. u]
116
+ antithetic_prices = [exp (u[1 ]) for u in antithetic_path. u]
117
+
118
+ # Calculate correlation between full paths
119
+ path_corr = cor (original_prices, antithetic_prices)
120
+ push! (price_correlations, path_corr)
121
+
122
+ # Extract terminal values
123
+ original_terminal = exp (original_path. u[end ][1 ])
124
+ antithetic_terminal = exp (antithetic_path. u[end ][1 ])
125
+
126
+ # Calculate product of terminal values
127
+ push! (terminal_products, original_terminal * antithetic_terminal)
128
+
129
+ # Store terminal values for payoff calculation
130
+ push! (original_terminals, original_terminal)
131
+ push! (antithetic_terminals, antithetic_terminal)
132
+ end
133
+
134
+ # Calculate payoffs
135
+ original_payoffs = prob. payoff .(original_terminals)
136
+ antithetic_payoffs = prob. payoff .(antithetic_terminals)
137
+
138
+ # Calculate standard MC estimator variance (using original paths)
139
+ standard_estimator = discount_factor * original_payoffs
140
+ standard_variance = var (standard_estimator)
141
+
142
+ # Calculate antithetic MC estimator variance
143
+ antithetic_pairs = [(original_payoffs[i] + antithetic_payoffs[i]) / 2 for i in 1 : half_paths]
144
+ antithetic_estimator = discount_factor * antithetic_pairs
145
+ antithetic_variance = var (antithetic_estimator)
146
+
147
+ # Calculate payoff correlation
148
+ payoff_correlation = cor (original_payoffs, antithetic_payoffs)
149
+
150
+ # Calculate variance reduction
151
+ var_reduction_ratio = standard_variance / antithetic_variance
152
+
153
+ return (
154
+ mean_path_correlation = mean (price_correlations),
155
+ path_correlations = price_correlations,
156
+ mean_terminal_product = mean (terminal_products),
157
+ payoff_correlation = payoff_correlation,
158
+ standard_variance = standard_variance,
159
+ antithetic_variance = antithetic_variance,
160
+ var_reduction_ratio = var_reduction_ratio
161
+ )
162
+ end
163
+
164
+ # Run analysis and generate plots
165
+ num_paths = 100 # Use an even number
166
+ bk_solution, bk_prob = analyze_bk_antithetic_paths (num_paths, 42 )
167
+
168
+ # Plot several path pairs to observe different behaviors
169
+ p1 = plot_bk_path_pair (bk_solution, bk_prob, 1 )
170
+ p2 = plot_bk_path_pair (bk_solution, bk_prob, 2 )
171
+ p3 = plot_bk_path_pair (bk_solution, bk_prob, 3 )
172
+ p4 = plot_bk_path_pair (bk_solution, bk_prob, 4 )
173
+
174
+ # Plot them in a 2x2 grid
175
+ path_plots = plot (p1, p2, p3, p4, layout= (2 ,2 ), size= (900 , 700 ), title= " Broadie-Kaya Antithetic Path Examples" )
176
+ display (path_plots)
177
+
178
+ # Also plot some in log scale
179
+ p1_log = plot_bk_path_pair (bk_solution, bk_prob, 1 , apply_exp= false )
180
+ p2_log = plot_bk_path_pair (bk_solution, bk_prob, 2 , apply_exp= false )
181
+
182
+ log_plots = plot (p1_log, p2_log, layout= (1 ,2 ), size= (900 , 400 ), title= " Broadie-Kaya Paths (Log Scale)" )
183
+ display (log_plots)
184
+
185
+ # Calculate statistics across all paths
186
+ stats = calculate_bk_path_statistics (bk_solution, bk_prob)
187
+
188
+ # Print summary statistics
189
+ println (" === Broadie-Kaya Antithetic Path Analysis ===" )
190
+ println (" Number of paths: $num_paths " )
191
+ println (" Average path correlation: $(round (stats. mean_path_correlation, digits= 4 )) " )
192
+ println (" Payoff correlation: $(round (stats. payoff_correlation, digits= 4 )) " )
193
+ println (" Standard estimator variance: $(round (stats. standard_variance, digits= 8 )) " )
194
+ println (" Antithetic estimator variance: $(round (stats. antithetic_variance, digits= 8 )) " )
195
+ println (" Variance reduction ratio: $(round (stats. var_reduction_ratio, digits= 2 )) ×" )
196
+
197
+ # Plot distribution of path correlations
198
+ histogram! (
199
+ stats. path_correlations,
200
+ bins= 20 ,
201
+ title= " Distribution of Broadie-Kaya Path Correlations" ,
202
+ xlabel= " Correlation" ,
203
+ ylabel= " Frequency" ,
204
+ legend= false
205
+ )
0 commit comments