def test_cache_addition_removal(): # Don't use a mocker here as it will hold refs to things and break the cache removal test set_session() p1 = IRSwap('Pay', '10y', 'DKK') with PricingContext(use_cache=True): market_data_location = PricingContext.current.market_data_location pricing_date = PricingContext.current.pricing_date p1.price() assert PricingCache.get(p1, market_data_location, risk.Price, pricing_date) assert not PricingCache.get(p1, market_data_location, risk.IRDelta, pricing_date) # Assert that deleting the cached instrument removes it from the PricingCache # N.B, this may not work when debugging tests del p1 p2 = IRSwap('Pay', '10y', 'DKK') assert not PricingCache.get(p2, market_data_location, risk.Price, pricing_date) with PricingContext(use_cache=True): p2.price() assert PricingCache.get(p2, market_data_location, risk.Price, pricing_date) # Change a property and assert that p2 is no longer cached p2.notional_currency = 'EUR' assert not PricingCache.get(p2, market_data_location, risk.Price, pricing_date)
def _raise_order( self, state: Union[date, Iterable[date]], trigger_info: Optional[Union[AddTradeActionInfo, Iterable[AddTradeActionInfo]]] = None): with PricingContext(is_batch=True, show_progress=True): state_list = make_list(state) orders = {} if trigger_info is None or isinstance(trigger_info, AddTradeActionInfo): trigger_info = [trigger_info for _ in range(len(state_list))] for s, ti in zip_longest(state_list, trigger_info): active_portfolio = self.action.dated_priceables.get( s) or self.action.priceables with PricingContext(pricing_date=s): orders[s] = (Portfolio(active_portfolio).resolve( in_place=False), ti) final_orders = {} for d, p in orders.items(): new_port = [] for t in p[0].result(): t.name = f'{t.name}_{d}' new_port.append(t) new_port = Portfolio(new_port) final_orders[d] = new_port.scale( None if p[1] is None else p[1].scaling, in_place=False) return final_orders
def test_cache_subset(mocker): set_session() ir_swap = IRSwap('Pay', '10y', 'DKK') values = [ {'date': '2019-10-07', 'value': 0.01}, {'date': '2019-10-08', 'value': 0.01} ] mocker.return_value = [[values]] dates = (dt.date(2019, 10, 7), dt.date(2019, 10, 8)) with HistoricalPricingContext(dates=dates, use_cache=True) as hpc: pricing_key = hpc.pricing_key price_f = ir_swap.price() price_f.result() cached = PricingCache.get(ir_swap, risk.Price, pricing_key) assert len(cached) == len(dates) cached_scalar = PricingCache.get(ir_swap, risk.Price, PricingContext(pricing_date=dates[0]).pricing_key) assert isinstance(cached_scalar, float) dates = dates + (dt.date(2019, 10, 9),) pricing_key = HistoricalPricingContext(dates=dates).pricing_key cached2 = PricingCache.get(ir_swap, risk.Price, pricing_key) assert cached2 is None cached3 = PricingCache.get(ir_swap, risk.Price, pricing_key, return_partial=True) assert len(cached3) < len(dates) values = [ {'date': '2019-10-07', 'marketDataType': 'IR', 'assetId': 'USD', 'pointClass': 'Swap', 'point': '1y', 'value': 0.01}, {'date': '2019-10-07', 'marketDataType': 'IR', 'assetId': 'USD', 'pointClass': 'Swap', 'point': '2y', 'value': 0.015}, {'date': '2019-10-08', 'marketDataType': 'IR', 'assetId': 'USD', 'pointClass': 'Swap', 'point': '1y', 'value': 0.01}, {'date': '2019-10-08', 'marketDataType': 'IR', 'assetId': 'USD', 'pointClass': 'Swap', 'point': '2y', 'value': 0.015}, {'date': '2019-10-09', 'marketDataType': 'IR', 'assetId': 'USD', 'pointClass': 'Swap', 'point': '1y', 'value': 0.01}, {'date': '2019-10-09', 'marketDataType': 'IR', 'assetId': 'USD', 'pointClass': 'Swap', 'point': '2y', 'value': 0.015} ] mocker.return_value = [[values]] with HistoricalPricingContext(dates=dates, use_cache=True) as hpc: pricing_key = hpc.pricing_key risk_f = ir_swap.calc(risk.IRDelta) risk_frame = risk_f.result() assert isinstance(risk_frame, pd.DataFrame) assert len(risk_frame.index.unique()) == len(dates) cached4 = PricingCache.get(ir_swap, risk.IRDelta, pricing_key) assert len(cached4.index.unique()) == len(dates) cached5 = PricingCache.get(ir_swap, risk.IRDelta, PricingContext(pricing_date=dates[0]).pricing_key) assert len(cached5.index.unique()) == len(cached5)
def test_aggregation_with_diff_risk_keys(mocker): with MockCalc(mocker): portfolio1 = Portfolio([ IRSwaption('Pay', '10y', 'EUR', expiration_date='3m', name='EUR3m10ypayer') ]) portfolio2 = Portfolio([ IRSwaption('Pay', '10y', 'EUR', expiration_date='6m', name='EUR6m10ypayer') ]) with PricingContext(csa_term='EUR-OIS', visible_to_gs=True): r1 = portfolio1.price() with PricingContext(csa_term='EUR-EuroSTR'): r2 = portfolio2.price() combined_result = r1 + r2 with pytest.raises(ValueError): combined_result.aggregate() assert isinstance(combined_result.aggregate(allow_mismatch_risk_keys=True), float)
def test_pricing_dates(): # May be on weekend but doesn't matter for basic test future_date = dt.date.today() + dt.timedelta(2) yesterday = dt.date.today() - dt.timedelta(1) pc = PricingContext(pricing_date=future_date, market=CloseMarket(yesterday)) assert pc is not None with pytest.raises(ValueError, match="pricing_date in the future"): PricingContext(pricing_date=future_date)
def run_backtest(cls, strategy, start=None, end=None, frequency='BM', window=None, states=None, risks=Price, show_progress=True): dates = pd.date_range(start=start, end=end, freq=frequency).date.tolist() risks = make_list(risks) + strategy.risks backtest = BackTest(strategy, dates, risks) if strategy.initial_portfolio is not None: for date in dates: backtest.portfolio_dict[date].append(strategy.initial_portfolio) for trigger in strategy.triggers: if trigger.calc_type != CalcType.path_dependent: triggered_dates = [date for date in dates if trigger.has_triggered(date, backtest)] for action in trigger.actions: if action.calc_type != CalcType.path_dependent: action.apply_action(triggered_dates, backtest) with PricingContext(is_batch=True, show_progress=show_progress): for day, portfolio in backtest.portfolio_dict.items(): with PricingContext(day): backtest.calc_calls += 1 backtest.calculations += len(portfolio) * len(risks) backtest.add_results(day, portfolio.calc(tuple(risks))) # semi path dependent initial calc for _, scaling_list in backtest.scaling_portfolios.items(): for p in scaling_list: with HistoricalPricingContext(dates=p.dates): backtest.calc_calls += 1 backtest.calculations += len(p.dates) * len(risks) p.results = Portfolio([p.trade]).calc(tuple(risks)) for date in dates: # semi path dependent scaling if date in backtest.scaling_portfolios: for p in backtest.scaling_portfolios[date]: scale_date = p.dates[0] scaling_factor = backtest.results[scale_date][p.risk][0] / p.results[scale_date][p.risk][0] scaled_trade = p.trade.as_dict() scaled_trade['notional_amount'] *= -scaling_factor scaled_trade = Instrument.from_dict(scaled_trade) for day in p.dates: backtest.add_results(day, p.results[day] * -scaling_factor) backtest.portfolio_dict[day] += Portfolio(scaled_trade) # path dependent for trigger in strategy.triggers: if trigger.calc_type == CalcType.path_dependent: if trigger.has_triggered(date, backtest): for action in trigger.actions: action.apply_action(date, backtest) else: for action in trigger.actions: if action.calc_type == CalcType.path_dependent: if trigger.has_triggered(date, backtest): action.apply_action(date, backtest) return backtest
def _raise_order(self, state: Union[date, Iterable[date]]): with PricingContext(is_batch=True): orders = {} for s in state: active_portfolio = self.action.dated_priceables.get( s) or self.action.priceables with PricingContext(pricing_date=s): orders[s] = Portfolio(active_portfolio).resolve( in_place=False) return orders
def test_cache_subset(mocker): set_session() ir_swap = IRSwap('Pay', '10y', 'DKK') values = [{'$type': 'Risk', 'val': 0.01}] mocker.return_value = [[[values]], [[values]]] dates = (dt.date(2019, 10, 7), dt.date(2019, 10, 8)) with HistoricalPricingContext(dates=dates, use_cache=True): price_f = ir_swap.price() price_f.result() for date in dates: risk_key = PricingContext(pricing_date=date)._PricingContext__risk_key( risk.Price, ir_swap.provider) cached_scalar = PricingCache.get(risk_key, ir_swap) assert cached_scalar assert isinstance(cached_scalar, float) risk_key = PricingContext( pricing_date=dt.date(2019, 10, 9))._PricingContext__risk_key( risk.Price, ir_swap.provider) cached2 = PricingCache.get(risk_key, ir_swap) assert cached2 is None values = [{ '$type': 'RiskVector', 'asset': [0.01, 0.015], 'points': [{ 'type': 'IR', 'asset': 'USD', 'class_': 'Swap', 'point': '1y' }, { 'type': 'IR', 'asset': 'USD', 'class_': 'Swap', 'point': '2y' }] }] # Check that we can return the same values from the cache, after calculating once (with return values set to None) for return_values in ([[[values]], [[values]], [[values]]], None): mocker.return_value = return_values with HistoricalPricingContext(dates=dates, use_cache=True): risk_f = ir_swap.calc(risk.IRDelta) risk_frame = risk_f.result() assert isinstance(risk_frame, pd.DataFrame) assert len(risk_frame.index.unique()) == len(dates)
def test_multiple_measures(mocker): day = [ [ [{ '$type': 'RiskVector', 'asset': [0.01, 0.015], 'points': [ {'type': 'IR Vol', 'asset': 'USD-LIBOR-BBA', 'class_': 'Swap', 'point': '1y'}, {'type': 'IR Vol', 'asset': 'USD-LIBOR-BBA', 'class_': 'Swap', 'point': '2y'} ] }] ], [ [{ '$type': 'RiskVector', 'asset': [0.01, 0.015], 'points': [ {'type': 'IR', 'asset': 'USD', 'class_': 'Swap', 'point': '1y'}, {'type': 'IR', 'asset': 'USD', 'class_': 'Swap', 'point': '2y'} ] }], ], [ [{'$type': 'Risk', 'val': 0.01}] ] ] mocker.return_value = [day, day, day] set_session() ir_swaption = IRSwaption('Pay', '10y', 'USD') dates = (dt.date(2019, 10, 7), dt.date(2019, 10, 8), dt.date(2019, 10, 9)) with HistoricalPricingContext(dates=dates, use_cache=True): ir_swaption.price() ir_swaption.calc(risk.IRDelta) ir_swaption.calc(risk.IRVega) # make sure all the risk measures got cached correctly for date in dates: with PricingContext(pricing_date=date) as pc: for risk_measure in (risk.Price, risk.IRDelta, risk.IRVega): val = PricingCache.get(pc._PricingContext__risk_key(risk_measure, ir_swaption.provider), ir_swaption) assert val is not None with PricingContext(pricing_date=dt.date(2019, 10, 11)) as pc: for risk_measure in (risk.Price, risk.IRDelta, risk.IRVega): val = PricingCache.get(pc._PricingContext__risk_key(risk_measure, ir_swaption.provider), ir_swaption) assert val is None
def _raise_order(self, state: Union[date, Iterable[date]], trigger_info: Optional[Union[AddTradeActionInfo, Iterable[AddTradeActionInfo]]] = None): with PricingContext(is_batch=True): state_list = make_list(state) orders = {} if trigger_info is None or isinstance(trigger_info, AddTradeActionInfo): trigger_info = [trigger_info for _ in range(len(state_list))] for s, ti in zip_longest(state_list, trigger_info): active_portfolio = self.action.dated_priceables.get(s) or self.action.priceables with PricingContext(pricing_date=s): orders[s] = (Portfolio(active_portfolio).resolve(in_place=False), ti) orders = {k: v[0].result().scale(None if v[1] is None else v[1].scaling, in_place=False) for k, v in orders.items()} return orders
def run_backtest(cls, strategy, start=None, end=None, frequency='BM', window=None, states=None, risks=Price, show_progress=True): dates = pd.date_range(start=start, end=end, freq=frequency).date.tolist() risks = make_list(risks) + strategy.risks backtest = BackTest(strategy, dates, risks) for trigger in strategy.triggers: if trigger.deterministic: triggered_dates = [ date for date in dates if trigger.has_triggered(date, backtest) ] for action in trigger.actions: if action.deterministic: action.apply_action(triggered_dates, backtest) with PricingContext(is_batch=True, show_progress=show_progress): for day, portfolio in backtest.portfolio_dict.items(): with PricingContext(day): backtest.calc_calls += 1 backtest.calculations += len(portfolio) * len(risks) backtest.add_results( day, portfolio.calc(risks[0] if len(risks) == 1 else tuple(risks))) for trigger in strategy.triggers: if not trigger.deterministic: for date in dates: if trigger.has_triggered(date, backtest): for action in trigger.actions: action.apply_action(date, backtest) else: for action in trigger.actions: if not action.deterministic: for date in dates: if trigger.has_triggered(date, backtest): action.apply_action(date, backtest) return backtest
def test_backtothefuture_pricing(mocker): with MockCalc(mocker): swap1 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.01, name='swap1') swap2 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.02, name='swap2') swap3 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.03, name='swap3') portfolio = Portfolio((swap1, swap2, swap3)) pricing_date = dt.date(2020, 10, 7) with PricingContext(pricing_date=pricing_date): with BackToTheFuturePricingContext(dates=business_day_offset( pricing_date, [-1, 0, 1], roll='forward')) as hpc: risk_key = hpc._PricingContext__risk_key( risk.DollarPrice, swap1.provider) results = portfolio.calc(risk.DollarPrice) expected = risk.SeriesWithInfo( pd.Series( data=[-35280379.86540368, -35348910.76427929, -30830994.939595155], index=business_day_offset(pricing_date, [-1, 0, 1], roll='forward')), risk_key=risk_key.ex_date_and_market, ) actual = results[risk.DollarPrice].aggregate() assert actual.equals(expected)
def get_risk_request_id(requests): """ This is not a formal equality of the risk request as it covers only the names of core components. When a formal eq function is provided on risk_request then this should be replaced with something derived from that. :param requests: a collection of RiskRequests :type requests: tuple of RiskRequest :return: hash :rtype: str """ identifier = str(len(requests)) for request in requests: identifier += '_' identifier += '-'.join([pos.instrument.name for pos in request.positions]) identifier += '-'.join([str(risk) for risk in request.measures]) date = request.pricing_and_market_data_as_of[0].pricing_date.strftime('%Y%b%d') today = PricingContext().pricing_date.strftime('%Y%b%d') identifier += 'today' if date == today else date if request.scenario is not None: scenario_identifier = [] for k, v in request.scenario.scenario.as_dict().items(): if k != 'shocks': scenario_identifier.append(str(k) + "=" + str(v)) else: shock_value = 'shock_value' + "=" + str(v[0].shock.value) pattern = v[0].pattern shock_pattern = 'shock_pattern' + "=" + '-'.join( [str(m) for m in [pattern.mkt_type, pattern.mkt_asset, pattern.mkt_class]]) scenario_identifier.append(shock_value + "+" + shock_pattern) identifier += '+'.join(sorted(scenario_identifier)) return hashlib.md5(identifier.encode('utf-8')).hexdigest()
def test_backtothefuture_pricing(mocker): with MockCalc(mocker): swap1 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.01, name='swap1') swap2 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.02, name='swap2') swap3 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.03, name='swap3') portfolio = Portfolio((swap1, swap2, swap3)) pricing_date = dt.date(2021, 2, 10) with PricingContext(pricing_date=pricing_date): with BackToTheFuturePricingContext(dates=business_day_offset( pricing_date, [-1, 0, 1], roll='forward')) as hpc: risk_key = hpc._PricingContext__risk_key( risk.DollarPrice, swap1.provider) results = portfolio.calc(risk.DollarPrice) expected = risk.SeriesWithInfo( pd.Series( data=[-22711963.80864744, -22655907.930484552, -21582551.58922608], index=business_day_offset(pricing_date, [-1, 0, 1], roll='forward')), risk_key=historical_risk_key(risk_key), ) actual = results[risk.DollarPrice].aggregate() assert actual.equals(expected)
def process(self): a_data = self.children_data.get('a') if isinstance(a_data, ProcessorResult): if a_data.success: date_array = a_data.data.reset_index()['date'] if self.end is None: self.end = max(date_array) if self.start is None: self.start = min(date_array) if not isinstance(self.weekdays_only, bool): self.value = ProcessorResult( False, "DateRangeProcessor requires weekdays_only argument to be a boolean." ) yesterday = date.today() - timedelta(days=1) with PricingContext(pricing_date=yesterday): # for EOD datasets latest datapoint is T-1, # relative dates will be evaluated using yesterday as base_date if isinstance(self.end, RelativeDate): self.end = self.end.apply_rule() if isinstance(self.start, RelativeDate): self.start = self.start.apply_rule() result = date_range(a_data.data, start_date=self.start, end_date=self.end, weekdays_only=self.weekdays_only) self.value = ProcessorResult(True, result) else: self.value = ProcessorResult( False, "DateRangeProcessor does not have 'a' series values yet") else: self.value = ProcessorResult( False, "DateRangeProcessor does not have 'a' series yet")
def apply_action(self, state: Union[datetime.date, Iterable[datetime.date]], backtest: BackTest): with PricingContext(is_batch=True): f = {} for s in state: active_portfolio = self._dated_priceables.get(s) or self._priceables with PricingContext(pricing_date=s): f[s] = Portfolio(active_portfolio).resolve(in_place=False) for s in backtest.states: pos = [] for create_date, portfolio in f.items(): pos += [inst for inst in portfolio.result().instruments if get_final_date(inst, create_date, self.trade_duration) >= s >= create_date] backtest.portfolio_dict[s].append(pos) return backtest
def test_aggregation_with_empty_measures(mocker): with MockCalc(mocker): swaptions = (IRSwaption(notional_currency='EUR', termination_date='7y', expiration_date='1y', pay_or_receive='Receive', strike='ATM+35', name='EUR 1y7y'), IRSwaption(notional_currency='EUR', termination_date='10y', expiration_date='2w', pay_or_receive='Receive', strike='ATM+50', name='EUR 2w10y')) portfolio = Portfolio(swaptions) from_date = dt.date(2021, 11, 18) to_date = dt.date(2021, 11, 19) explain_2d = PnlExplain(CloseMarket(date=to_date)) with PricingContext(pricing_date=from_date, visible_to_gs=True): portfolio.resolve() result_explain = portfolio.calc(explain_2d) total_risk = aggregate_risk(result_explain[explain_2d])['value'].sum() risk_swaption_1 = result_explain[0]['value'].sum() risk_swaption_2 = result_explain[1]['value'].sum() assert total_risk == risk_swaption_1 + risk_swaption_2
def test_structured_calc(mocker): set_session() for priceable in priceables: if priceable.assetClass == AssetClass.Rates: for measure in (risk.IRDelta, risk.IRVega): structured_calc(mocker, priceable, measure) elif priceable.assetClass == AssetClass.FX: for measure in (risk.FXDelta, risk.FXGamma, risk.FXVega): structured_calc(mocker, priceable, measure) values = { '$type': 'RiskVector', 'asset': [0.01, 0.015], 'points': [ {'type': 'IR', 'asset': 'USD', 'class_': 'Swap', 'point': '1y'}, {'type': 'IR', 'asset': 'USD', 'class_': 'Swap', 'point': '2y'} ] } mocker.return_value = [[[[values]] * len(priceables)]] with PricingContext(): delta_f = [p.calc(risk.IRDelta) for p in priceables] delta = risk.aggregate_risk(delta_f, threshold=0) assert len(delta) == 2
def test_structured_calc(mocker): set_session() for priceable in priceables: if priceable.assetClass == AssetClass.Rates: for measure in (risk.IRDelta, risk.IRVega): structured_calc(mocker, priceable, measure) elif priceable.assetClass == AssetClass.FX: for measure in (risk.FXDelta, risk.FXGamma, risk.FXVega): structured_calc(mocker, priceable, measure) values = [{ 'marketDataType': 'IR', 'assetId': 'USD', 'pointClass': 'Swap', 'point': '1y', 'value': 0.01 }, { 'marketDataType': 'IR', 'assetId': 'USD', 'pointClass': 'Swap', 'point': '2y', 'value': 0.015 }] mocker.return_value = [[values] * len(priceables)] with PricingContext(): delta_f = [p.calc(risk.IRDelta) for p in priceables] delta = risk.aggregate_risk(delta_f, threshold=0) assert len(delta) == 2
def test_pricing_context(mocker): swap1 = IRSwap('Pay', '1y', 'EUR', name='EUR1y') future_date = business_day_offset(dt.date.today(), 10, roll='forward') with MockCalc(mocker): with RollFwd(date='10b', realise_fwd=True): market = swap1.market() with pytest.raises(ValueError): # cannot pass in future date into pricing context, use RollFwd instead with PricingContext(pricing_date=future_date): _ = swap1.calc(risk.Price) # cannot pass in market dated in the future into pricing context, use RollFwd instead with PricingContext(market=CloseMarket(date=future_date)): _ = swap1.calc(risk.Price) with PricingContext(market=OverlayMarket(base_market=CloseMarket(date=future_date, location='NYC'), market_data=market.result())): _ = swap1.calc(risk.Price)
def test_async_calc(mocker): set_session() mocker.return_value = [[[[{'$type': 'Risk', 'val': 0.01 * idx}] for idx in range(len(priceables))]]] with PricingContext(): dollar_price_f = [p.dollar_price() for p in priceables] prices = tuple(f.result() for f in dollar_price_f) assert prices == tuple(0.01 * i for i in range(len(priceables)))
def test_single_instrument(mocker): with MockCalc(mocker): swap1 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.0, name='10y@0') portfolio = Portfolio(swap1) assert portfolio.paths('10y@0') == (PortfolioPath(0), ) with PricingContext(pricing_date=dt.date(2020, 10, 15)): prices: PortfolioRiskResult = portfolio.dollar_price() assert tuple(map(lambda x: round(x, 0), prices)) == (7391258.0, ) assert round(prices.aggregate(), 0) == 7391258.0 assert round(prices[swap1], 0) == 7391258.0
def test_single_instrument_new_mock(mocker): with MockCalc(mocker): with PricingContext(pricing_date=dt.date(2020, 10, 15)): swap1 = IRSwap('Pay', '10y', 'USD', name='swap1') portfolio = Portfolio(swap1) fwd: PortfolioRiskResult = portfolio.calc(risk.IRFwdRate) assert portfolio.paths('swap1') == (PortfolioPath(0), ) assert tuple(map(lambda x: round(x, 6), fwd)) == (0.007512, ) assert round(fwd.aggregate(), 2) == 0.01 assert round(fwd[swap1], 6) == 0.007512
def get_attributes(p, risks, ctx='PricingCtx1', resolve=False, no_frame=False): if resolve: p.resolve() if ctx == 'Multiple': with HistoricalPricingContext(date(2020, 1, 14), date(2020, 1, 15), market_data_location='LDN'): res = p.calc(risks) elif ctx == 'PricingCtx1': with PricingContext(date(2020, 1, 14), market_data_location='LDN'): res = p.calc(risks) elif ctx == 'Multiple2': with HistoricalPricingContext(date(2020, 1, 16), date(2020, 1, 17), market_data_location='LDN'): res = p.calc(risks) elif ctx == 'PricingCtx2': with PricingContext(date(2020, 1, 16), market_data_location='NYC'): res = p.calc(risks) elif ctx == 'PricingCtx3': with PricingContext(date(2020, 1, 16), market_data_location='LDN'): res = p.calc(risks) elif ctx == 'RollFwd': with RollFwd(date=date(2020, 11, 3)): res = p.calc(risks) elif ctx == 'CurveScen1': with CurveScenario(parallel_shift=5): res = p.calc(risks) elif ctx == 'CurveScen2': with CurveScenario(curve_shift=1, tenor_start=5, tenor_end=30): res = p.calc(risks) if not no_frame: frame = res.to_frame(None, None, None) cols = [col for col in frame.columns] return cols, res, frame else: return res
def test_disjoint_priceables_measures(mocker): set_session() swap = priceables[4] swaption = priceables[6] mocker.return_value = [[[[{'$type': 'Risk', 'val': 0.01}]]]] * 2 with PricingContext(): swap_price_f = swap.price() swaption_dollar_price_f = swaption.dollar_price() assert swap_price_f.result() == 0.01 assert swaption_dollar_price_f.result() == 0.01
def test_hedge_action_risk_trigger(mocker): with MockCalc(mocker): start_date = date(2021, 12, 1) # end_date = date(2021, 12, 3) # Define trade call = FXOption(buy_sell='Buy', option_type='Call', pair='USDJPY', strike_price='ATMF', notional_amount=1e5, expiration_date='2y', name='2y_call') hedge_risk = FXDelta(aggregation_level='Type') fwd_hedge = FXForward(pair='USDJPY', settlement_date='2y', notional_amount=1e5, name='2y_forward') trig_req = RiskTriggerRequirements(risk=hedge_risk, trigger_level=0, direction=TriggerDirection.ABOVE) action_hedge = HedgeAction(hedge_risk, fwd_hedge, '2b') triggers = StrategyRiskTrigger(trig_req, action_hedge) with PricingContext(pricing_date=start_date): fut = call.resolve(in_place=False) call = fut.result() strategy = Strategy(call, triggers) # run backtest daily engine = GenericEngine() # backtest = engine.run_backtest(strategy, start=start_date, end=end_date, frequency='1b', show_progress=True) backtest = engine.run_backtest( strategy, states=[date(2021, 12, 1), date(2021, 12, 2), date(2021, 12, 3)], show_progress=True) summary = backtest.result_summary assert len(summary) == 3 assert round(summary[hedge_risk].sum()) == 0 assert round(summary['Cumulative Cash'].sum()) == -7090 assert Price in summary.columns
def test_duplicate_instrument(mocker): with MockCalc(mocker): swap1 = IRSwap('Pay', '1y', 'EUR', name='EUR1y') swap2 = IRSwap('Pay', '2y', 'EUR', name='EUR2y') swap3 = IRSwap('Pay', '3y', 'EUR', name='EUR3y') portfolio = Portfolio((swap1, swap2, swap3, swap1)) assert portfolio.paths('EUR1y') == (PortfolioPath(0), PortfolioPath(3)) assert portfolio.paths('EUR2y') == (PortfolioPath(1), ) with PricingContext(pricing_date=dt.date(2020, 10, 15)): fwds: PortfolioRiskResult = portfolio.calc(risk.IRFwdRate) assert tuple(map(lambda x: round(x, 6), fwds)) == (-0.005378, -0.005224, -0.00519, -0.005378) assert round(fwds.aggregate(), 6) == -0.02117 assert round(fwds[swap1], 6) == -0.005378
def test_portfolio(mocker): with MockCalc(mocker): with PricingContext(pricing_date=dt.date(2020, 10, 15)): swap1 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.001, name='swap_10y@10bp') swap2 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.002, name='swap_10y@20bp') swap3 = IRSwap('Pay', '10y', 'USD', fixed_rate=0.003, name='swap_10y@30bp') portfolio = Portfolio((swap1, swap2, swap3)) prices: PortfolioRiskResult = portfolio.dollar_price() result = portfolio.calc((risk.DollarPrice, risk.IRDelta)) assert tuple(sorted(map(lambda x: round(x, 0), prices))) == (4439478.0, 5423405.0, 6407332.0) assert round(prices.aggregate(), 2) == 16270214.48 assert round(prices[0], 0) == 6407332.0 assert round(prices[swap2], 0) == 5423405.0 assert round(prices['swap_10y@30bp'], 0) == 4439478.0 assert tuple(map(lambda x: round(x, 0), result[risk.DollarPrice])) == (6407332.0, 5423405.0, 4439478.0) assert round(result[risk.DollarPrice].aggregate(), 0) == 16270214.0 assert round(result[risk.DollarPrice]['swap_10y@30bp'], 0) == 4439478.0 assert round(result[risk.DollarPrice]['swap_10y@30bp'], 0) == round(result['swap_10y@30bp'][risk.DollarPrice], 0) assert round(result[risk.IRDelta].aggregate().value.sum(), 0) == 278977.0 prices_only = result[risk.DollarPrice] assert tuple(map(lambda x: round(x, 0), prices)) == tuple(map(lambda x: round(x, 0), prices_only)) swap4 = IRSwap('Pay', '10y', 'USD', fixed_rate=-0.001, name='swap_10y@-10bp') portfolio.append(swap4) assert len(portfolio.instruments) == 4 extracted_swap = portfolio.pop('swap_10y@20bp') assert extracted_swap == swap2 assert len(portfolio.instruments) == 3 swap_dict = {'swap_5': swap1, 'swap_6': swap2, 'swap_7': swap3} portfolio = Portfolio(swap_dict) assert len(portfolio) == 3
def apply_action(self, state: datetime.date, backtest: BackTest): with PricingContext(pricing_date=state): f = self._priceable.resolve(in_place=False) hedge_delta = self._priceable.calc(self.risk) hedge = f.result() ratio = backtest.results[state][self.risk].aggregate() / hedge_delta.result() hedge = scale_trade(hedge, ratio) active_dates = [pricing_date for pricing_date in backtest.states if get_final_date(hedge, state, self.trade_duration) >= pricing_date >= state] with HistoricalPricingContext(dates=active_dates): backtest.calc_calls += 1 backtest.calculations += len(active_dates) * len(backtest.risks) hedge_res = Portfolio(hedge).calc(backtest.risks) for pricing_date in active_dates: results = hedge_res[pricing_date] backtest.add_results(pricing_date, results)
def get_risk_request_id(requests): """ This is not a formal equality of the risk request as it covers only the names of core components. When a formal eq function is provided on risk_request then this should be replaced with something derived from that. :param requests: a collection of RiskRequests :type requests: tuple of RiskRequest :return: hash :rtype: str """ identifier = str(len(requests)) for request in requests: identifier += '_' identifier += '-'.join([pos.instrument.name for pos in request.positions]) identifier += '-'.join([str(risk) for risk in request.measures]) date = request.pricing_and_market_data_as_of[0].pricing_date.strftime('%Y%b%d') today = PricingContext().pricing_date.strftime('%Y%b%d') identifier += 'today' if date == today else date identifier += '+'.join(sorted(str(k) + "=" + str(v) for k, v in request.scenario.scenario.as_dict().items())) \ if request.scenario is not None else '' return identifier[:232] # cut down to less than max number of char for filename