def test_cashflow_currency_conversion(self) -> None: model = _make_model() self.assertEqual(model.currencies, {"EUR", "USD"}) c = When(At(model.dategrid[-1]), Stock("ABC") * One("EUR")) cf_eur = c.generate_cashflows(model.eval_date_index, model) self.assertRaises(AssertionError, lambda: model.in_currency(cf_eur, "GBP")) cf_usd = model.in_currency(cf_eur, "USD") self.assertEqual(cf_eur.cashflows.shape, cf_usd.cashflows.shape) self.assertEqual(cf_usd.currencies[0], "USD") cf_eur_conv = model.in_numeraire_currency(cf_usd) self.assertEqual(cf_eur_conv.currencies[0], "EUR") self.assertTrue( np.allclose(cf_eur.cashflows["value"], cf_eur_conv.cashflows["value"]) ) # test with deterministic spots numeraire = np.ones((1, model.dategrid.size), dtype=np.float_) ccyspot = np.arange(1, model.dategrid.size + 1, dtype=np.float_).reshape( numeraire.shape ) model2 = Model( model.dategrid, {}, {("UND", "ACC"): ccyspot}, {}, numeraire, "UND" ) self.assertEqual(model2.currencies, {"UND", "ACC"}) for i, dt in enumerate(model2.dategrid): c = When(At(dt), One("UND")) cf_und = c.generate_cashflows(model2.eval_date_index, model2) cf_acc = model2.in_currency(cf_und, "ACC") self.assertEqual(cf_acc.cashflows["value"][0, 0], i + 1) for i, dt in enumerate(model2.dategrid): c = When(At(dt), One("ACC")) cf_acc = c.generate_cashflows(model2.eval_date_index, model2) cf_und = model2.in_numeraire_currency(cf_acc) self.assertEqual(cf_und.cashflows["value"][0, 0], 1 / (i + 1))
def test_rates(self) -> None: model = _make_model() dategrid = model.dategrid.flatten() yearfraction = (dategrid[-1] - dategrid[-3]).astype(np.float64) / 365 c = When( At(model.dategrid[-1]), FixedAfter(At(dategrid[-3]), LinearRate("EUR", "3M")) * yearfraction * One("EUR"), ) cf = model.generate_cashflows(c) self.assertEqual(cf.currencies.shape, (1,)) self.assertEqual(cf.cashflows.shape, (model.nsim, 1)) self.assertTrue((cf.cashflows["date"] == model.dategrid[-1]).all()) self.assertEqual(cf.currencies[0], "EUR")
def test_fixed_after(self) -> None: model = _make_model() fixed1 = FixedAfter(AlternatingBool(), Stock("ABC")) fixed1sim = fixed1.simulate(model.eval_date_index, model) altsim = AlternatingBool().simulate(model.eval_date_index, model) self.assertTrue( np.allclose(fixed1sim[altsim[:, 0], 0], fixed1sim[altsim[:, 0], -1]) ) self.assertFalse( np.isclose(fixed1sim[~altsim[:, 0], 0], fixed1sim[~altsim[:, 0], -1]).any() ) fixed2 = FixedAfter(At(model.dategrid[-2, 0]), Stock("ABC")) fixed2sim = fixed2.simulate(model.eval_date_index, model) self.assertTrue(np.allclose(fixed2sim[:, -2], fixed2sim[:, -1])) self.assertFalse(np.isclose(fixed2sim[:, -3], fixed2sim[:, -1]).any()) # ensure fixing condition is checked after first_observation_idx always = Stock("ABC") > 0 fixed3 = FixedAfter(always, Stock("ABC")) fixed3sim1 = fixed3.simulate(model.eval_date_index, model) self.assertTrue( np.allclose( fixed3sim1, np.repeat(model.simulated_stocks["ABC"][:, :1], model.ndates, axis=1), ) # all columns equal to first one as immediately fixed ) fixed3sim2 = fixed3.simulate( DateIndex(model.eval_date_index.index + model.ndates - 1), model ) self.assertTrue( np.allclose(fixed3sim2, Stock("ABC").simulate(model.eval_date_index, model)) )
def test_contract_creation(self) -> None: And( Or( Cond(AlternatingBool(), Zero(), One("USD")), When(At(np.datetime64("2030-07-14")), One("EUR")), ), Give(Scale(KonstFloat(1.23), One("USD"))), ) KonstFloat(1.23) KonstFloat(123)
def test_stock(self) -> None: model = _make_model() c = When(At(model.dategrid[-1]), Stock("ABC") * One("EUR")) cf = model.generate_cashflows(c) self.assertEqual(cf.currencies.shape, (1,)) self.assertEqual(cf.cashflows.shape, (model.nsim, 1)) self.assertTrue( (cf.cashflows["value"][:, 0] == model.simulated_stocks["ABC"][:, -1]).all() ) self.assertTrue((cf.cashflows["date"] == model.dategrid[-1]).all()) self.assertEqual(cf.currencies[0], "EUR")
def test_when_cashflow_generation(self) -> None: model = _make_model() cf = model.generate_cashflows(When(At(model.dategrid[0]), One("EUR"))) self.assertEqual(cf.currencies.shape, (1,)) self.assertEqual(cf.currencies[0], "EUR") self.assertEqual(cf.cashflows.shape, (model.nsim, 1)) self.assertTrue((cf.cashflows["value"] == 1).all()) self.assertTrue((cf.cashflows["date"] == model.dategrid[0]).all()) cf1 = model.generate_cashflows(When(At(model.dategrid[1]), One("EUR"))) self.assertEqual(cf1.currencies.shape, (1,)) self.assertTrue(cf1.currencies[0], "EUR") self.assertEqual(cf1.cashflows.shape, (model.nsim, 1)) self.assertTrue((cf1.cashflows["value"] == 1).all()) self.assertTrue((cf1.cashflows["date"] == model.dategrid[1]).all()) cf2 = model.generate_cashflows(When(AlternatingBool(), One("EUR"))) self.assertEqual(cf2.currencies.shape, (1,)) self.assertTrue(cf2.currencies[0], "EUR") self.assertEqual(cf2.cashflows.shape, (model.nsim, 1)) self.assertTrue( (cf2.cashflows["value"][np.arange(0, model.nsim, 2), :] == 0).all() ) self.assertTrue( (cf2.cashflows["value"][np.arange(1, model.nsim, 2), :] == 1).all() )
def test_date_index(self) -> None: model = _make_model() at0 = At(model.dategrid[0]) idx0 = model.eval_date_index.next_after( at0.simulate(model.eval_date_index, model) ) self.assertTrue((idx0.index == 0).all()) at1 = At(model.dategrid[1]) idx1 = model.eval_date_index.next_after( at1.simulate(model.eval_date_index, model) ) self.assertTrue((idx1.index == 1).all())
def test_equity_black_scholes(self) -> None: dategrid = np.arange( np.datetime64("2020-01-01"), np.datetime64("2020-01-10"), dtype="datetime64[D]", ) rnd = np.random.RandomState(seed=123) ndates = dategrid.size n = 100 m = simulate_equity_black_scholes_model( "ABC", "EUR", 123, dategrid, 0.2, 0.2, n, rnd, use_moment_matching=True ) self.assertIn("ABC", m.simulated_stocks) self.assertEqual(m.simulated_stocks["ABC"].shape, (n, ndates)) self.assertEqual(m.numeraire.shape, (n, ndates)) for t in dategrid: c = When(At(t), Stock("ABC") * One("EUR")) self.assertTrue(np.isclose(m.evaluate(c), 123))
def test_or_cashflow_generation(self) -> None: model = _make_model() c2 = One("EUR") | When(At(model.dategrid[-1]), One("EUR")) self.assertRaises(NotImplementedError, lambda: model.generate_cashflows(c2)) c3 = One("EUR") | 2 * One("EUR") cf = model.generate_cashflows(c3) self.assertEqual(cf.currencies.shape, (2,)) self.assertEqual(cf.currencies[0], "EUR") self.assertEqual(cf.currencies[1], "EUR") self.assertEqual(cf.cashflows.shape, (model.nsim, 2)) self.assertTrue((cf.cashflows["value"][:, 0] == 0).all()) self.assertTrue((np.isnat(cf.cashflows["date"][:, 0])).all()) self.assertTrue((cf.cashflows["value"][:, 1] == 2).all()) self.assertTrue((cf.cashflows["date"][:, 1] == model.eval_date).all()) c4 = One("EUR") | One("USD") cf4 = model.generate_cashflows(c4) self.assertEqual(cf4.currencies.shape, (2,)) self.assertEqual(cf4.currencies[0], "EUR") self.assertEqual(cf4.currencies[1], "USD")
def test_discounting(self) -> None: dategrid = np.arange( np.datetime64("2020-01-01"), np.datetime64("2020-06-01"), 30, dtype="datetime64[D]", ) rnd = np.random.RandomState(seed=123) n = 100 rate = 0.03 model = simulate_equity_black_scholes_model( "ABC", "USD", 123, dategrid, 0.2, rate, n, rnd, use_moment_matching=True ) for t in dategrid: c = When(At(t), One("USD")) cf = c.generate_cashflows(model.eval_date_index, model) npv = model.discount(cf)[:, 0].mean() self.assertEqual(model.evaluate(c), npv) self.assertEqual(model.evaluate(cf), npv) dt = (t - dategrid[0]).astype(np.float64) / 365 self.assertTrue(np.isclose(npv, np.exp(-rate * dt))) model.evaluate(When(Stock("ABC") > 130, One("USD")))
def test_contract_str(self) -> None: c = ( Cond((Stock("ABC") > 28) & ~(Stock("DEF") > 28), Zero(), One("USD")) | When(At(np.datetime64("2030-07-14")), One("EUR")) ) + ( Until(FX("EUR", "USD") < 1.0, -(1.23 * One("USD"))) + Anytime( (Stock("DEF") >= 50) | (Stock("DEF") < 20), Stock("ABC") * One("EUR"), ) ) expected = ( "And(Or(Cond((Stock(ABC) > 28) & (~(Stock(DEF) > 28)), Zero, One(USD)), " "When(2030-07-14, One(EUR))), And(Until(~(FX(EUR/USD) >= 1.0), " "Give(Scale(1.23, One(USD)))), Anytime((Stock(DEF) >= 50) " "| (~(Stock(DEF) >= 20)), Scale(Stock(ABC), One(EUR)))))" ) self.assertEqual(str(c), expected) c2 = (Stock("ABC") ** 2 / (Stock("DEF") - 1.7) + 42) * One("EUR") self.assertEqual( str(c2), "Scale((((Stock(ABC)) ** (2)) / ((Stock(DEF)) + (-1.7))) + (42), One(EUR))", )
def test_boolean_obs_at(self) -> None: model = _make_model() at0 = At(model.dategrid[0]) at0sim = at0.simulate(model.eval_date_index, model) self.assertEqual(at0sim.dtype, np.bool_) self.assertEqual(at0sim.shape, model.shape) self.assertTrue(at0sim[:, 0].all()) self.assertFalse(at0sim[:, 1].any()) at1 = At(model.dategrid[1]) at1sim = at1.simulate(model.eval_date_index, model) self.assertEqual(at1sim.dtype, np.bool_) self.assertEqual(at1sim.shape, model.shape) self.assertFalse(at1sim[:, 0].any()) self.assertTrue(at1sim[:, 1].all()) alt = AlternatingBool() altsim = alt.simulate(model.eval_date_index, model) self.assertFalse(altsim[np.arange(0, model.nsim, 2), :].any()) self.assertTrue(altsim[np.arange(1, model.nsim, 2), :].all())
def resolve(self) -> Contract: return When(At(self.maturity), self.notional * One("EUR"))
def test_evaluation(self) -> None: model = _make_model() c = When(At(model.dategrid[-1]), One("EUR")) npv = model.evaluate(c) self.assertEqual(npv, 1)