Giter VIP home page Giter VIP logo

gs-quant's Introduction

GS Quant

GS Quant is a Python toolkit for quantitative finance, created on top of one of the world’s most powerful risk transfer platforms. Designed to accelerate development of quantitative trading strategies and risk management solutions, crafted over 25 years of experience navigating global markets.

It is created and maintained by quantitative developers (quants) at Goldman Sachs to enable the development of trading strategies and analysis of derivative products. GS Quant can be used to facilitate derivative structuring, trading, and risk management, or as a set of statistical packages for data analytics applications.

Please refer to Goldman Sachs Developer for additional information.

Requirements

  • Python 3.6 or greater
  • Access to PIP package manager

Installation

pip install gs-quant

Examples

You can find examples, guides and tutorials in the respective folders as well as on Goldman Sachs Developer.

Contributions

Contributions are encouraged! Please see CONTRIBUTING for more details.

Help

Please reach out to [email protected] with any questions, comments or feedback.

gs-quant's People

Contributors

anastasiyab avatar andrewphillipsn avatar brice1997 avatar bryangalindo avatar chrisdroukas avatar david-vanden-bon avatar dominiccyk avatar ehoffm03 avatar francisg77 avatar gg4real avatar itrestian avatar martinroberson avatar ml874 avatar nick-young-gs avatar razvan-dorobantu avatar robertoronderosjr avatar rtsscy avatar s-manglik avatar scottweinstein avatar stephen-183 avatar yiding15 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gs-quant's Issues

repeat() function

Describe the problem.
Provide a repeat() function that repeats the last available values in a timeseries for missing date.

Describe the solution you'd like
repeat(x, mode) function takes a timeseries and a mode argument. The mode argument can be either CALENDAR_DAYS and it then repeats values on all calendar days or WORK_DAYS and then skips Saturdays and Sundays.

Describe alternatives you've considered
We considered having a UNION mode that would align the series with the date of another series and fill the missing values but that would be redundant as the align() already supports that use case.

Are you willing to contribute
Yes

[BUG] `Dataset.get_data` fails with string `start_time`/`end_time`

Describe the bug
Dataset.get_data fails with string start_time/end_time

To Reproduce

from gs_quant.data import Dataset

ds = Dataset("EDRVS_EXPIRY_INTRADAY_PREMIUM")

ds.get_data(
    assetId=[
        "MA067JXW39B5T8VS",
        "MA07M4WC9PY2FBFG",
        "MA09RV3TD3ZEAZY3",
        "MA0B74FZFBAMZMQP",
        "MA0CT27ED3K3JD04",
    ],
    start_time="2022-03-03T00:00:00+00:00",
    end_time="2022-03-03T23:59:59+00:00",
    limit=10_000,
    strikeReference=["spot", "forward", "normalized", "delta"],
)

Expected behavior
get back a dataframe of data with no exceptions

Screenshots
Seems unneeded

Systems setup:

  • OS: Windows 10
  • Python version: '3.8.12 | packaged by conda-forge | (default, Oct 12 2021, 21:22:46) [MSC v.1916 64 bit (AMD64)]'
  • GS-Quant version: '0.9.9'

Additional context
below is the stack trace. Didn't dig into your code fully, but seems to expect a timestamp, not a string, though does work if I pass in pd.Timestamps:

ds.get_data(
    assetId=[
        "MA067JXW39B5T8VS",
        "MA07M4WC9PY2FBFG",
        "MA09RV3TD3ZEAZY3",
        "MA0B74FZFBAMZMQP",
        "MA0CT27ED3K3JD04",
    ],
    start_time=pd.Timestamp("2022-03-03T00:00:00+00:00"),
    end_time=pd.Timestamp("2022-03-03T23:59:59+00:00"),
    limit=10_000,
    strikeReference=["spot", "forward", "normalized", "delta"],
)

TypeError Traceback (most recent call last)
Input In [7], in <cell line: 1>()
----> 1 ds.get_data(
2 assetId=[
3 "MA067JXW39B5T8VS",
4 "MA07M4WC9PY2FBFG",
5 "MA09RV3TD3ZEAZY3",
6 "MA0B74FZFBAMZMQP",
7 "MA0CT27ED3K3JD04",
8 ],
9 start_time="2022-03-03T00:00:00+00:00",
10 end_time="2022-03-03T23:59:59+00:00",
11 limit=10_000,
12 strikeReference=["spot", "forward", "normalized", "delta"],
13 )

File C:\ProgramData\Miniconda3\envs\t_env\lib\site-packages\gs_quant\data\dataset.py:122, in Dataset.get_data(self, start, end, as_of, since, fields, asset_id_type, **kwargs)
100 """
101 Get data for the given range and parameters
102
(...)
117 >>> weather_data = weather.get_data(dt.date(2016, 1, 15), dt.date(2016, 1, 16), city=('Boston', 'Austin'))
118 """
120 field_names = None if fields is None else list(map(lambda f: f if isinstance(f, str) else f.value, fields))
--> 122 query = self.provider.build_query(
123 start=start,
124 end=end,
125 as_of=as_of,
126 since=since,
127 fields=field_names,
128 **kwargs
129 )
130 data = self.provider.query_data(query, self.id, asset_id_type=asset_id_type)
132 return self.provider.construct_dataframe_with_types(self.id, data)

File C:\ProgramData\Miniconda3\envs\t_env\lib\site-packages\gs_quant\api\data.py:101, in DataApi.build_query(start, end, as_of, since, restrict_fields, format, dates, **kwargs)
99 snake_case_field = inflection.underscore(field)
100 if snake_case_field in query_properties:
--> 101 setattr(query, snake_case_field, value)
102 else:
103 query.where[field] = value

File C:\ProgramData\Miniconda3\envs\t_env\lib\site-packages\gs_quant\base.py:226, in Base.setattr(self, key, value)
223 raise ValueError(f'{key} cannot be set')
225 key = snake_case_key
--> 226 value = self.__coerce_value(fld.type, value)
227 self._property_changed(key, value)
228 elif key == 'name':

File C:\ProgramData\Miniconda3\envs\t_env\lib\site-packages\gs_quant\base.py:253, in Base.__coerce_value(cls, typ, value)
251 return value.to_dict()
252 if _is_supported_generic(typ):
--> 253 return _decode_generic(typ, value, False)
254 else:
255 return value

File C:\ProgramData\Miniconda3\envs\t_env\lib\site-packages\dataclasses_json\core.py:277, in decode_generic(type, value, infer_missing)
275 res = _decode_generic(type_arg, value, infer_missing)
276 else:
--> 277 res = _support_extended_types(type_arg, value)
278 else: # Union (already decoded or unsupported 'from_json' used)
279 res = value

File C:\ProgramData\Miniconda3\envs\t_env\lib\site-packages\dataclasses_json\core.py:220, in _support_extended_types(field_type, field_value)
218 else:
219 tz = datetime.now(timezone.utc).astimezone().tzinfo
--> 220 res = datetime.fromtimestamp(field_value, tz=tz)
221 elif _issubclass_safe(field_type, Decimal):
222 res = (field_value
223 if isinstance(field_value, Decimal)
224 else Decimal(field_value))

TypeError: an integer is required (got type str)

statsmodels upgrade

Describe the problem.
statsmodels < 13 can't be installed with scipy >= 1.8.0: https://stackoverflow.com/questions/71106940/cannot-import-name-centered-from-scipy-signal-signaltools

This is a valid configuration per this projects setup.py:

gs-quant/setup.py

Lines 76 to 77 in 1272790

"scipy>=1.2.0,<=1.8.0;python_version>'3.6'",
"statsmodels>=0.11.1,<0.13.0",

Repro:

python3 -m venv .venv
source .venv/bin/activate
pip install gs-quant
python -c "from statsmodels.api import OLS"

Traceback:

  from pandas import (to_datetime, Int64Index, DatetimeIndex, Period,
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/jabbera/stats/.venv/lib/python3.8/site-packages/statsmodels/api.py", line 27, in <module>
    from .tsa import api as tsa
  File "/home/jabbera/stats/.venv/lib/python3.8/site-packages/statsmodels/tsa/api.py", line 31, in <module>
    from .filters import api as filters
  File "/home/jabbera/stats/.venv/lib/python3.8/site-packages/statsmodels/tsa/filters/api.py", line 6, in <module>
    from .filtertools import miso_lfilter, convolution_filter, recursive_filter
  File "/home/jabbera/stats/.venv/lib/python3.8/site-packages/statsmodels/tsa/filters/filtertools.py", line 18, in <module>
    from scipy.signal.signaltools import _centered as trim_centered
ImportError: cannot import name '_centered' from 'scipy.signal.signaltools' (/home/jabbera/stats/.venv/lib/python3.8/site-packages/scipy/signal/signaltools.py)

Describe the solution you'd like
Remove the requirement for statsmodels < 13. I really don't want to be pinned to scipy < 1.8.0

Describe alternatives you've considered
Downgrade scipy.

Are you willing to contribute
Need to check with my legal department.

Missing issue template for both feature request and bug report

Andy's Proposal for Feature Request:

Please make sure that this is a feature request. As per our GitHub Policy, we only address code/doc bugs, performance issues, feature requests and build/installation issues on GitHub.

System information

TensorFlow version (you are using):
Are you willing to contribute it (Yes/No):
Describe the feature and the current behavior/state.

Will this change the current api? How?

Who will benefit with this feature?

Any Other info.

Github's Default Proposal for Feature Request

Describe the problem.
A clear and concise description of the problem.

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Are you willing to contribute
Yes/No

Additional context
Add any other context or screenshots about the feature request here

Github's Default Proposal for Bug Report

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

What does everyone think?

[BUG] ModuleNotFoundError in Dataset.get_coverage

Describe the bug

Error raised with trying to find dataset coverage.

To Reproduce

  ds = Dataset('EDRVOL_PERCENT_STANDARD')
  ds.get_coverage()

This raises ModuleNotFoundError: No module named 'gs_quant.config'

Expected behavior

Return a dataframe with the dataset coverage.

Screenshots
If applicable, add screenshots to help explain your problem.

Systems setup:

  • OS: Windows 10
  • Python 3.8.8
  • GS-Quant version '0.8.272'

Unable to roundtrip Portfolio.to_csv -> Portfolio.from_csv

Describe the bug
A Portfolio of instruments can be serialised to disk via portfolio.to_csv() however the output csv file cannot then be reloaded via Portfolio.from_csv().
Effectively the to/from csv feature is useless as we cannot round trip portfolios of instruments.

To Reproduce

from gs_quant.common import PayReceive, Currency
from gs_quant.instrument import IRSwap
from gs_quant.markets.portfolio import Portfolio


def build_portfolio() -> Portfolio:
    p = Portfolio()
    for ccy in (Currency.EUR, Currency.GBP, Currency.USD, Currency.AUD):
        for tenor in (f'{x}Y' for x in range(1, 31)):
            p.append(
                IRSwap(PayReceive.Receive,
                       notional_amount=100_000_00,
                       notional_currency=ccy,
                       fixed_rate='atm',
                       name=f'{ccy}_{tenor}'
                       )
            )
    return p


def main():
    file_name = r'c:\temp\portfolio.csv'
    build_portfolio().to_csv(file_name)
    load_portfolio = Portfolio.from_csv(file_name)
    assert load_portfolio == build_portfolio()


if __name__ == '__main__':
    main()

Expected behavior
To be able to load a portfolio of trades from csv.
The above code should run to completion.

Screenshots
image.

Systems setup:

  • OS: Windows 10
  • Python version 3.8.5
  • GS-Quant version 0.9.82

Additional context
The issue is one of data sanitation. Before saving to csv all the Enums should be replaced by their .values (as when serialising to JSON or MSGPack) this way when read back in the get_enum_from() code will be able to convert a 'Swap' into an AssetType.Swap - at the moment it fails to convert an 'AssetType.Swap'

Instead of saving this:
,asset_class,fee,fixed_rate,name,notional_amount,notional_currency,pay_or_receive,type
0,AssetClass.Rates,0.0,atm,Currency.EUR_1Y,10000000,Currency.EUR,PayReceive.Receive,AssetType.Swap
1,AssetClass.Rates,0.0,atm,Currency.EUR_2Y,10000000,Currency.EUR,PayReceive.Receive,AssetType.Swap
2,AssetClass.Rates,0.0,atm,Currency.EUR_3Y,10000000,Currency.EUR,PayReceive.Receive,AssetType.Swap
3,AssetClass.Rates,0.0,atm,Currency.EUR_4Y,10000000,Currency.EUR,PayReceive.Receive,AssetType.Swap

You would save:
,asset_class,fee,fixed_rate,name,notional_amount,notional_currency,pay_or_receive,type
0,Rates,0.0,atm,Currency.EUR_1Y,10000000,EUR,Receive,Swap
1,Rates,0.0,atm,Currency.EUR_2Y,10000000,EUR,Receive,Swap
2,Rates,0.0,atm,Currency.EUR_3Y,10000000,EUR,Receive,Swap
3,Rates,0.0,atm,Currency.EUR_4Y,10000000,EUR,Receive,Swap

One could make the argument that all the Enums you declare should have a repr of self.value rather than just have that in the parent class EnumBase - as somehow the MRO is getting messed around and Currency.EUR has a MRO of (<enum 'Currency'>, <class 'gs_quant.base.EnumBase'>, <enum 'Enum'>, <class 'object'>) and so the repr of Currency.EUR is 'Currency.EUR' rather than 'EUR' - which is what I assume was the intention of overriding the _repr on EnumBase?

WIP: Backtest Module

Describe the problem.
This is a feature proposal to add a first version of the backtest module in gs_quant. This module is a work-in-progress and would continue to improve over the coming months.

Describe the solution you'd like
For the first working version of the backtest module, we wanted to support backtesting for systematic strategies on Equity Options. By systematic strategy, we refer to non-event based strategies where the strategy gets into a well defined portfolio every day, optionally hedges the delta risk and roll positions in the portfolio after a fixed time.

Example code snippet would be as following

underlierList = [
EqOption("SPX", "3m", 3000, OptionType.Call, OptionStyle.European),
EqOption("SPX", "3m", 3000, OptionType.Put, OptionStyle.European)
]

hedge = DeltaHedgeParameters(frequency="Daily")
strategy = StrategySystematic(name = "Mock Test",
                                underliers=underlierList,
                                delta_hedge=hedge,
                                quantity=100000,
                                quantity_type= QuantityType.Notional,
                                trade_in_method= TradeInMethod.FixedRoll,
                                roll_frequency="1m")

result = strategy.backtest(start="2019-06-01", end="2019-07-01")

In the coming months, we would want to add support for signal/event driven strategies, intraday actions, different trading and transaction cost models and support for more products.

Are you willing to contribute
Yes

[BUG] gs_quant python entity Country not working

Describe the bug
country entity only work with lower-case non-space country name

To Reproduce

from gs_quant.entities.entity import Country, Subdivision
from gs_quant.markets.securities import Asset, AssetIdentifier


for cty in ['taiwan', 'Taiwan', 'china', 'united states', 'United Kingdom']:
    print( cty, ': ', Country.get(cty, Country.Identifier.NAME) )

Expected behavior
all countries should be found

Screenshots
image

Systems setup:

  • OS: Windows 10
  • Python version
  • GS-Quant version 1.0.3

Additional context
Add any other context about the problem here.

Portfolios

Describe the problem.
What is the best way to represent a Portfolio that can consists of so many types of investments?

Describe the solution you'd like
I would like to leverage the concept of an Instrument to describe a Position in a gs-quant Portfolio. An Instrument is an object with industry-standard required fields that all-together describe a unique investment. Using instruments to represent positions in a portfolio will allow a gs-quant user to quickly understand how to add investments of any type to their portfolio and calculate analytics such as pricing or risk in a way that is agnostic to the portfolio's contents.

Here are a few code examples:

Example 1:

from gs_quant.session import *
from gs_quant.portfolio import GsPositionSet, GsPortfolio
from gs_quant.instrument import IRSwaption, EqOption
import matplotlib.pyplot as plt

with GsSession.get(Environment.PROD):

    positions = GsPositionSet([
        IRSwaption(PayReceive.Receive, '5y', 'USD', expirationDate='13m', strike='atm+40', notionalAmount=1e8),
        EqOption('SPX', date(2020, 12, 31), 2800, OptionType.Call, OptionStyle.European)
    ], date(2019, 05, 10))

    positions.calculate_prices('USD')

    portfolio = GsPortfolio('Derek's Example Portfolio', 'USD', [positions])
    portfolio.create_performance_report()
    portfolio.schedule_reports()
    
    performance = portfolio.get_normalized_performance()
    plt.plot(performance, label='Portfolio Performance')
    plt.show()

Example 2:

from gs_quant.session import *
from gs_quant.portfolio import GsPositionSet
from gs_quant.index import GsBasket
from gs_quant.instrument import Equity

with GsSession.get(Environment.PROD):

    positions = GsPositionSet([
        Equity('GS', 24222, AssetIdentifier.TICKER),
        Equity('AAPL', 23984, AssetIdentifier.TICKER)
    ], date(2019, 05, 10))

    positions.calculate_prices('USD')
    positions.calculate_liquidity('APEX', '.10') # 10% participation rate

    SPX = SecurityMaster.get_asset("SPX", AssetIdentifier.TICKER, asset_type=AssetType.INDEX)
    hedge = positions.hedge('Replicate Performance', universe=SPX.getUnderlyingAssetIds())

    hedge_positions = GsPositionSet(hedge.get_constituents())
    hedge_positions.calculate_liquidity('APEX', '.10') # 10% participation rate

    hedge_positions = [p for p in hedge_positions if p['adv22DayPct'] < 5.0]
    hedge_positions = [p for p in hedge_positions if p['transactionCostBps'] < 14.50]

    hedge_basket = GsBasket(
        "GSCBMARQ",
        "Basket From Hedge",
        hedge_positions,
        PublishParameters(False, False, False),
        100
    )

    hedge_basket.create()

Are you willing to contribute
Yes

Additional context
Let's build context from the ground up...

  • Position - A unique notional investment at a given point-in-time.
  • PositionSet - A set of Positions you're invested at a given point-in-time.
  • Portfolio - A set of PositionSets that you've invested in at different points in time.

RiskMeasure - how to check for equality : RiskMeasure.from_dict(IRFwdRate.as_dict()) == IRFwdRate returns False

Describe the bug
RiskMeasure.from_dict(IRFwdRate.as_dict()) == IRFwdRate
Returns False. Arguably this should return True.

What is the correct way to check for RiskMeasure equality?

For example:
RiskMeasure.from_dict(IRFwdRate.as_dict()).measure_type == IRFwdRate.measure_type
returns True, but this feels a little clunky.

I ask as I'd like to write code like:
if risk_measure in (IRFwdRate, IRSpotRate, etc, etc):

where risk_measure has been created by RiskMeasure.from_dict(some_measure.as_dict()).

To Reproduce
from gs_quant.risk import IRFwdRate
from gs_quant.target.risk import RiskMeasure
RiskMeasure.from_dict(IRFwdRate.as_dict()) == IRFwdRate

Returns False.

Expected behavior
RiskMeasure.from_dict(IRFwdRate.as_dict()) == IRFwdRate to return True.

OR please explain how I should compare RiskMeasure types

PricingContext._handle_results incorrectly removes unfetched Futures

PricingContext._handle_results incorrectly removes unfetched Futures

PricingContext in markets.core

The following code (with dummy details) should return the ir delta of the swap and the PV of the bond. - however it throws a KeyError

with dev_session:        
        with PricingContext(is_batch=False):
            irs = IRSwap(PayReceive.Receive,'5y', Currency.GBP, 10000000, fixed_rate=0.05)
            irs_delta = irs.calc(IRDelta)
            bond = Security(isin='XS0123456789')
            bond_price = bond.price()

        print('irs delta')
        print(irs_delta.result())
        print('bond price')
        print(bond_price.result())

However the code fails with the following error:

    self._calc()
  File gs_quant\markets\core.py, line 237, in _calc
    run_request(risk_request, GsSession.current)
  File gs_quant\markets\core.py, line 201, in run_request
    self._handle_results(calc_result)
  File gs_quant\markets\core.py, line 256, in _handle_results
    positions_for_measure = self.__futures[risk_measure]
KeyError: Price

Explanation:
Two Futures are created in PricingContext.__futures as {IRDelta: Future1, Price: Future2}.
The results are fetched sequentially from the Session.
While processing the first returned results, the IRDelta, PricingContext._handle_results correctly removes the first future {IRDelta:Future1} from __Futures and sets the IRDelta onto the Future (via .set_results) - all this happens in lines 249 to 260
However _handle_results assumes that all remaining Futures are bad (see comment on line 262) and removes them from __futures (lines 264 to 268).
As a result when the second set of results, bond price, come back from the Session and flow through _handle_results __futures is an empty dictionary and so the bond price can't be bound to a future (as the future was destroyed while processing the previous result)

[BUG] fields argument in Dataset.get_data makes non-selected columns NaN but all columns still present in DataFrame

Describe the bug
Non-selected fields become NaN but all fields are still present in the dataframe - this is different from the example output in the docs.

To Reproduce
Run the code from the "Fields Selection" section of this page:

from gs_quant.data import Dataset
from datetime import date

weather_ds = Dataset('WEATHER')
data_frame = weather_ds.get_data(date(2016, 1, 1), date(2016, 1, 2), city=["Boston"], fields=['maxTemperature', 'minTemperature'])

print(data_frame)

Expected behavior
Returns:

     city        date  maxTemperature  minTemperature
0  Boston  2016-01-01            41.0            33.0
1  Boston  2016-01-02            40.0            31.0

Actual behavior
Returns:

              city  maxTemperature  minTemperature  dewPoint  windSpeed   
date                                                                      
2016-01-01  Boston            41.0            33.0       NaN        NaN  \
2016-01-02  Boston            40.0            31.0       NaN        NaN   

            precipitation  snowfall  pressure  updateTime  
date                                                       
2016-01-01            NaN       NaN       NaN         NaN  
2016-01-02            NaN       NaN       NaN         NaN  

Systems setup:

  • OS: Windows 10
  • Python version: 3.11
  • GS-Quant version: 1.0.44

Vol structure

Describe the problem.
Vol structure for equities should be available in gs-quant and PlotTool.

Describe the solution you'd like
Implement in the measures module.

@plot_measure((AssetClass.Equity,), (AssetType.ETF, AssetType.Index))
def vol_structure(asset, relativeStrike):
    # implementation uses EDRVOL_PERCENT or EDRVOL_PERCENT_EXPIRY

gs-quant usage: call function within a DataContext that specifies date range.
PlotTool usage: write SPX.vol_structure(1.0) in expressions window, set date range in UI.

If date range indicates the expiration dates that should be shown, is another variable needed for the date that the term structure was generated?

Are you willing to contribute
Yes

[BUG] Dataset date handling is broken with dataclasses-json version 0.6.5 dependency, gives ValueError: Cannot convert 2024-04-29 to date

Describe the bug
Dataset date handling is broken

This problem is caused by the change in the dataclasses-json, since version 0.6.5 of the library.
That library is a dependency of gs-quant that was changed here:
https://github.com/lidatong/dataclasses-json/releases/tag/v0.6.5

The breaking change was introduced on 29th of April 2024 by the library dependency.

To Reproduce
Steps to reproduce the behavior with code example:

from gs_quant.data import Dataset
import datetime as dt
ds = Dataset('EDRVOL_PERCENT_PREMIUM')
date = dt.datetime.strptime('2024-04-29', '%Y-%m-%d').date() 
assetId = 'MA4B66MW5E27U8P32SB'
ds.get_data(start=date, end=date, assetId=assetId)

Gives an error:

Traceback (most recent call last):
  File "C:\Code\GS\date_issue.py", line 6, in <module>
    ds.get_data(start=date, end=date, assetId=assetId)
  File "C:\Code\GS\.venv\Lib\site-packages\gs_quant\data\dataset.py", line 156, in get_data
    query = self.provider.build_query(
            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Code\GS\.venv\Lib\site-packages\gs_quant\api\data.py", line 88, in build_query
    query = DataQuery(
            ^^^^^^^^^^
  File "C:\Code\GS\.venv\Lib\site-packages\gs_quant\base.py", line 86, in wrapper
    return init(self, *args, **normalised_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 8, in __init__
  File "C:\Code\GS\.venv\Lib\site-packages\gs_quant\base.py", line 231, in __setattr__
    value = self.__coerce_value(fld.type, value)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Code\GS\.venv\Lib\site-packages\gs_quant\base.py", line 252, in __coerce_value
    return _decode_generic(typ, value, False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Code\GS\.venv\Lib\site-packages\dataclasses_json\core.py", line 323, in _decode_generic
    res = _decode_type(type_arg, value, infer_missing)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Code\GS\.venv\Lib\site-packages\dataclasses_json\core.py", line 233, in _decode_type
    return _get_decoder_in_global_config(type_)(value)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Code\GS\.venv\Lib\site-packages\gs_quant\json_convertors.py", line 39, in decode_optional_date
    raise ValueError(f'Cannot convert {value} to date')
ValueError: Cannot convert 2024-04-29 to date

Expected behavior
The code should not throw a ValueError

Screenshots
Not applicable

Systems setup:

  • OS: Windows 10
  • Python version: 3.11.6
  • GS-Quant version 1.0.72

Additional context
The workaround for the issue is to pin dataclasses-json to version 0.6.4

Content module

Describe the problem

Currently there is no programatic way of accessing Marquee Content through gs_quant. This is a feature proposal to add a content module for interacting with the new Marquee Content API (/v1/content).

Describe the solution you'd like

State of the world:

At the time of writing, there are two primary means of retrieving content via the Marquee Content API:

Description Method Endpoint Developer Site URL
Get a content piece GET /v1/content/{id} Link
Get many content pieces GET /v1/content Link

Eventually, the entire suite of endpoints will be implemented which will allow querying, searching, updating, and creation of content.

Proposed Solution:

Get Many Contents:

gs_quant should expose out a Content module for supporting the above endpoints.

from gs_quant.content import Content

content = Content()
content.get_many(**kwargs)

# Kwargs correspond to supported query params on the API. I.e:
#
# content.get_many(authorId=<some_author_id>)
# content.get_many(tag=<some_tag>)
# content.get_many(assetId=<some_asset_id>)
# etc...

Get a Single Content:

gs_quant should expose out a Content module for supporting the above endpoints.

from gs_quant.content import Content

content = Content()
content.get('<some_content_id>')

All returned content will be of the form ContentResponse. A link to this object can be found here on the Marquee Developer Site.

By default, all content is Base64 encoded along with the associated MimeType. This allows for transporting the content via JSON, given that we support many different content types (HTML, text, image, PDF, etc...).

A client of gs_quant using that content module might then do:

import base64
from gs_quant.content import Content

content = Content()
response = content.get('<some_content_id>')

text = base64.b64decode(response.content.body)
# <html><h1>blah blah blah</h1></html>

Describe alternatives you've considered

Currently bouncing between the following two implementation styles:

  1. Declare a Content() object (as examples show above) that creates an instance of the class, for doing things like:
content = Content()
content.get()
content.get_many()
...
  1. Go the route of the Dataset model, where the code would look like this:
content = Content('<some_content_id>')
contents = Content('<some_content_id_1>', '<some_content_id_2>', ...)

Not really a fan of this approach for content as I think it's a little awkward / doesn't really provide a fluent API for querying/searching.

Are you willing to contribute
Yes!

Additional context

N/A

@andyphillipsgs @francisg77 @bobbyalex83 @ScottWeinstein

Add support for pagination or scrolling in `Dataset.get_data`

Describe the problem.
For requests that are apparently too large, the API will return a timeout error. It doesn't seem clear beforehand what exactly will be a request that is too large, and a timeout error is not particularly helpful.

I opened a ticket with Marquee support asking for the best way to do this or a fix for it, but haven't heard back in a couple days.

Describe the solution you'd like
The possibly already supported pagination or scrolling could be made accessible in the method. Then, I can just create a wrapper that will just iterate over chunks and combine the results.

Describe alternatives you've considered
I have some code that iterates over years, but that sometimes fails. I could do smaller date ranges, but that would be overkill for smaller requests. I think the biggest issue with alternatives is that I don't want to have to chunk before I know when it might fail because each call introduces latency to my code.

Are you willing to contribute
Yes

Additional context
I can provide examples of requests that timed out if that's helpful, though running the examples might require access to our paid datasets.

Remove pandas version restriction

Describe the problem.
There are bug fixes and features available in newer versions of pandas that I would like to have access to.
In my non-thorough testing, this package works just fine with newer versions of pandas (1.2.0).

Describe the solution you'd like
Testing of the package with the version constraint removed followed by removing the version restriction.

Describe alternatives you've considered
I currently just have multiple environments with different package versions to work around this restriction.
If there are issues found, the restriction is reasonable to remain, but I would be willing to help fix issues that are found.

Are you willing to contribute
Yes

Additional context
Because pandas follows semantic versioning, there should be no breaking changes going from 1.0 to 1.2 unless you are relying on some internal implementation details in pandas or a bug.

[BUG]

Describe the bug
Sample code does not work. Window is not defined in the example.

To Reproduce
Run first sample code in github page

I fixed this by adding ts to the front window variable but I think the example should be updated.
This is what works - (ts added to ..ts.Window(22, 0)) )

import gs_quant.timeseries as ts

x = ts.generate_series(1000) # Generate random timeseries with 1000 observations
vol = ts.volatility(x, ts.Window(22, 0)) # Compute realized volatility using a window of 22 and a ramp up value of 0
vol.tail()

[BUG] The variable name for creating instruments is wrong.

Describe the bug
Cannot run the code snippet in the documentation for creating financial instruments.

Example error:
TypeError: init() got an unexpected keyword argument 'fixed_rate'

To Reproduce
Use the code snippet
https://pypi.org/project/gs-quant/
https://developer.gs.com/docs/gsquant/guides/Pricing-and-Risk/instruments/

Expected behavior
Code snippet should work

Systems setup:

  • GS-Quant version 0.8.77

Proposed Change
https://pypi.org/project/gs-quant/
fixed_rate -> fixedRate

https://developer.gs.com/docs/gsquant/guides/Pricing-and-Risk/instruments/
expiration_date -> expirationDate
notional_amount -> notionalAmount

BUG - sample code doesn't work

> python .\sample.py
Traceback (most recent call last):
  File ".\sample.py", line 17, in <module>
    coverage = weather.get_coverage(weather)  # GS-specific functionality
  File "C:\Users\scott\AppData\Roaming\Python\Python37\site-packages\gs_quant\data\dataset.py", line 234, in get_coverage
    fields=fields
  File "C:\Users\scott\AppData\Roaming\Python\Python37\site-packages\gs_quant\api\gs\data.py", line 137, in get_coverage
    body = GsSession.current._get('/data/{}/coverage'.format(dataset_id), payload=params)
  File "C:\Users\scott\AppData\Roaming\Python\Python37\site-packages\gs_quant\session.py", line 165, in _get

    return self.__request('GET', path, payload=payload, cls=cls, include_version=include_version)
  File "C:\Users\scott\AppData\Roaming\Python\Python37\site-packages\gs_quant\session.py", line 140, in __request
    raise MqRequestError(response.status_code, response.text, context='{} {}'.format(method, url))
gs_quant.errors.MqRequestError: context: GET https://api.marquee.gs.com/v1/data/WEATHER/coverage
status: 400, message: {"statusCode":400,"reasonPhrase":"Bad Request","title":"Path/Query Parameter Validation Error","messages":["Failed validation of request path and/or query parameters: [ECMA 262 regex \"^[\\d]{1,256}$\" does not match input string \"<gs_quant.data.dataset.Dataset object at 0x000002061B96F6D8>\"]"]}

Regression in PlotToolPro

Describe the problem.
Regression should be available in PlotToolPro.

Describe the solution you'd like
Add a class in gs-quant (constructor in PlotToolPro) for the model fitting which has methods to get model parameters and perform prediction.

For example, linear regression should take the form LinearRegression(X, y, w) where X denotes explanatory variables [x1, x2, x3, ...], y is the dependent variable and w is the moving window. The class has methods:
coefficient(i) - the i-th coefficient
rsquare() - model R squared
tstat(i) - t-stat of the i-th coefficient
fitted_values() - fitted values
predict(X') where X' is the test set of explanatory variables - predicated values (Note: if multiple regression models have been fitted over the moving window, the most recent model will be used for prediction)

In PlotToolPro, users could fit a linear regression between changes of implied volatility of S&P 500 index and returns on the underlying by:

spx = returns(SPX.spot(), 1, simple)
spx_vol = diff(SPX.implied_volatility(1m, forward, 100), 1)
r = LinearRegression([spx], spx_vol, 60)
r.fitted_values()

Are you willing to contribute
Yes

Docs for build and test

Describe the problem.
Docs are missing details on how to build the project

Describe the solution you'd like
Updated readme

Describe alternatives you've considered
None

Are you willing to contribute
Yes

Sharpe ratio and information ratio

Describe the problem.
Provide an implementation for sharpe ratio and information ratio calculations in gs_quant.

Describe the solution you'd like
Information ratio should calculate the rolling return per unit volatility of a series versus a benchmark series. Sharpe ratio calculation should compute rolling return per unit volatility of a series versus risk free rate for currency of the target series (i.e excess return). Both of these measures should calculate excess return daily with correct holiday / interpolation handling

Are you willing to contribute
Yes

Additional context
Needs full regtesting. Identify correct data sources for risk free rates

Error text on invalid scope is overly verbose

Describe the problem.
The error text if your application account doen't have the correct scope is too verbose

> python .\sample.py
Traceback (most recent call last):
  File ".\sample.py", line 14, in <module>
    with GsSession.get(Environment.PROD, CLIENTID, SECRET, scopes=('read_product_data', 'run_analytics')):
  File "C:\Users\scott\AppData\Roaming\Python\Python37\site-packages\gs_quant\context_base.py", line 67, in __enter__
    self._on_enter()
  File "C:\Users\scott\AppData\Roaming\Python\Python37\site-packages\gs_quant\session.py", line 81, in _on_enter
    self.init()
  File "C:\Users\scott\AppData\Roaming\Python\Python37\site-packages\gs_quant\session.py", line 92, in init
    self._authenticate()
  File "C:\Users\scott\AppData\Roaming\Python\Python37\site-packages\gs_quant\session.py", line 270, in _authenticate
    raise MqAuthenticationError(reply.status_code, reply.text, context=self.auth_url)
gs_quant.errors.MqAuthenticationError: context: https://idfs.gs.com/as/token.oauth2
status: 400, message: {"error_description":"The requested scope(s) must be blank or a subset of the provided scopes.","error":"invalid_scope"}

Describe the solution you'd like
The message should be - "your application account needs the following scopes X,Y,Z to invoke the APIs you used. Request scopes at URL"

Are you willing to contribute
No

IRSwap does not round trip. IRSwap.from_dict(swap.as_dict()) throws type error

When attempting to round trip an unresolved IRSwap instrument a TypeError is thrown.

The issue is not with IRSwap instrument but the way that the "Base" classes method __from_dict() handles datetime.dates. In 0.8.149 setting a instrument property to a datetime and then attempting to roundtrip via Instrument.from_dict(inst.as_dict()) will fail as Base.__from_dict() cannot handle datetime.date inputs.

To Reproduce
import datetime
from gs_quant.instrument import IRSwap
swap = IRSwap('Pay', termination_date=datetime.date(2030, 1, 2), fixed_rate='atm', notional_currency='eur')
swap.as_dict()
new_swap = IRSwap.from_dict(swap.as_dict())

Expected behavior
new_swap should be a new IRSwap instrument.

This used to work as expected in prior versions, eg 0.8.90.

Screenshots
If applicable, add screenshots to help explain your problem.

Systems setup:

  • OS: Windows 10
  • Python version 3.7
  • GS-Quant version 0.8.149

Additional context
Error is:
File "", line 7, in
IRSwap.from_dict(swap.as_dict())
File "C:\Users\alexa\Anaconda3\lib\site-packages\gs_quant\instrument\core.py", line 55, in from_dict
return cls._from_dict(values)
File "C:\Users\alexa\Anaconda3\lib\site-packages\gs_quant\base.py", line 330, in _from_dict
instance.__from_dict(values)
File "C:\Users\alexa\Anaconda3\lib\site-packages\gs_quant\base.py", line 282, in __from_dict
setattr(self, prop, dateutil.parser.isoparse(prop_value).date())
File "C:\Users\alexa\Anaconda3\lib\site-packages\dateutil\parser\isoparser.py", line 37, in func
return f(self, str_in, *args, **kwargs)
File "C:\Users\alexa\Anaconda3\lib\site-packages\dateutil\parser\isoparser.py", line 134, in isoparse
components, pos = self._parse_isodate(dt_str)
File "C:\Users\alexa\Anaconda3\lib\site-packages\dateutil\parser\isoparser.py", line 208, in _parse_isodate
return self._parse_isodate_common(dt_str)
File "C:\Users\alexa\Anaconda3\lib\site-packages\dateutil\parser\isoparser.py", line 213, in _parse_isodate_common
len_str = len(dt_str)
TypeError: object of type 'datetime.date' has no len()

Issue is around line 280 of gs_quant.base.py:

elif issubclass(prop_type, dt.date):
try:
setattr(self, prop, dateutil.parser.isoparse(prop_value).date())
except ValueError:
if str in additional_types:
setattr(self, prop, prop_value)

suggest the except clause should be extended to include TypeError

This didn't occur in earlier versions of gs_quant as in this case prop_type (line 259) evaluated to None so this section of code ran:
if prop_type is None:
# This shouldn't happen
setattr(self, prop, prop_value)

[BUG] broken link in README

Describe the bug
Broken link for "the developer-site repo" within the gs-quant README file.

To Reproduce
Visit gs-quant/README.md. In the "Overview" section, click the link "the developer-site repo."

Expected behavior
Valid link.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.