Skip to content

Commit ebb8ee5

Browse files
authored
Bug fixes (#231)
1 parent f50bbb3 commit ebb8ee5

File tree

15 files changed

+664
-84
lines changed

15 files changed

+664
-84
lines changed

gs_quant/api/gs/portfolios.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,15 +184,17 @@ def get_positions_data(cls,
184184
start_date: dt.date,
185185
end_date: dt.date,
186186
fields: List[str] = None,
187-
position_type: PositionType = None) -> List[dict]:
187+
position_type: PositionType = None,
188+
include_all_business_days: bool = False) -> List[dict]:
188189
start_date_str = start_date.isoformat()
189190
end_date_str = end_date.isoformat()
190191
url = f'/portfolios/{portfolio_id}/positions/data?startDate={start_date_str}&endDate={end_date_str}'
191192
if fields is not None:
192193
url += '&fields='.join([''] + fields)
193-
194194
if position_type is not None:
195195
url += '&type=' + position_type.value
196+
if include_all_business_days:
197+
url += '&includeAllBusinessDays=true'
196198

197199
return GsSession.current._get(url)['results']
198200

gs_quant/base.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ def __setattr__(self, key, value):
195195

196196
key = snake_case_key
197197
value = self.__coerce_value(fld.type, value)
198-
self._property_changed(snake_case_key, value)
199198

200199
__setattr__(self, key, value)
201200

@@ -247,9 +246,6 @@ def _field_mappings(cls) -> Mapping[str, str]:
247246
cls.__field_mappings = field_mappings
248247
return cls.__field_mappings
249248

250-
def _property_changed(self, prop: str, value):
251-
pass
252-
253249
def clone(self, **kwargs):
254250
"""
255251
Clone this object, overriding specified values
@@ -481,14 +477,6 @@ def metadata(self):
481477
def metadata(self, value):
482478
self.__metadata = value
483479

484-
def _property_changed(self, prop: str, value):
485-
try:
486-
if self.__resolution_key and prop not in ('valuation_overrides', 'name'):
487-
self.unresolve()
488-
except AttributeError:
489-
# Can happen during init
490-
pass
491-
492480
def from_instance(self, instance):
493481
self.__resolution_key = None
494482
super().from_instance(instance)
@@ -504,12 +492,6 @@ def resolved(self, values: dict, resolution_key: RiskKey):
504492
new_instrument.__resolution_key = resolution_key
505493
return new_instrument
506494

507-
def unresolve(self):
508-
if self.__resolution_key and self.__unresolved:
509-
self.from_instance(self.__unresolved)
510-
self.__resolution_key = None
511-
self.__unresolved = None
512-
513495

514496
@dataclass
515497
class Market(ABC):

gs_quant/entities/entity.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,11 +451,15 @@ def get_positions_data(self,
451451
start: dt.date = DateLimit.LOW_LIMIT.value,
452452
end: dt.date = dt.date.today(),
453453
fields: [str] = None,
454-
position_type: PositionType = PositionType.CLOSE) -> List[Dict]:
454+
position_type: PositionType = PositionType.CLOSE,
455+
include_all_business_days: bool = False) -> List[Dict]:
455456
if self.positioned_entity_type == EntityType.ASSET:
457+
if include_all_business_days:
458+
raise MqError('"include_all_business_days" cannot be set to true for assets')
456459
return GsIndexApi.get_positions_data(self.id, start, end, fields, position_type)
457460
if self.positioned_entity_type == EntityType.PORTFOLIO:
458-
return GsPortfolioApi.get_positions_data(self.id, start, end, fields, position_type)
461+
return GsPortfolioApi.get_positions_data(self.id, start, end, fields,
462+
position_type, include_all_business_days)
459463
raise NotImplementedError
460464

461465
def get_position_dates(self) -> Tuple[dt.date, ...]:

gs_quant/instrument/core.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,6 @@ def dummy_result(self) -> Union[str, float]:
360360

361361
@dummy_result.setter
362362
def dummy_result(self, value: Union[str, float]):
363-
self._property_changed('dummy_result')
364363
self.__dummy_result = value
365364

366365
@property

gs_quant/markets/core.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ def __init__(self,
138138
get_enum_value(PricingLocation, market_data_location):
139139
raise ValueError('market.location and market_data_location cannot be different')
140140

141-
if not market and pricing_date and pricing_date > dt.date.today():
141+
if not market and pricing_date and pricing_date > dt.date.today() + dt.timedelta(5):
142+
# We allow a small tolerance to rolling over weekends/holidays
143+
# We should use a calendar but not everyone has access
142144
raise ValueError(
143145
'The PricingContext does not support a pricing_date in the future. Please use the RollFwd Scenario '
144146
'to roll the pricing_date to a future date')

gs_quant/markets/markets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def close_market_date(_location: Optional[Union[PricingLocation, str]] = None,
6161
from .core import PricingContext
6262
date = date or PricingContext.current.pricing_date
6363

64-
if date == dt.date.today():
64+
if date >= dt.date.today():
6565
# Don't use the calendars argument here as external users do not (yet) have access to that dataset
6666
date = prev_business_date(date)
6767

gs_quant/markets/portfolio_manager.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,30 @@ def get_custom_aum(self,
184184
return [CustomAUMDataPoint(date=dt.datetime.strptime(data['date'], '%Y-%m-%d'),
185185
aum=data['aum']) for data in aum_data]
186186

187+
def get_aum(self, start_date: dt.date, end_date: dt.date):
188+
"""
189+
Get AUM data for portfolio
190+
:param start_date: start date
191+
:param end_date: end date
192+
:return: dictionary of dates with corresponding AUM values
193+
"""
194+
aum_source = self.get_aum_source()
195+
if aum_source == RiskAumSource.Custom_AUM:
196+
aum = self.get_custom_aum(start_date=start_date, end_date=end_date)
197+
return {aum_point.date.strftime('%Y-%m-%d'): aum_point.aum for aum_point in aum}
198+
if aum_source == RiskAumSource.Long:
199+
aum = self.get_performance_report().get_long_exposure(start_date=start_date, end_date=end_date)
200+
return {row['date']: row['longExposure'] for index, row in aum.iterrows()}
201+
if aum_source == RiskAumSource.Short:
202+
aum = self.get_performance_report().get_short_exposure(start_date=start_date, end_date=end_date)
203+
return {row['date']: row['shortExposure'] for index, row in aum.iterrows()}
204+
if aum_source == RiskAumSource.Gross:
205+
aum = self.get_performance_report().get_gross_exposure(start_date=start_date, end_date=end_date)
206+
return {row['date']: row['grossExposure'] for index, row in aum.iterrows()}
207+
if aum_source == RiskAumSource.Net:
208+
aum = self.get_performance_report().get_net_exposure(start_date=start_date, end_date=end_date)
209+
return {row['date']: row['netExposure'] for index, row in aum.iterrows()}
210+
187211
def upload_custom_aum(self,
188212
aum_data: List[CustomAUMDataPoint],
189213
clear_existing_data: bool = None):

gs_quant/markets/securities.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,14 +1549,29 @@ def get_asset_id_type(type_: SecurityIdentifier):
15491549
values.append(asset_id)
15501550
elif output_type == "bbg":
15511551
if SecurityIdentifier.BBG in output_types:
1552-
inner[output_type] = output_value
1552+
if SecurityIdentifier.BBG.value not in inner:
1553+
inner[SecurityIdentifier.BBG.value] = []
1554+
inner[SecurityIdentifier.BBG.value].append(output_value)
15531555
if SecurityIdentifier.BBID in output_types:
1554-
inner[SecurityIdentifier.BBID.value] = f"{output_value} {row.get('exchange', '??')}"
1556+
exchange = row.get('exchange')
1557+
if SecurityIdentifier.BBID.value not in inner:
1558+
inner[SecurityIdentifier.BBID.value] = []
1559+
if exchange is not None:
1560+
inner[SecurityIdentifier.BBID.value].append(f"{output_value} {exchange}")
1561+
else:
1562+
inner[SecurityIdentifier.BBID.value].append(f"{output_value}")
15551563
if SecurityIdentifier.BCID in output_types:
1556-
inner[SecurityIdentifier.BCID.value] = f"{output_value} {row.get('compositeExchange', '??')}"
1564+
composite_exchange = row.get('compositeExchange')
1565+
if composite_exchange is not None:
1566+
if SecurityIdentifier.BCID.value not in inner:
1567+
inner[SecurityIdentifier.BCID.value] = []
1568+
inner[SecurityIdentifier.BCID.value].append(f"{output_value} {composite_exchange}")
15571569
else:
15581570
if SecurityIdentifier(output_type) in output_types:
1559-
inner[output_type] = output_value
1571+
if output_type not in inner:
1572+
inner[output_type] = []
1573+
if output_value not in inner[output_type]:
1574+
inner[output_type].append(output_value)
15601575

15611576
current += date_delta
15621577

gs_quant/test/markets/test_pricing_context.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,29 @@
1717

1818
import pytest
1919
from gs_quant import risk
20+
from gs_quant.common import PayReceive, Currency
2021
from gs_quant.datetime import business_day_offset
2122
from gs_quant.instrument import IRSwap
2223
from gs_quant.markets import PricingContext, CloseMarket, OverlayMarket, MarketDataCoordinate
2324
from gs_quant.risk import RollFwd
25+
from gs_quant.target.common import PricingLocation
2426
from gs_quant.test.utils.test_utils import MockCalc
2527

28+
WEEKEND_DATE = dt.date(2022, 3, 19)
29+
30+
31+
@pytest.fixture
32+
def today_is_saturday(monkeypatch):
33+
class MockedDatetime:
34+
@classmethod
35+
def today(cls):
36+
return WEEKEND_DATE
37+
38+
monkeypatch.setattr(dt, 'date', MockedDatetime)
39+
2640

2741
def test_pricing_context(mocker):
28-
swap1 = IRSwap('Pay', '1y', 'EUR', name='EUR1y')
42+
swap1 = IRSwap(PayReceive.Pay, '1y', Currency.EUR, name='EUR1y')
2943
future_date = business_day_offset(dt.date.today(), 10, roll='forward')
3044
with MockCalc(mocker):
3145
with RollFwd(date='10b', realise_fwd=True):
@@ -47,14 +61,22 @@ def test_pricing_context(mocker):
4761

4862
def test_pricing_dates():
4963
# May be on weekend but doesn't matter for basic test
50-
future_date = dt.date.today() + dt.timedelta(2)
64+
future_date = dt.date.today() + dt.timedelta(10)
5165
yesterday = dt.date.today() - dt.timedelta(1)
5266
pc = PricingContext(pricing_date=future_date, market=CloseMarket(yesterday))
5367
assert pc is not None
5468
with pytest.raises(ValueError, match="pricing_date in the future"):
5569
PricingContext(pricing_date=future_date)
5670

5771

72+
def test_weekend_dates(today_is_saturday):
73+
assert dt.date.today() == WEEKEND_DATE # Check mock worked
74+
next_monday = WEEKEND_DATE + dt.timedelta(2)
75+
prev_friday = WEEKEND_DATE - dt.timedelta(1)
76+
pc = PricingContext(pricing_date=next_monday)
77+
assert pc.market == CloseMarket(prev_friday, PricingLocation.LDN)
78+
79+
5880
def test_market_data_object():
5981
coord_val_pair = [
6082
{'coordinate': {

0 commit comments

Comments
 (0)