Skip to content

Commit 5eb4149

Browse files
committed
Add findZeroNPVStrike first-draft (naive bisection method)
1 parent baa760c commit 5eb4149

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

derivatives/target_redemption_forward.h

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ class TargetRedemptionForward {
7878
while (t < end_date_years_ && !trigger_reached) {
7979
const double z = absl::Gaussian<double>(bitgen_, 0, 1);
8080
const double stoch_term = sigma * std::sqrt(dt) * z;
81-
// double r_d = domestic_rates.forwardRate(t, t + dt);
82-
// double r_f = foreign_rates.forwardRate(t, t + dt);
8381
const double drift_term = (r_d - r_f - 0.5 * sigma * sigma) * dt;
8482

8583
t += dt;
@@ -152,6 +150,50 @@ class TargetRedemptionForward {
152150
mutable absl::BitGen bitgen_;
153151
};
154152

153+
inline double findZeroNPVStrike(double notional,
154+
double target,
155+
double end_date_years,
156+
double settlement_date_frequency,
157+
FxTradeDirection direction,
158+
double spot,
159+
double sigma,
160+
const RatesCurve& foreign_rates,
161+
const RatesCurve& domestic_rates) {
162+
// TODO: compute the forward for a more intelligent starting guess.
163+
// AND ALSO then verify that the value of one is positive and the other is
164+
// negative.
165+
double k_low = spot * 0.5;
166+
double k_high = spot * 2;
167+
double k_mid = 0.5 * (k_low + k_high);
168+
169+
double tolerance_pct =
170+
0.0001; // 0.01% difference for starters. Do not hard-code!
171+
172+
// TODO is there ever a need to have this be smaller than the period?
173+
double dt = settlement_date_frequency * 0.5;
174+
175+
// Initial method: bisection.
176+
while (std::abs(k_high / k_low - 1) > tolerance_pct) {
177+
TargetRedemptionForward tarf_mid(notional,
178+
target,
179+
k_mid,
180+
end_date_years,
181+
settlement_date_frequency,
182+
direction);
183+
184+
double npv_mid =
185+
tarf_mid.price(spot, sigma, dt, 4000, foreign_rates, domestic_rates);
186+
187+
if (npv_mid > 0) {
188+
k_low = k_mid;
189+
} else if (npv_mid < 0) {
190+
k_high = k_mid;
191+
}
192+
k_mid = 0.5 * (k_low + k_high);
193+
}
194+
return k_mid;
195+
}
196+
155197
} // namespace smileexplorer
156198

157199
#endif // SMILEEXPLORER_DERIVATIVES_TARGET_REDEMPTION_FORWARD_H_

derivatives/target_redemption_forward_test.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,56 @@ TEST_F(TargetRedemptionForwardTest, KnockoutAlmostDeterministic) {
107107
}
108108
}
109109

110+
TEST_F(TargetRedemptionForwardTest, VegaIsNegative) {
111+
TargetRedemptionForward tarf(
112+
1e6, 6e6, 131., 4.0, 0.25, FxTradeDirection::kLong);
113+
114+
double vol_low = 0.05;
115+
double npv_vol_lower =
116+
tarf.price(125., vol_low, 0.1, 10000, *foreign_curve_, *domestic_curve_);
117+
double npv_vol_higher = tarf.price(
118+
125., vol_low + 0.01, 0.1, 10000, *foreign_curve_, *domestic_curve_);
119+
120+
EXPECT_GT(npv_vol_lower, npv_vol_higher);
121+
}
122+
123+
TEST_F(TargetRedemptionForwardTest, FindZeroNPVStrike) {
124+
const double strike = findZeroNPVStrike(1e6,
125+
100e6,
126+
4,
127+
0.25,
128+
FxTradeDirection::kLong,
129+
125,
130+
0.0001,
131+
*foreign_curve_,
132+
*domestic_curve_);
133+
134+
EXPECT_NEAR(135.657, strike, 0.002);
135+
}
136+
137+
TEST_F(TargetRedemptionForwardTest, LoweringTargetReducesLongStrike) {
138+
const double strike_6mm = findZeroNPVStrike(1e6,
139+
6e6,
140+
4,
141+
0.25,
142+
FxTradeDirection::kLong,
143+
125,
144+
0.05,
145+
*foreign_curve_,
146+
*domestic_curve_);
147+
148+
const double strike_5mm = findZeroNPVStrike(1e6,
149+
5e6,
150+
4,
151+
0.25,
152+
FxTradeDirection::kLong,
153+
125,
154+
0.05,
155+
*foreign_curve_,
156+
*domestic_curve_);
157+
158+
EXPECT_LT(strike_5mm, strike_6mm);
159+
}
160+
110161
} // namespace
111162
} // namespace smileexplorer

0 commit comments

Comments
 (0)