@@ -34,6 +34,47 @@ class TargetRedemptionForward {
3434 settlement_date_frequency_(settlement_date_frequency),
3535 direction_(direction) {}
3636
37+ /* Returns the weighted average of the forward FX rate, weighted by the
38+ discount factors.
39+
40+ As a simple example, suppose spot is at 100 and there are 3 periods of
41+ interest, with the following forward FX rates and the following discount
42+ factors from the `domestic_rates` curve:
43+
44+ t (yrs) fwd df
45+ ------- --- ---
46+ 1 102 0.95
47+ 2 104 0.90
48+ 3 106 0.85
49+
50+ Then this function would return ~103.9259
51+ or in spreadsheet pseudocode:
52+ = sumproduct(fwd, df)/sum(df)
53+ */
54+ double weightedAvgForward (double spot,
55+ const RatesCurve& foreign_rates,
56+ const RatesCurve& domestic_rates) const {
57+ double sumproduct = 0 .;
58+ double fx = spot;
59+ double df_sum = 0 .;
60+
61+ int num_payments = std::round (end_date_years_ / settlement_date_frequency_);
62+ for (int i = 1 ; i <= num_payments; ++i) {
63+ double t_init = (i - 1 ) * settlement_date_frequency_;
64+ double t_final = i * settlement_date_frequency_;
65+ double rd = domestic_rates.forwardRate (t_init, t_final);
66+ double rf = foreign_rates.forwardRate (t_init, t_final);
67+ fx *= std::exp ((rd - rf) * settlement_date_frequency_);
68+ sumproduct += fx * domestic_rates.df (t_final);
69+ df_sum += domestic_rates.df (t_final);
70+ }
71+
72+ // Sanity check in case of degenerate case.
73+ if (df_sum <= 0 .) return 0 .;
74+
75+ return sumproduct / df_sum;
76+ }
77+
3778 // Initial implementation: given a flat volatility and a specified forward,
3879 // price the scenarios using the provided discount curve. (It is up to the
3980 // client to ensure that the current discount curve is provided, otherwise a
@@ -162,15 +203,23 @@ inline double findZeroNPVStrike(double notional,
162203 // TODO: compute the forward for a more intelligent starting guess.
163204 // AND ALSO then verify that the value of one is positive and the other is
164205 // negative.
165- double k_low = spot * 0.5 ;
166- double k_high = spot * 2 ;
206+ TargetRedemptionForward tarf_forward (notional,
207+ target,
208+ spot,
209+ end_date_years,
210+ settlement_date_frequency,
211+ direction);
212+ double atm_fwd =
213+ tarf_forward.weightedAvgForward (spot, foreign_rates, domestic_rates);
214+ double k_low = atm_fwd * 0.5 ;
215+ double k_high = atm_fwd * 1.1 ;
167216 double k_mid = 0.5 * (k_low + k_high);
168217
169218 double tolerance_pct =
170219 0.0001 ; // 0.01% difference for starters. Do not hard-code!
171220
172- // TODO is there ever a need to have this be smaller than the period?
173- double dt = settlement_date_frequency * 0.5 ;
221+ // Relatively coarse timesteps.
222+ double dt = settlement_date_frequency * 0.2 ;
174223
175224 // Initial method: bisection.
176225 while (std::abs (k_high / k_low - 1 ) > tolerance_pct) {
@@ -182,7 +231,7 @@ inline double findZeroNPVStrike(double notional,
182231 direction);
183232
184233 double npv_mid =
185- tarf_mid.price (spot, sigma, dt, 4000 , foreign_rates, domestic_rates);
234+ tarf_mid.price (spot, sigma, dt, 5000 , foreign_rates, domestic_rates);
186235
187236 if (npv_mid > 0 ) {
188237 k_low = k_mid;
0 commit comments