{"uuid":"28f65685-6934-4bbe-4aef-47300b1859b4","categoryUuid":"79074c4b-9122-423a-5752-9982e2c5ab9e","data":{"meta":{"name":"Workbench: Backtesting Guide"},"configs":[{"meta":{"refUuid":"9acaeb54-0f19-4f4b-5673-d0fd8735e9d2"},"uuid":"45931354-b5dd-4c7e-8196-62df3088b961","configType":"workbench"},{"meta":{"refUuid":"72a4a0cc-3187-4bf6-70ce-ef8cd9a85688"},"uuid":"35d49f58-e526-4188-88bd-1a04e6a73ed9","extra":{"zoom":"5y"},"configType":"workbench"},{"meta":{"refUuid":"b223538b-9e72-4ff3-49e0-986a671d0902"},"uuid":"79923412-2e7e-473a-acbf-47ad8cb5c092","configType":"workbench"},{"meta":{"refUuid":"99ccd8ad-bc24-46e1-5411-39ca565fc4eb"},"uuid":"7a00b61b-17b4-4824-99f8-a731e8324f3e","configType":"workbench"},{"meta":{"refUuid":"d5828137-25e3-42fd-6325-fa9ba94d347b"},"uuid":"284bf50a-0cae-48c8-89ac-cdcddb016331","configType":"workbench"},{"meta":{"refUuid":"a594381e-0bf7-47fc-5948-d156a847140e"},"uuid":"d10f04ea-2577-4ccd-8f8c-1917cc8b25d0","configType":"workbench"},{"meta":{"refUuid":"6326daaa-e123-43a3-4828-ffc810436f8e"},"uuid":"25028038-6814-465a-83d9-3a5c3077604d","configType":"workbench"},{"meta":{"content":"## 📈 Backtesting Portfolio Strategies in Workbench 📉\n---\n\nThis article introduces a backtesting suite for our [Workbench tool](https://studio.glassnode.com/workbench). Workbench lets users quickly compare existing metrics, apply formulas, and derive new custom metrics. We will demonstrate how trading strategies can be defined within Workbench and how a simulation of such self-defined trading strategies on historical data can be run. \n\nIf you would like to learn the basics of Workbench, please refer to our [Workbench Video Tutorial](https://www.youtube.com/watch?v=bJs1PQslhRc) and the written [Workbench Guide](https://docs.glassnode.com/guides-and-tutorials/workbench-guide) to help you get started.\n\n### Introduction\n\nThe backtesting function is designed to model trading strategies and portfolio performance for a given initial capital size. The `backtest` function returns the NAV of a portfolio invested in Bitcoin, of size `initial_capital_usd`, starting at date `since`, and with `rel_trading_costs` in fees per trade.\n\n`backtest(m1, f1, since, initial_capital_usd, rel_trading_costs)`\n\nWhere the input parameters are:\n\n- `m1`: the price series (e.g., BTC) you want to trade.\n\n- `f1 = input_signal`: the trading signal which represents a gearing ratio applied to the percent change performance of price `m1`.\n\n- `since`: a timestamp indicating the start date of your trading simulation, e.g., `\"2020-01-01\"`\n\n- `initial_capital_usd`: USD denominated capital allocated to the strategy. No additional capital flows in or out of the simulated trading portfolio. The trading simulation will vary the exposure of the starting capital to the traded asset over time, depending on the magnitude of the trading signal.\n\n- `rel_trading_costs`: an approximation of the expected relative trading costs, which consist of exchange fees and slippage. A value of, e.g., `0.001` refers to trading costs of 0.1% of the volume of each trade.\n\n\n\n"},"uuid":"6ddd599b-f161-4142-b010-189e2ebe1d9b","extra":{"backgroundColor":"#ffffff"},"configType":"content"},{"meta":{"content":"## Example 3: Combining Multiple Trading Strategies\n---\n\nFor the last backtest example, we look at combining the SMA Cross-over, and the SOPR trading strategies into one overarching strategy. \n\nIn the Workbench example, we have defined the SMA cross-over signal as `f2` and the SOPR trading signal as `f3`; then, we can run a combined backtest:\n\n```jsx\nbacktest(m1, (f2+f3)/2, \"2020-01-01\", 1000, 0.001)\n```\n\nNote how we average the two individual trading signals as input to this backtest. This indicates that the $1000 initial capital will have the following exposure to price performance:\n\n- 0% when neither signal is triggered (in cash)\n- 50% exposure when one signal is triggered.\n- 100% exposure when both signals are triggered.\n\nIn the following, we plot the two individual backtests of SMA cross-over and SOPR, labeled as:\n\n- 🔵 **SMA cross-over backtest [USD]**\n- 🟠 **SOPR backtest [USD]**\n- 🔴 **Combined backtest [USD]**\n\nNote how the signal bars *Combined strategy signal* now not only takes values 0 and 1 but 0.5 in cases where the two strategy components’ signals do not coincide, representing a position of $1000 invested, but with 0.5x leverage."},"uuid":"0b3c562c-d654-4d3e-a0e4-2a46894648a8","extra":{"backgroundColor":"#ffffff"},"configType":"content"},{"meta":{"content":"## Example 0: HODL Strategy\n---\n\nWe will start with the most basic trading strategy of Buy and Hold. Imagine you had a lump sum of cash you wanted to invest in bitcoin. The most straightforward way is to buy bitcoin and never sell. This will serve as a baseline to compare with other strategies. \n\nThe parameters for this strategy are as follows:\n\n- `m1`: BTC Price\n- `f1`: Input Signal of 1.0 (defined as `m1/m1`)\n- `since`: Starting out strategy on `\"2020-01-01\"`\n- `initial_capital`: $1,000\n- `rel_trading-cost`: 0.1%\n\n```jsx\nf2 = backtest(m1, f1, \"2020-01-01\", 1000, 0.001)\n```\n\nOutput formula `f2` is labeled *HODL backtest [USD]* and returns the portfolio NAV of $1000 exposure starting on 1-Jan-2020."},"uuid":"dbe47063-7aff-4a9f-b655-f1302cbdbb99","extra":{"backgroundColor":"#ffffff"},"configType":"content"},{"meta":{"content":"### Example 1a: Simple Moving Average Cross-Over\n---\n\nThe Simple Moving Average (SMA) cross-over is our first example of a systematic trading strategy. This popular trend-following indicator consists of two SMAs (20-days and 50-day) with the following trading rules:\n\n1. If 20D-SMA is above 50D-SMA: `input_signal` = 1\n1. If 20D-SMA is below 50D-SMA: `input_signal` = 0\n\nPurely for the sake of illustration, let’s quickly look at a Python-style pseudo-code to formalize these trading rules:\n\n```python\n# SMA cross-over trading rules:\nif sma20 \u003e sma50:\n    signal = 1\nelse:\n    signal = 0\n```\n\nThe Workbench preset on the right-hand side shows a plot of the BTC price (bars) and the two simple moving averages (SMA20 and SMA50). Our set of trading rules determines the color of the signal bar. When the trading signal is one, the color is green; when the signal is zero, the color is red.\n\nWe will use the Workbench `if` conditional (see the [Workbench Guide](https://academy.glassnode.com/introduction-to-glassnode/workbench-guide) for details). In line with the previously defined trading rules, we define the trading signal `f3` as:\n\n```jsx\nif(sma(m1, 20), \"\u003e\", sma(m1, 50), 1, 0)\n```\n\nNow that we have defined the trading signal, we can perform a backtest on the SMA cross-over trading strategy\n\n```jsx\nbacktest(m1, f3, \"2020-01-01\", 1000, 0.001)\n```\n\nThis directly output the NAV curve of the SMA cross-over trading strategy, labeled in the Workbench preset as *SMA cross-over backtest* 🔵. The previously introduced *HODL backtest* 🟠 is included for comparison."},"uuid":"e1ba5a0e-a58e-453c-ac01-fa519a3ed23e","extra":{"backgroundColor":"#ffffff"},"configType":"content"},{"meta":{"content":"## SOPR Strategies\n---\n\n[The Spent Output Profit Ratio (SOPR)](https://studio.glassnode.com/metrics?a=BTC\u0026category=\u0026m=indicators.Sopr), for example, is computed by dividing the realized value (in USD) divided by the value at creation (USD) of a spent output. Or simply: price sold / price paid.\n\nThe value of SOPR informs us whether the average investor sells at a profit (SOPR \u003e 1) or a loss (SOPR \u003c 1). One might deduce that an environment where the average investor sells at a profit is preferable for holding bitcoin compared to when the average investor sells at a loss. Therefore, we define the SOPR-based trading hypothesis (in Python-style pseudo-code for illustration) as:\n\n```python\n# SOPR-based trading rules:\nif sopr \u003e 1:\n    signal = 1\nelse:\n    signal = 0\n```\n\nThe SOPR signal is quite noisy; thus, we smooth it with a 7-day exponential moving average (EMA), and use the `if` function to formalize the SOPR trading rules in Workbench syntax:\n\n```jsx\nif(m2, \"\u003e\", 1, 1, 0)\n```\n\nThis is our trading signal `f3`. We want to be long bitcoin (`signal = 1`) when SOPR (`m2`) is larger than one and otherwise zero. The NAV curve of the backtest can then be calculated as follows:\n\n```jsx\nbacktest(m1, f3, \"2020-01-01\", 1000, 0.001)\n```\n\n### Example 2b - Backtesting short positions\n\nThe backtesting suite also allows for simulation of short positions, established by setting a negative signal value. In this second example, a second signal is generated for periods where SOPR falls below 1.0, indicating holders are realizing losses.\n\n```jsx\nif(m2, \"\u003c\", 1, -1, 0)\n```\n\nWe now have two signals, reflecting the conditions of long (`f3 = 1` when SOPR \u003e 1) and short (`f4 = -1` when SOPR \u003c 1). Thus we can backtest a long / short SOPR strategy:\n\n```jsx\nbacktest(m1, f3+f4, \"2020-06-01\", 1000, 0.001)\n```\n\nThis example chart presents the backtest results for 🟠 HODL, 🔵 Long Only, and 🔴 Long+Short strategies."},"uuid":"3a803dae-eee4-42ec-a2ab-caaedd3b39dc","extra":{"backgroundColor":"#ffffff"},"configType":"content"},{"meta":{"content":"## Modelling Portfolio Performance\n---\n\nThe NAV curve calculated via the `backtest` function offers an overview of how an investment would have performed over time. But it does not take the risk of a strategy into account, and neither can one directly read off the relative performance over different periods.\n\nTherefore, we introduce the following companion set of functions, which will allow you to get a deeper understanding of your backtest’s performance and allow you to compare different backtests quantitatively:\n\n1. `drawdown(m1)`\nThis function takes a backtest result, or any time series, as input and returns the relative drawdown from the all-time high for each point in time. This is analog to the [Bitcoin: Price Drawdown from ATH](https://studio.glassnode.com/metrics?a=BTC\u0026category=\u0026ema=0\u0026m=market.PriceDrawdownRelative) metric.\n\n2. `mean_return(m1, period)`\nThe annualized rolling mean return of a time series over a given period (in days).\n\n3. `realized_vol(m1, period)`\nThe annualized rolling realized volatility of a time series over a given period (in days). See also [Annualized Realized Volatility](https://studio.glassnode.com/metrics?a=BTC\u0026category=\u0026ema=0\u0026m=market.RealizedVolatility1Week).\n\n4. `sharpe_ratio(m1, period)`\nThe annualized rolling Sharpe ratio is the ratio of annualized rolling mean return and annualized rolling realized volatility. It is one of the most widely used methods for measuring risk-adjusted relative returns.\n\nNote that all functions of the backtesting suite are described in the [Workbench Guide](https://academy.glassnode.com/introduction-to-glassnode/workbench-guide), too. In Workbench preset on the right-hand side, we showcase the `drawdown` and `realized_vol` functions. We compare the depth of the drawdowns from the HODL strategy (example 0) to the SOPR strategy (example 2). Moreover, we compare the annualized realized volatility of the two strategies. We find that the SOPR strategy has significantly reduced drawdowns compared to a plain HODL investment, and the realized volatility is also highly reduced on a one-year rolling basis."},"uuid":"d0b0ef1d-b3fb-479a-80f1-52d43c1cbd3d","extra":{"backgroundColor":"#ffffff"},"configType":"content"},{"meta":{"content":"### Disclaimer\n---\n\nThe content of this article, dashboard and the introduced backtesting functions are for informational purposes only, you should not construe any such information or other material as legal, tax, investment, financial, or other advice. Past performance is fictional, for illustrative purposes only and no indication of future performance.\n\nThe introduced functions and features are in a free beta version state and are subject for change.\n\n*This dashboard and associated content does not provide any investment advice. All data is provided for information purposes only. No investment decision shall be based on the information provided here and you are solely responsible for your own investment decisions. For for details, please refer to our [Terms and Conditions](https://studio.glassnode.com/terms-and-conditions).*"},"uuid":"0d71528b-0e3c-466a-8e58-81e38df48e91","extra":{"backgroundColor":"#ffffff"},"configType":"content"},{"meta":{"refUuid":"d6239b70-8c66-4ae6-419b-d660cc253802"},"uuid":"835362f9-438b-4289-adb1-4b38445c34d6","configType":"workbench"},{"meta":{"content":"## Backtesting Template\n---\n\nThis template is available with several of the input functions setup as parameters, including the threshold for SOPR as an example metric.\n\nAnalysts can change the SOPR metric, and the definition of the input signal to get started building new custom strategies."},"uuid":"dbf4d533-2146-4a71-a701-0118d20c56ac","extra":{"backgroundColor":"#ffffff"},"configType":"content"}],"layouts":[{"h":6,"i":"45931354-b5dd-4c7e-8196-62df3088b961","w":7,"x":5,"y":6,"minH":1,"minW":3,"moved":false,"static":false},{"h":6,"i":"35d49f58-e526-4188-88bd-1a04e6a73ed9","w":7,"x":5,"y":12,"minH":1,"minW":3,"moved":false,"static":false},{"h":6,"i":"79923412-2e7e-473a-acbf-47ad8cb5c092","w":7,"x":5,"y":18,"minH":1,"minW":3,"moved":false,"static":false},{"h":6,"i":"7a00b61b-17b4-4824-99f8-a731e8324f3e","w":7,"x":5,"y":24,"minH":1,"minW":3,"moved":false,"static":false},{"h":8,"i":"284bf50a-0cae-48c8-89ac-cdcddb016331","w":7,"x":5,"y":36,"minH":1,"minW":3,"moved":false,"static":false},{"h":6,"i":"d10f04ea-2577-4ccd-8f8c-1917cc8b25d0","w":7,"x":5,"y":30,"minH":1,"minW":3,"moved":false,"static":false},{"h":8,"i":"25028038-6814-465a-83d9-3a5c3077604d","w":7,"x":5,"y":44,"minH":1,"minW":3,"moved":false,"static":false},{"h":6,"i":"6ddd599b-f161-4142-b010-189e2ebe1d9b","w":12,"x":0,"y":0,"minH":1,"minW":3,"moved":false,"static":false},{"h":8,"i":"0b3c562c-d654-4d3e-a0e4-2a46894648a8","w":5,"x":0,"y":36,"minH":1,"minW":3,"moved":false,"static":false},{"h":6,"i":"dbe47063-7aff-4a9f-b655-f1302cbdbb99","w":5,"x":0,"y":6,"minH":1,"minW":3,"moved":false,"static":false},{"h":12,"i":"e1ba5a0e-a58e-453c-ac01-fa519a3ed23e","w":5,"x":0,"y":12,"minH":1,"minW":3,"moved":false,"static":false},{"h":12,"i":"3a803dae-eee4-42ec-a2ab-caaedd3b39dc","w":5,"x":0,"y":24,"minH":1,"minW":3,"moved":false,"static":false},{"h":8,"i":"d0b0ef1d-b3fb-479a-80f1-52d43c1cbd3d","w":5,"x":0,"y":44,"minH":1,"minW":3,"moved":false,"static":false},{"h":6,"i":"0d71528b-0e3c-466a-8e58-81e38df48e91","w":12,"x":0,"y":58,"minH":1,"minW":3,"moved":false,"static":false},{"h":6,"i":"835362f9-438b-4289-adb1-4b38445c34d6","w":7,"x":5,"y":52,"minH":1,"minW":3,"moved":false,"static":false},{"h":6,"i":"dbf4d533-2146-4a71-a701-0118d20c56ac","w":5,"x":0,"y":52,"minH":1,"minW":3,"moved":false,"static":false}]}}
