def test_intrinsic_value_runs(self): ratchets = [ (date(2019, 8, 28), [ (0.0, -150.0, 255.2), (2000.0, -200.0, 175.0), ]), (date(2019, 9, 10), [ (0.0, -170.5, 235.8), (700.0, -180.2, 200.77), (1800.0, -190.5, 174.45), ]) ] storage_start = date(2019, 8, 28) storage_end = date(2019, 9, 25) constant_injection_cost = 0.015 constant_pcnt_consumed_inject = 0.0001 constant_withdrawal_cost = 0.02 constant_pcnt_consumed_withdraw = 0.000088 constant_pcnt_inventory_loss = 0.001; constant_pcnt_inventory_cost = 0.002; def terminal_npv_calc(price, inventory): return price * inventory - 15.4 # Some arbitrary calculation cmdty_storage = cs.CmdtyStorage('D', storage_start, storage_end, constant_injection_cost, constant_withdrawal_cost, ratchets, ratchet_interp=cs.RatchetInterp.LINEAR, cmdty_consumed_inject=constant_pcnt_consumed_inject, cmdty_consumed_withdraw=constant_pcnt_consumed_withdraw, terminal_storage_npv=terminal_npv_calc, inventory_loss=constant_pcnt_inventory_loss, inventory_cost=constant_pcnt_inventory_cost) inventory = 650.0 val_date = date(2019, 9, 2) forward_curve = utils.create_piecewise_flat_series([58.89, 61.41, 59.89, 59.89], [val_date, date(2019, 9, 12), date(2019, 9, 18), storage_end], freq='D') # TODO test with proper interest rate curve flat_interest_rate = 0.03 interest_rate_curve = pd.Series(index = pd.period_range(val_date, storage_end + timedelta(days=60), freq='D')) interest_rate_curve[:] = flat_interest_rate twentieth_of_next_month = lambda period: period.asfreq('M').asfreq('D', 'end') + 20 intrinsic_results = cs.intrinsic_value(cmdty_storage, val_date, inventory, forward_curve, settlement_rule=twentieth_of_next_month, interest_rates=interest_rate_curve, num_inventory_grid_points=100)
def test_storage_value_date_equals_storage_end_returns_zero_npv_empty_profile( self): storage_start = date(2019, 8, 28) storage_end = date(2019, 9, 25) cmdty_storage = cs.CmdtyStorage('D', storage_start, storage_end, injection_cost=0.1, withdrawal_cost=0.2, min_inventory=0, max_inventory=1000, max_injection_rate=2.5, max_withdrawal_rate=3.6) inventory = 0.0 val_date = date(2019, 9, 25) forward_curve = utils.create_piecewise_flat_series( [58.89, 61.41, 70.89, 70.89], [storage_start, date(2019, 9, 12), date(2019, 9, 18), storage_end], freq='D') flat_interest_rate = 0.03 interest_rate_curve = pd.Series(index=pd.period_range( val_date, storage_end + timedelta(days=60), freq='D')) interest_rate_curve[:] = flat_interest_rate twentieth_of_next_month = lambda period: period.asfreq('M').asfreq( 'D', 'end') + 20 intrinsic_results = cs.intrinsic_value( cmdty_storage, val_date, inventory, forward_curve, settlement_rule=twentieth_of_next_month, interest_rates=interest_rate_curve, num_inventory_grid_points=100) self.assertEqual(0.0, intrinsic_results.npv) self.assertEqual(0, len(intrinsic_results.profile))
def test_trinomial_value_runs(self): constraints = [(date(2019, 8, 28), [ (0.0, -150.0, 255.2), (2000.0, -200.0, 175.0), ]), (date(2019, 9, 10), [ (0.0, -170.5, 235.8), (700.0, -180.2, 200.77), (1800.0, -190.5, 174.45), ])] storage_start = date(2019, 8, 28) storage_end = date(2019, 9, 25) constant_injection_cost = 0.015 constant_pcnt_consumed_inject = 0.0001 constant_withdrawal_cost = 0.02 constant_pcnt_consumed_withdraw = 0.000088 constant_pcnt_inventory_loss = 0.001 constant_pcnt_inventory_cost = 0.002 def terminal_npv_calc(price, inventory): return price * inventory - 15.4 # Some arbitrary calculation cmdty_storage = cs.CmdtyStorage( 'D', storage_start, storage_end, constant_injection_cost, constant_withdrawal_cost, constraints, cmdty_consumed_inject=constant_pcnt_consumed_inject, cmdty_consumed_withdraw=constant_pcnt_consumed_withdraw, terminal_storage_npv=terminal_npv_calc, inventory_loss=constant_pcnt_inventory_loss, inventory_cost=constant_pcnt_inventory_cost) inventory = 650.0 val_date = date(2019, 9, 2) forward_curve = utils.create_piecewise_flat_series( [58.89, 61.41, 59.89, 59.89], [val_date, date(2019, 9, 12), date(2019, 9, 18), storage_end], freq='D') # TODO test with proper interest rate curve flat_interest_rate = 0.03 interest_rate_curve = pd.Series(index=pd.period_range( val_date, storage_end + timedelta(days=60), freq='D')) interest_rate_curve[:] = flat_interest_rate # Trinomial Tree parameters mean_reversion = 14.5 spot_volatility = utils.create_piecewise_flat_series( [1.35, 1.13, 1.24, 1.24], [val_date, date(2019, 9, 12), date(2019, 9, 18), storage_end], freq='D') time_step = 1.0 / 365.0 twentieth_of_next_month = lambda period: period.asfreq('M').asfreq( 'D', 'end') + 20 trinomial_value = cs.trinomial_value( cmdty_storage, val_date, inventory, forward_curve, spot_volatility, mean_reversion, time_step, settlement_rule=twentieth_of_next_month, interest_rates=interest_rate_curve, num_inventory_grid_points=100) self.assertTrue(isinstance(trinomial_value, float))
def test_trinomial_delta_deep_itm_equals_intrinsic_delta(self): storage_start = '2019-12-01' storage_end = '2020-04-01' constant_injection_rate = 700.0 constant_withdrawal_rate = 700.0 constant_injection_cost = 1.23 constant_withdrawal_cost = 0.98 min_inventory = 0.0 max_inventory = 100000.0 cmdty_storage = cs.CmdtyStorage( 'D', storage_start, storage_end, constant_injection_cost, constant_withdrawal_cost, min_inventory=min_inventory, max_inventory=max_inventory, max_injection_rate=constant_injection_rate, max_withdrawal_rate=constant_withdrawal_rate) inventory = 0.0 val_date = '2019-08-29' low_price = 23.87 high_price = 150.32 num_days_at_high_price = 20 date_switch_high_price = '2020-03-12' # TODO calculate this from num_days_at_high_price forward_curve = utils.create_piecewise_flat_series( [low_price, high_price, high_price], [val_date, date_switch_high_price, storage_end], freq='D') flat_interest_rate = 0.00 interest_rate_curve = pd.Series( index=pd.period_range(val_date, '2020-06-01', freq='D')) interest_rate_curve[:] = flat_interest_rate # Trinomial Tree parameters mean_reversion = 14.5 spot_volatility = pd.Series( index=pd.period_range(val_date, '2020-06-01', freq='D')) spot_volatility[:] = 1.15 time_step = 1.0 / 365.0 twentieth_of_next_month = lambda period: period.asfreq('M').asfreq( 'D', 'end') + 20 delta_fwd_contracts = [(storage_start, '2020-03-11'), (date_switch_high_price, storage_end)] trinomial_deltas = cs.trinomial_deltas( cmdty_storage, val_date, inventory, forward_curve, spot_volatility, mean_reversion, time_step, interest_rates=interest_rate_curve, settlement_rule=twentieth_of_next_month, fwd_contracts=delta_fwd_contracts, num_inventory_grid_points=500) withdraw_delta = trinomial_deltas[1] expected_withdraw_delta = constant_withdrawal_rate * num_days_at_high_price pcnt_error = (withdraw_delta - expected_withdraw_delta) / expected_withdraw_delta self.assertAlmostEqual(pcnt_error, 0.0, 3) self.assertTrue(isinstance(trinomial_deltas, list))
class TestCmdtyStorage(unittest.TestCase): _default_freq = 'D' _default_ratchets = ( (date(2019, 8, 28), ( (0.0, -150.0, 255.2), (2000.0, -200.0, 175.0), )), (date(2019, 9, 10), ( (0.0, -170.5, 235.8), (700.0, -180.2, 200.77), (1800.0, -190.5, 174.45), )) ) _constant_min_inventory = 2.54 _constant_max_inventory = 1234.56 _constant_max_injection_rate = 65.64 _constant_max_withdrawal_rate = 107.07 _series_min_inventory = utils.create_piecewise_flat_series([2.4, 1.2, 0.0, 0.0], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _series_max_inventory = utils.create_piecewise_flat_series([1250.5, 1358.5, 54.5, 54.5], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _series_max_injection_rate = utils.create_piecewise_flat_series([125.5, 100, 120.66, 120.66], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _series_max_withdrawal_rate = utils.create_piecewise_flat_series([211.52, 200, 220.66, 220.66], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _default_storage_start = date(2019, 8, 28) _default_storage_end = date(2019, 9, 25) _default_ratchets_interp = cs.RatchetInterp.LINEAR _constant_injection_cost = 0.015 _constant_cmdty_consumed_inject = 0.0001 _constant_withdrawal_cost = 0.02 _constant_cmdty_consumed_withdraw = 0.000088 _constant_inventory_loss = 0.001 _constant_inventory_cost = 0.002 _series_injection_cost = utils.create_piecewise_flat_series([1.41384, 2.284, 0.75, 0.75], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _series_cmdty_consumed_inject = utils.create_piecewise_flat_series([0.438, 0.413, 4.434, 4.434], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _series_withdrawal_cost = utils.create_piecewise_flat_series([0.143, 0.248, 5, 5], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _series_cmdty_consumed_withdraw = utils.create_piecewise_flat_series([0.045, 0.0415, 2, 2], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _series_inventory_loss = utils.create_piecewise_flat_series([0.003, 0.0015, 0.0017, 0.0017], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _series_inventory_cost = utils.create_piecewise_flat_series([0.04, 0.02, 0.055, 0.055], [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 10), date(2019, 9, 25)], 'D') _default_terminal_npv_calc = lambda price, inventory: price * inventory - 15.4 # Some arbitrary calculation def _create_storage(cls, freq=_default_freq, storage_start=_default_storage_start, storage_end=_default_storage_end, ratchets=_default_ratchets, ratchet_interp = _default_ratchets_interp, min_inventory=None, max_inventory=None, max_injection_rate=None, max_withdrawal_rate=None, injection_cost=_constant_injection_cost, withdrawal_cost=_constant_withdrawal_cost, cmdty_consumed_inject=_constant_cmdty_consumed_inject, cmdty_consumed_withdraw=_constant_cmdty_consumed_withdraw, terminal_storage_npv=_default_terminal_npv_calc, inventory_loss=_constant_inventory_loss, inventory_cost=_constant_inventory_cost): return cs.CmdtyStorage(freq, storage_start, storage_end, injection_cost, withdrawal_cost, ratchets=ratchets, ratchet_interp=ratchet_interp, min_inventory=min_inventory, max_inventory=max_inventory, max_injection_rate=max_injection_rate, max_withdrawal_rate=max_withdrawal_rate, cmdty_consumed_inject=cmdty_consumed_inject, cmdty_consumed_withdraw=cmdty_consumed_withdraw, terminal_storage_npv=terminal_storage_npv, inventory_loss=inventory_loss, inventory_cost=inventory_cost) def test_ratchets_step_interp_as_expected(self): step_ratchets = (('2019-08-28', ( (0.0, -150.0, 255.2), (2000.0, -150.0, 255.2), )), ('2019-09-10', ( (0.0, -170.5, 235.8), (700.0, -180.2, 200.77), (1800.0, -180.2, 200.77), ))) storage = self._create_storage(ratchets=step_ratchets, ratchet_interp=cs.RatchetInterp.STEP) for inventory in [0.0, 1252.5, 1999.0]: with_rate, inj_rate = storage.inject_withdraw_range('2019-09-05', inventory) self.assertEqual(-150.0, with_rate) self.assertEqual(255.2, inj_rate) def test_init_ratchets_arg_not_none_other_constraint_args_not_none_raises(self): with self.assertRaisesRegex(ValueError, "min_inventory parameter should not be provided if ratchets parameter is provided."): storage = self._create_storage(ratchets=self._default_ratchets, min_inventory=self._constant_min_inventory) with self.assertRaisesRegex(ValueError, "max_inventory parameter should not be provided if ratchets parameter is provided."): storage = self._create_storage(ratchets=self._default_ratchets, max_inventory=self._constant_max_inventory) with self.assertRaisesRegex(ValueError, "max_injection_rate parameter should not be provided if ratchets parameter is provided."): storage = self._create_storage(ratchets=self._default_ratchets, max_injection_rate=self._constant_max_injection_rate) with self.assertRaisesRegex(ValueError, "max_withdrawal_rate parameter should not be provided if ratchets parameter is provided."): storage = self._create_storage(ratchets=self._default_ratchets, max_withdrawal_rate=self._constant_max_withdrawal_rate) def test_init_ratchets_arg_none_other_constraint_args_none_raises(self): with self.assertRaisesRegex(ValueError, "min_inventory parameter should be provided if ratchets parameter is not provided."): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=None, max_inventory=self._constant_max_inventory, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=self._constant_max_withdrawal_rate) with self.assertRaisesRegex(ValueError, "max_inventory parameter should be provided if ratchets parameter is not provided."): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=None, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=self._constant_max_withdrawal_rate) with self.assertRaisesRegex(ValueError, "max_injection_rate parameter should be provided if ratchets parameter is not provided."): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=None, max_withdrawal_rate=self._constant_max_withdrawal_rate) with self.assertRaisesRegex(ValueError, "max_withdrawal_rate parameter should be provided if ratchets parameter is not provided."): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=None) def test_init_ratchets_arg_none_ratchets_interp_not_none_raises(self): with self.assertRaisesRegex(ValueError, "ratchet_interp should not be provided if ratchets parameter is not provided."): storage = self._create_storage(ratchets=None, ratchet_interp=cs.RatchetInterp.LINEAR, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=self._constant_max_withdrawal_rate) def test_init_ratchets_arg_not_none_ratchets_none_raises(self): with self.assertRaisesRegex(ValueError, "ratchet_interp parameter should be provided if ratchets parameter is provided."): storage = self._create_storage(ratchets=self._default_ratchets, ratchet_interp=None, min_inventory=None, max_inventory=None, max_injection_rate=None, max_withdrawal_rate=None) def test_start_property(self): storage = self._create_storage() self.assertEqual(pd.Period(self._default_storage_start, freq='D'), storage.start) def test_end_property(self): storage = self._create_storage() self.assertEqual(pd.Period(self._default_storage_end, freq='D'), storage.end) def test_freq_property(self): storage = self._create_storage() self.assertEqual(self._default_freq, storage.freq) def test_empty_at_end_true_when_terminal_storage_npv_none(self): storage = self._create_storage(terminal_storage_npv=None) self.assertEqual(True, storage.empty_at_end) def test_empty_at_end_false_when_terminal_storage_npv_not_none(self): storage = self._create_storage() self.assertEqual(False, storage.empty_at_end) def test_terminal_storage_npv_always_zero_when_terminal_storage_npv_none(self): storage = self._create_storage(terminal_storage_npv=None) for cmdty_price in [0.0, 23.85, 75.9, 100.22]: for terminal_inventory in [0.0, 500.58, 1268.65, 1800.0]: self.assertEqual(0.0, storage.terminal_storage_npv(cmdty_price, terminal_inventory)) def test_terminal_storage_npv_evaluates_to_function_specified(self): storage = self._create_storage() for cmdty_price in [0.0, 23.85, 75.9, 100.22]: for terminal_inventory in [0.0, 500.58, 1268.65, 1800.0]: self.assertEqual(TestCmdtyStorage._default_terminal_npv_calc(cmdty_price, terminal_inventory), storage.terminal_storage_npv(cmdty_price, terminal_inventory)) def test_inject_withdraw_range_linearly_interpolated(self): storage = self._create_storage() # Inventory half way between pillars, so assert against mean of min/max inject/withdraw at the pillars min_dec, max_dec = storage.inject_withdraw_range(date(2019, 8, 29), 1000.0) self.assertEqual(-175.0, min_dec) self.assertEqual((255.2 + 175.0)/2.0, max_dec) def test_inject_withdraw_range_from_float_init_parameters(self): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=self._constant_max_withdrawal_rate) for inventory in [2.54, 500.58, 1234.56]: for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: min_dec, max_dec = storage.inject_withdraw_range(dt, inventory) self.assertEqual(-self._constant_max_withdrawal_rate, min_dec) self.assertEqual(self._constant_max_injection_rate, max_dec) def test_inject_withdraw_range_from_int_init_parameters(self): int_max_injection_rate = int(self._constant_max_injection_rate) int_max_withdrawal_rate = int(self._constant_max_withdrawal_rate) storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=int_max_injection_rate, max_withdrawal_rate=int_max_withdrawal_rate) for inventory in [2.54, 500.58, 1234.56]: for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: min_dec, max_dec = storage.inject_withdraw_range(dt, inventory) self.assertEqual(-int_max_withdrawal_rate, min_dec) self.assertEqual(int_max_injection_rate, max_dec) def test_inject_withdraw_range_from_series_init_parameters(self): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=self._series_max_injection_rate, max_withdrawal_rate=self._series_max_withdrawal_rate) for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: expected_min_dec = -self._series_max_withdrawal_rate[dt] expeted_max_dec = self._series_max_injection_rate[dt] for inventory in [2.54, 500.58, 1234.56]: min_dec, max_dec = storage.inject_withdraw_range(dt, inventory) self.assertEqual(expected_min_dec, min_dec) self.assertEqual(expeted_max_dec, max_dec) def test_inject_withdraw_range_from_series_max_injection_rate_init_parameters(self): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=self._series_max_injection_rate, max_withdrawal_rate=self._constant_max_withdrawal_rate) for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: expected_min_dec = -self._constant_max_withdrawal_rate expeted_max_dec = self._series_max_injection_rate[dt] for inventory in [2.54, 500.58, 1234.56]: min_dec, max_dec = storage.inject_withdraw_range(dt, inventory) self.assertEqual(expected_min_dec, min_dec) self.assertEqual(expeted_max_dec, max_dec) def test_inject_withdraw_range_from_series_max_withdrawal_rate_init_parameters(self): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=self._series_max_withdrawal_rate) for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: expected_min_dec = -self._series_max_withdrawal_rate[dt] expeted_max_dec = self._constant_max_injection_rate for inventory in [2.54, 500.58, 1234.56]: min_dec, max_dec = storage.inject_withdraw_range(dt, inventory) self.assertEqual(expected_min_dec, min_dec) self.assertEqual(expeted_max_dec, max_dec) def test_min_inventory_property_from_constraints_table(self): storage = self._create_storage() self.assertEqual(0.0, storage.min_inventory(date(2019, 8, 29))) self.assertEqual(0.0, storage.min_inventory(date(2019, 9, 11))) def test_min_inventory_property_from_float_init_param(self): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=self._constant_max_withdrawal_rate) self.assertEqual(self._constant_min_inventory, storage.min_inventory(date(2019, 8, 29))) self.assertEqual(self._constant_min_inventory, storage.min_inventory(date(2019, 9, 11))) def test_min_inventory_property_from_series_init_param(self): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._series_min_inventory, max_inventory=self._series_max_inventory, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=self._constant_max_withdrawal_rate) self.assertEqual(2.4, storage.min_inventory(date(2019, 8, 29))) self.assertEqual(1.2, storage.min_inventory(date(2019, 9, 1))) self.assertEqual(0.0, storage.min_inventory(date(2019, 9, 11))) def test_max_inventory_property_from_float_init_param(self): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._constant_min_inventory, max_inventory=self._constant_max_inventory, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=self._constant_max_withdrawal_rate) self.assertEqual(self._constant_max_inventory, storage.max_inventory(date(2019, 8, 29))) self.assertEqual(self._constant_max_inventory, storage.max_inventory(date(2019, 9, 11))) def test_max_inventory_property_from_series_init_param(self): storage = self._create_storage(ratchets=None, ratchet_interp=None, min_inventory=self._series_min_inventory, max_inventory=self._series_max_inventory, max_injection_rate=self._constant_max_injection_rate, max_withdrawal_rate=self._constant_max_withdrawal_rate) self.assertEqual(1250.5, storage.max_inventory(date(2019, 8, 29))) self.assertEqual(1358.5, storage.max_inventory(date(2019, 9, 1))) self.assertEqual(54.5, storage.max_inventory(date(2019, 9, 11))) def test_max_inventory_property_from_constraints_table(self): storage = self._create_storage() self.assertEqual(2000.0, storage.max_inventory(date(2019, 8, 29))) self.assertEqual(1800.0, storage.max_inventory(date(2019, 9, 11))) def test_injection_cost_scalar_init_parameter(self): storage = self._create_storage() injected_volume = 58.74 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: for inventory in [0, 500.58, 1234.56, 1800]: injection_cost = storage.injection_cost(dt, inventory, injected_volume) self.assertEqual(injected_volume * self._constant_injection_cost, injection_cost) def test_injection_cost_series_init_parameter(self): storage = self._create_storage(injection_cost=self._series_injection_cost) injected_volume = 58.74 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: expected_injection_cost = self._series_injection_cost[dt]*injected_volume for inventory in [0, 500.58, 1234.56, 1800]: injection_cost = storage.injection_cost(dt, inventory, injected_volume) self.assertEqual(expected_injection_cost, injection_cost) def test_cmdty_consumed_inject_scalar_init_parameter(self): storage = self._create_storage() injected_volume = 58.74 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: for inventory in [2.54, 500.58, 1234.56]: cmdty_consumed_inject = storage.cmdty_consumed_inject(dt, inventory, injected_volume) self.assertEqual(injected_volume * self._constant_cmdty_consumed_inject, cmdty_consumed_inject) def test_cmdty_consumed_inject_none_init_parameter(self): storage = self._create_storage(cmdty_consumed_inject=None) injected_volume = 58.74 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: for inventory in [2.54, 500.58, 1234.56]: cmdty_consumed_inject = storage.cmdty_consumed_inject(dt, inventory, injected_volume) self.assertEqual(0, cmdty_consumed_inject) def test_cmdty_consumed_inject_series_init_parameter(self): storage = self._create_storage(cmdty_consumed_inject=self._series_cmdty_consumed_inject) injected_volume = 58.74 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: expected_cmdty_consumed_inject = self._series_cmdty_consumed_inject[dt] * injected_volume for inventory in [2.54, 500.58, 1234.56]: cmdty_consumed_inject = storage.cmdty_consumed_inject(dt, inventory, injected_volume) self.assertEqual(expected_cmdty_consumed_inject, cmdty_consumed_inject) def test_withdrawal_cost_scalar_init_parameter(self): storage = self._create_storage() withdrawn_volume = 12.05 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: for inventory in [0, 500.58, 1234.56, 1800]: withdrawal_cost = storage.withdrawal_cost(dt, inventory, withdrawn_volume) self.assertEqual(withdrawn_volume * self._constant_withdrawal_cost, withdrawal_cost) def test_withdrawal_cost_series_init_parameter(self): storage = self._create_storage(withdrawal_cost=self._series_withdrawal_cost) withdrawn_volume = 12.05 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: expected_withdrawal_cost = self._series_withdrawal_cost[dt] * withdrawn_volume for inventory in [0, 500.58, 1234.56, 1800]: withdrawal_cost = storage.withdrawal_cost(dt, inventory, withdrawn_volume) self.assertEqual(expected_withdrawal_cost, withdrawal_cost) def test_cmdty_consumed_withdraw_scalar_init_parameter(self): storage = self._create_storage() withdrawn_volume = 12.05 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: for inventory in [2.54, 500.58, 1234.56]: cmdty_consumed_withdraw = storage.cmdty_consumed_withdraw(dt, inventory, withdrawn_volume) self.assertEqual(withdrawn_volume * self._constant_cmdty_consumed_withdraw, cmdty_consumed_withdraw) def test_cmdty_consumed_withdraw_none_init_parameter(self): storage = self._create_storage(cmdty_consumed_withdraw=None) withdrawn_volume = 12.05 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: for inventory in [2.54, 500.58, 1234.56]: cmdty_consumed_withdraw = storage.cmdty_consumed_withdraw(dt, inventory, withdrawn_volume) self.assertEqual(0, cmdty_consumed_withdraw) def test_cmdty_consumed_withdraw_series_init_parameter(self): storage = self._create_storage(cmdty_consumed_withdraw=self._series_cmdty_consumed_withdraw) withdrawn_volume = 12.05 for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: expected_cmdty_consumed_withdraw = self._series_cmdty_consumed_withdraw[dt] * withdrawn_volume for inventory in [2.54, 500.58, 1234.56]: cmdty_consumed_withdraw = storage.cmdty_consumed_withdraw(dt, inventory, withdrawn_volume) self.assertEqual(expected_cmdty_consumed_withdraw, cmdty_consumed_withdraw) def test_inventory_pcnt_loss_scalar_init_parameter(self): storage = self._create_storage() for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: inventory_loss = storage.inventory_pcnt_loss(dt) self.assertEqual(self._constant_inventory_loss, inventory_loss) def test_inventory_pcnt_loss_none_init_parameter(self): storage = self._create_storage(inventory_loss=None) for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: inventory_loss = storage.inventory_pcnt_loss(dt) self.assertEqual(0, inventory_loss) def test_inventory_pcnt_loss_series_init_parameter(self): storage = self._create_storage(inventory_loss=self._series_inventory_loss) for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: inventory_loss = storage.inventory_pcnt_loss(dt) expected_inventory_loss = self._series_inventory_loss[dt] self.assertEqual(expected_inventory_loss, inventory_loss) def test_inventory_cost_scalar_init_parameter(self): storage = self._create_storage() for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: for inventory in [0, 500.58, 1234.56, 1800]: inventory_cost = storage.inventory_cost(dt, inventory) self.assertEqual(self._constant_inventory_cost * inventory, inventory_cost) def test_inventory_cost_none_init_parameter(self): storage = self._create_storage(inventory_cost=None) for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: for inventory in [0, 500.58, 1234.56, 1800]: inventory_cost = storage.inventory_cost(dt, inventory) self.assertEqual(0.0, inventory_cost) def test_inventory_cost_series_init_parameter(self): storage = self._create_storage(inventory_cost=self._series_inventory_cost) for dt in [date(2019, 8, 28), date(2019, 9, 1), date(2019, 9, 20)]: expected_inventory_cost = self._series_inventory_cost[dt] for inventory in [0, 500.58, 1234.56, 1800]: inventory_cost = storage.inventory_cost(dt, inventory) self.assertEqual(expected_inventory_cost * inventory, inventory_cost)
def test_three_factor_seasonal_regression(self): storage_start = '2019-12-01' storage_end = '2020-04-01' constant_injection_rate = 700.0 constant_withdrawal_rate = 700.0 constant_injection_cost = 1.23 constant_withdrawal_cost = 0.98 min_inventory = 0.0 max_inventory = 100000.0 cmdty_storage = CmdtyStorage('D', storage_start, storage_end, constant_injection_cost, constant_withdrawal_cost, min_inventory=min_inventory, max_inventory=max_inventory, max_injection_rate=constant_injection_rate, max_withdrawal_rate=constant_withdrawal_rate) inventory = 0.0 val_date = '2019-08-29' low_price = 23.87 high_price = 150.32 date_switch_high_price = '2020-03-12' forward_curve = utils.create_piecewise_flat_series([low_price, high_price, high_price], [val_date, date_switch_high_price, storage_end], freq='D') flat_interest_rate = 0.03 interest_rate_curve = pd.Series(index=pd.period_range(val_date, '2020-06-01', freq='D')) interest_rate_curve[:] = flat_interest_rate # Multi-Factor parameters spot_mean_reversion = 16.2 spot_volatility = 1.15 seasonal_volatility = 0.18 long_term_vol = 0.14 def twentieth_of_next_month(period): return period.asfreq('M').asfreq('D', 'end') + 20 progresses = [] def on_progress(progress): progresses.append(progress) # Simulation parameter num_sims = 500 seed = 11 fwd_sim_seed = seed # Temporarily set to pass regression tests basis_funcs = '1 + x_st + x_sw + x_lt + x_st**2 + x_sw**2 + x_lt**2' discount_deltas = False multi_factor_val = three_factor_seasonal_value(cmdty_storage, val_date, inventory, forward_curve, interest_rate_curve, twentieth_of_next_month, spot_mean_reversion, spot_volatility, long_term_vol, seasonal_volatility, num_sims, basis_funcs, discount_deltas, seed=seed, fwd_sim_seed=fwd_sim_seed, on_progress_update=on_progress) self.assertAlmostEqual(multi_factor_val.npv, 1766460.137569665, places=6) self.assertEqual(123, len(multi_factor_val.deltas)) # TODO look into why deltas is longer the intrinsic profile self.assertEqual(123, len(multi_factor_val.expected_profile)) self.assertEqual(progresses[-1], 1.0) self.assertEqual(245, len(progresses)) self.assertEqual(1703773.0757192627, multi_factor_val.intrinsic_npv) self.assertEqual(123, len(multi_factor_val.intrinsic_profile)) self.assertEqual((123, num_sims), multi_factor_val.sim_spot_regress.shape) self.assertEqual((123, num_sims), multi_factor_val.sim_spot_valuation.shape) self.assertEqual((123, num_sims), multi_factor_val.sim_inventory.shape) self.assertEqual((123, num_sims), multi_factor_val.sim_inject_withdraw.shape) self.assertEqual((123, num_sims), multi_factor_val.sim_cmdty_consumed.shape) self.assertEqual((123, num_sims), multi_factor_val.sim_inventory_loss.shape) self.assertEqual((123, num_sims), multi_factor_val.sim_net_volume.shape)