{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Examples require an initialized GsSession and relevant entitlements. External clients need to substitute thier own client id and client secret below. Please refer to [Sessions](https://developer.gs.com/docs/gsquant/Authentication/gs-session/) for details." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from gs_quant.session import GsSession\n", "GsSession.use(client_id=None, client_secret=None, scopes=('run_analytics',))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Backtesting\n", "\n", "The backtesting framework is a language for describing backtests that's (1) intuitive (2) cross-asset and instrument agnostic (3) available natively in python for users to change and extend. It's compatible with a variety of calculation engines - eliminating need for specific syntax knowledge - and uses the gs risk API as the default calculation engine.\n", "\n", "The language consists of several basic components:\n", "* [Trigger](#Trigger)\n", "* [Action](#Action)\n", "* [Strategy](#Strategy)\n", "* [Calculation Engine](#Calculation-Engine)\n", "* [Backtest](#Backtest)\n", "\n", "In this tutorial, we will examine the components in the context of looking at a basic vol selling strategy where we sell a 1m10y USD straddle daily." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Trigger\n", "\n", "A trigger can be used to amend a strategy based on a set of requirements and the current state of the backtest. Each trigger has an associated action or set of actions that are applied when the trigger is active. A backtest can have multiple triggers.\n", "\n", "Below are the triggers we currently offer:\n", "* `PeriodicTrigger`: triggers at a regular interval (time/date). e.g.: every month end\n", "* `MktTrigger`: triggers when market data, absolute or change, hits some target e.g. whenever SPX 1m vol crosses 10. Check out our [data catalog](https://marquee.gs.com/s/discover/data-services/catalog) for sources of market data that can be leveraged here\n", "* `StrategyRiskTrigger`: triggers when the current risk of the strategy hits some target e.g. delta crosses $10m\n", "\n", "Since in our example, we are selling an instrument periodically e.g. (daily), we will use a single `PeriodicTrigger.` Let's define the `PeriodicTriggerRequirements` first and then talk about actions, since we need to specify at least a single action to happen when this trigger is triggered." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from gs_quant.backtests.triggers import PeriodicTrigger, PeriodicTriggerRequirements\n", "from datetime import date\n", "\n", "start_date, end_date = date(2020, 1, 1), date(2020, 12, 1)\n", "# define dates on which actions will be triggered (1b=every business day here)\n", "trig_req = PeriodicTriggerRequirements(start_date=start_date, end_date=end_date, frequency='1b')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Action\n", "\n", "Actions define how the portfolio is updated when a trigger is triggered. Below are the actions we currently offer:\n", "* `AddTradeAction`: adds trade to a strategy\n", "* `ExitTradeAction`: removes trade from a strategy\n", "* `HedgeAction`: add trade that's a hedge for a risk\n", "\n", "Since in our example we are selling a straddle, we only want a single `AddTradeAction` to supply to our trigger. Let's define it." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from gs_quant.backtests.actions import AddTradeAction\n", "from gs_quant.common import PayReceive, Currency\n", "from gs_quant.instrument import IRSwaption\n", "\n", "# straddle is the position we'll be adding (note it's a short position since buy_sell='Sell')\n", "straddle = IRSwaption(PayReceive.Straddle, '10y', Currency.USD, expiration_date='1m', notional_amount=1e8, buy_sell='Sell')\n", "\n", "# this action specifies we will add the straddle when this action is triggered and hold it until expiration_date\n", "action = AddTradeAction(straddle, 'expiration_date')\n", "\n", "# we will now combine our trigger requirement and action to produce a PeriodicTrigger\n", "# Note, action can be a list if there are multiple actions\n", "trigger = PeriodicTrigger(trig_req, action)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Strategy\n", "\n", "A `Strategy` combines all the information needed to run a backtest. It has 2 components: an initial trade or portfolio and a trigger or set of triggers. \n", "\n", "In our example, we don't have a starting portfolio and we have a single trigger we defined in the previous step. Let's put these together." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from gs_quant.backtests.strategy import Strategy\n", "\n", "# in this case we have a single trigger but this can be a list of triggers as well\n", "strategy = Strategy(None, trigger)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Calculation engine\n", "\n", "The final piece is calculate engine - the underlying engine/source of calculations you want to run your backtest. \n", "\n", "Once you have defined your backtest in the steps above, you can view which available engines support your strategy. There may be multiple as different engines have been developed and optimized for certain types of backtesting. For example, the `EquityVolEngine` is optimized for fast volatility backtesting for a specific set of equity underlyers. That said, `GenericEngine` will support majority of use cases, running calculations using gs-quant's risk apis. You can check which available engines support your strategy using the function below:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "strategy.get_available_engines()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, you check whether a particular engine supports your strategy:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from gs_quant.backtests.generic_engine import GenericEngine\n", "\n", "ge = GenericEngine()\n", "ge.supports_strategy(strategy)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As a final step, let's put everything together to run the backtest. The frequency here indicates the frequency of calculations. You can also supply additional risks you may want to calculate as part of your backtest (greeks or dollar price, for example)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "backtest = ge.run_backtest(strategy, start=start_date, end=end_date, frequency='1b', show_progress=True)\n", "backtest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Backtest\n", "\n", "* to view the results of `backtest.risks` (by default, `Price`) (all in local ccy) and the cumulative cashflows evaluated on each backtested date: `backtest.results_summary`\n", "* to view status for each trade: `backtest.trade_ledger()`\n", "* to view and further evaluate the portfolio on any given backtested date: `backtest.portfolio_dict`\n", "* to view the number of total api calls and number of calculations performed: `backtest.calc_calls` and `backtest.calculations`\n", "\n", "Here we'll use `backtest.results_summary` to plot mark to market and performance of the strategy" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "backtest.trade_ledger()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "backtest.result_summary" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "from gs_quant.risk import Price\n", "pd.DataFrame({'Generic backtester': backtest.result_summary[Price]}).plot(figsize=(10, 6), title='Mark to market')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pd.DataFrame({'Generic backtester': backtest.result_summary['Cumulative Cash'] + backtest.result_summary[Price]}).plot(figsize=(10, 6), title='Performance')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you have feedback on further expanding this framework, please reach out to `gs-quant-dev@gs.com`" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.4" } }, "nbformat": 4, "nbformat_minor": 4 }