From ee81afcbeb27429fdf462549d2131c919beffb11 Mon Sep 17 00:00:00 2001 From: "Roberson, Martin [GBM Public]" Date: Tue, 2 Jul 2024 14:30:39 +0000 Subject: [PATCH] Chore: Make release 1.0.93 --- .../generic_datatype_encoders.py | 28 +++++++++++++++++++ .../json_encoders/response_encoders.py | 15 +++------- gs_quant/api/gs/backtests_xasset/response.py | 7 +++-- .../response_datatypes/backtest_datatypes.py | 6 ++-- gs_quant/target/instrument.py | 26 +++++++++++++++++ 5 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 gs_quant/api/gs/backtests_xasset/json_encoders/response_datatypes/generic_datatype_encoders.py diff --git a/gs_quant/api/gs/backtests_xasset/json_encoders/response_datatypes/generic_datatype_encoders.py b/gs_quant/api/gs/backtests_xasset/json_encoders/response_datatypes/generic_datatype_encoders.py new file mode 100644 index 00000000..3a78e535 --- /dev/null +++ b/gs_quant/api/gs/backtests_xasset/json_encoders/response_datatypes/generic_datatype_encoders.py @@ -0,0 +1,28 @@ +""" +Copyright 2019 Goldman Sachs. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +""" + +import datetime as dt +from typing import Dict, Tuple + +from gs_quant.instrument import Instrument + + +def decode_inst_tuple(t: tuple) -> Tuple[Instrument, ...]: + return tuple(Instrument.from_dict(i) for i in t) + + +def decode_daily_portfolio(results: dict) -> Dict[dt.date, Tuple[Instrument, ...]]: + return {dt.date.fromisoformat(k): decode_inst_tuple(v) for k, v in results.items()} diff --git a/gs_quant/api/gs/backtests_xasset/json_encoders/response_encoders.py b/gs_quant/api/gs/backtests_xasset/json_encoders/response_encoders.py index 26c2b7b9..339ba176 100644 --- a/gs_quant/api/gs/backtests_xasset/json_encoders/response_encoders.py +++ b/gs_quant/api/gs/backtests_xasset/json_encoders/response_encoders.py @@ -19,6 +19,8 @@ from typing import Dict, Any, Tuple, Union +from gs_quant.api.gs.backtests_xasset.json_encoders.response_datatypes.generic_datatype_encoders import \ + decode_inst_tuple from gs_quant.api.gs.backtests_xasset.json_encoders.response_datatypes.risk_result_datatype_encoders import \ encode_series_result, encode_dataframe_result from gs_quant.api.gs.backtests_xasset.json_encoders.response_datatypes.risk_result_encoders import decode_risk_result, \ @@ -27,7 +29,6 @@ from gs_quant.api.gs.backtests_xasset.response_datatypes.risk_result_datatypes import RiskResultWithData from gs_quant.backtests import FlowVolBacktestMeasure from gs_quant.common import Currency, CurrencyName, RiskMeasure -from gs_quant.instrument import Instrument from gs_quant.json_convertors_common import encode_risk_measure, decode_risk_measure from gs_quant.priceable import PriceableImpl @@ -42,12 +43,8 @@ def encode_response_obj(data: Any) -> Dict: return data.to_dict() -def _decode_inst_tuple(t: tuple) -> Tuple[Instrument, ...]: - return tuple(Instrument.from_dict(i) for i in t) - - def decode_leg_refs(d: dict) -> Dict[str, PriceableImpl]: - return {k: _decode_inst_tuple(v) for k, v in d.items()} + return {k: decode_inst_tuple(v) for k, v in d.items()} def decode_risk_measure_refs(d: dict) -> Dict[str, RiskMeasure]: @@ -63,10 +60,6 @@ def decode_basic_bt_measure_dict(results: dict) -> Dict[FlowVolBacktestMeasure, for k, v in results.items()} -def decode_basic_bt_portfolio(results: dict) -> Dict[dt.date, Tuple[Instrument, ...]]: - return {dt.date.fromisoformat(k): _decode_inst_tuple(v) for k, v in results.items()} - - def decode_basic_bt_transactions(results: dict) -> Dict[dt.date, Tuple[Transaction, ...]]: def to_ccy(s: str) -> Union[Currency, CurrencyName, str]: if s in [x.value for x in Currency]: @@ -77,5 +70,5 @@ def to_ccy(s: str) -> Union[Currency, CurrencyName, str]: return s return {dt.date.fromisoformat(k): tuple( - Transaction(_decode_inst_tuple(t['portfolio']), t['portfolio_price'], t['cost'], to_ccy(t['currency'])) + Transaction(decode_inst_tuple(t['portfolio']), t['portfolio_price'], t['cost'], to_ccy(t['currency'])) for t in v) for k, v in results.items()} diff --git a/gs_quant/api/gs/backtests_xasset/response.py b/gs_quant/api/gs/backtests_xasset/response.py index 3d85be7e..daf8fdfd 100644 --- a/gs_quant/api/gs/backtests_xasset/response.py +++ b/gs_quant/api/gs/backtests_xasset/response.py @@ -21,9 +21,10 @@ from dataclasses_json import dataclass_json, LetterCase, config +from gs_quant.api.gs.backtests_xasset.json_encoders.response_datatypes.generic_datatype_encoders import \ + decode_daily_portfolio from gs_quant.api.gs.backtests_xasset.json_encoders.response_encoders import decode_leg_refs, \ - decode_risk_measure_refs, decode_result_tuple, decode_basic_bt_measure_dict, decode_basic_bt_portfolio, \ - decode_basic_bt_transactions + decode_risk_measure_refs, decode_result_tuple, decode_basic_bt_measure_dict, decode_basic_bt_transactions from gs_quant.api.gs.backtests_xasset.response_datatypes.backtest_datatypes import Transaction, AdditionalResults from gs_quant.api.gs.backtests_xasset.response_datatypes.risk_result import RiskResultsByDate from gs_quant.api.gs.backtests_xasset.response_datatypes.risk_result_datatypes import RiskResultWithData @@ -47,7 +48,7 @@ class BasicBacktestResponse: measures: Dict[FlowVolBacktestMeasure, Dict[dt.date, RiskResultWithData]] = \ field(default=None, metadata=config(decoder=decode_basic_bt_measure_dict)) portfolio: Dict[dt.date, Tuple[Instrument, ...]] \ - = field(default=None, metadata=config(decoder=decode_basic_bt_portfolio)) + = field(default=None, metadata=config(decoder=decode_daily_portfolio)) transactions: Dict[dt.date, Tuple[Transaction, ...]] \ = field(default=None, metadata=config(decoder=decode_basic_bt_transactions)) additional_results: Optional[AdditionalResults] = field(default=None) diff --git a/gs_quant/api/gs/backtests_xasset/response_datatypes/backtest_datatypes.py b/gs_quant/api/gs/backtests_xasset/response_datatypes/backtest_datatypes.py index c8b65dc9..91f7dadc 100644 --- a/gs_quant/api/gs/backtests_xasset/response_datatypes/backtest_datatypes.py +++ b/gs_quant/api/gs/backtests_xasset/response_datatypes/backtest_datatypes.py @@ -23,9 +23,10 @@ from dataclasses_json import dataclass_json, LetterCase, config from gs_quant.api.gs.backtests_xasset.json_encoders.request_encoders import legs_decoder +from gs_quant.api.gs.backtests_xasset.json_encoders.response_datatypes.generic_datatype_encoders import \ + decode_daily_portfolio from gs_quant.instrument import Instrument from gs_quant.json_convertors import decode_optional_date -from gs_quant.priceable import PriceableImpl from gs_quant.target.backtests import BacktestTradingQuantityType, EquityMarketModel from gs_quant.common import Currency, CurrencyName, PricingLocation @@ -46,7 +47,8 @@ class Transaction: @dataclass_json(letter_case=LetterCase.CAMEL) @dataclass class AdditionalResults: - hedges: Optional[Dict[dt.date, PriceableImpl]] = None + hedges: Optional[Dict[dt.date, Tuple[Instrument, ...]]] = field(default=None, + metadata=config(decoder=decode_daily_portfolio)) hedge_pnl: Optional[Dict[dt.date, float]] = None no_of_calculations: Optional[int] = None diff --git a/gs_quant/target/instrument.py b/gs_quant/target/instrument.py index 76f8519e..9f8e88f7 100644 --- a/gs_quant/target/instrument.py +++ b/gs_quant/target/instrument.py @@ -705,6 +705,19 @@ class FXForward(Instrument): type_: Optional[AssetType] = field(init=False, default=AssetType.Forward, metadata=config(field_name='type', exclude=exclude_none)) name: Optional[str] = field(default=None, metadata=name_metadata) + def scale_in_place(self, scaling: Optional[float] = None): + if self.unresolved is None: + raise RuntimeError('Can only scale resolved instruments') + if scaling is None or scaling == 1: + return + + if scaling < 0: + flip_dict = {BuySell.Buy: BuySell.Sell, BuySell.Sell: BuySell.Buy} + self.buy_sell = flip_dict[self.buy_sell] + + self.notional_amount *= abs(scaling) + return + @handle_camel_case_args @dataclass_json(letter_case=LetterCase.CAMEL) @@ -825,6 +838,19 @@ class FXOption(Instrument): type_: Optional[AssetType] = field(init=False, default=AssetType.Option, metadata=config(field_name='type', exclude=exclude_none)) name: Optional[str] = field(default=None, metadata=name_metadata) + def scale_in_place(self, scaling: Optional[float] = None): + if self.unresolved is None: + raise RuntimeError('Can only scale resolved instruments') + if scaling is None or scaling == 1: + return + + if scaling < 0: + flip_dict = {BuySell.Buy: BuySell.Sell, BuySell.Sell: BuySell.Buy} + self.buy_sell = flip_dict[self.buy_sell] + + self.notional_amount *= abs(scaling) + return + @handle_camel_case_args @dataclass_json(letter_case=LetterCase.CAMEL)