.. _user.pairs: ============================================ Creating An Automated Pairs Trading Strategy ============================================ Motivation and Context ~~~~~~~~~~~~~~~~~~~~~~ There are circumstances where a single script requires access to multiple instruments and multiple charts. Some examples where this requirement comes up include: 1. Running some strategy on multiple markets 2. Managing a portfolio of strategies where each strategy is on its own market 3. Strategies that rely on a relationship between instruments 4. Strategies that require multiple bar timeframes There are other examples but the common theme amongst them is the need for a single script that works with multiple instruments or multiple charts. Working with multiple instruments raises various challenges and questions: 1. How to setup the charts in a way that is efficient, useful and makes our programming easy and reliable? 2. How to access data? Do we use async, sync? 3. Should we work with each instrument separately or merge it? 4. How to handle multiple timeframes? This can be different vs. multiple instruments on a single timeframe 5. How do we submit orders to multiple instruments? How do track positions for each instrument? This is broad subject with many nuances, this tutorial will cover some of them. Learning objectives ~~~~~~~~~~~~~~~~~~~ - Working with multiple charts on a single timeframe - Accessing more than one chart in a single script where all the charts are on the same timeframe - Submitting orders to multiple charts - Trading strategy implementation flow - we demonstrate a useful blueprint that many trading strategy implementations follow - Implement a simple and basic pairs trading strategy Overview of The Pairs Trading Strategy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pais trading is a simple mean reverting strategy. At a high level it follows the following steps: 1. Find two assets A and B that tend to move together. Examples of this are Ford and GM, ES and NQ, etc. 2. Monitor for situations where they diverge from one another 3. When that happens, we sell one asset and buy the other 4. Wait for the assets to come back together and exit In more technical terms, for this turotial we will be trading futures pairs on a 1H chart. Firstly we calculate a measure of spread, the ratio :math:`S=\frac AB`. We then calculate the 15-bar MA and standard deviation of the spread and get the z-score :math:`z_S`. With that we can generate our entry signals. If the z-score of the spread crosses above :math:`2`, we have that our spread is exceptionally high, and thus we can bet that the price of A will go down and/or the price of B will go up. **We call this shorting the spread**. Similarly, if :math:`z_S` crosses below :math:`-2` we expect the price of A to go up and/or the price of B to go down. **We call this longing the spread**. We will use fixed sizing for entering the markets, and exiting will be done once :math:`z_S` crosses :math:`0` or when a trade has gone on for longer than 7 bars by simply flattening our position. Now that we understand the general logic behind the strategy we can start thinking about how to design the code on a high level. Preparing the Charts ~~~~~~~~~~~~~~~~~~~~ For this example, let our asset A be ES and our asset B be NQ. The first design consideration we have to make is when we are going to merge the hourly data from asset A and asset B. We have 2 options: 1. Merge them in Sierra Chart using the ``Add Additional Symbol`` study. 2. Merge them in python by setting up two :func:`~trade29.sc.SCBridge.subscribe_to_chart_data` requests. Each option has its own limitations and strength. The main consideration is the alignment of the data. In general Sierra Chart is really good at that and there shouldnt be any alignment problems with the ``Add Additional Symbol`` study in that respect. The drawback however is that ``Add Additional Symbol`` will force the additional symbol to be the same timeframe. Since we want to work in the same timeframe anyways for both assets, we will utilize the supreme alignment of merging the data in Sierra Charts with the ``Add Additional Symbol`` study. Thus setup two hourly charts, one for asset A (ES) and one for asset B (NQ). Add the SC-dX custom study to both, setting the key for A to ``A`` and the key for B to ``B``. .. thumbnail:: media/pairs/figure0.png .. admonition:: Why do we open two charts? We still open two charts even though we are only fetching data from one. This is so we can trade both, as the :func:`~trade29.sc.SCBridge` does not yet have a method for trading an arbitrary symbol. Next add the ``Add Additional Symbol`` study to chart A, filling in the symbol parameter as symbol B. **Take note of the ID of the study for later**. .. thumbnail:: media/pairs/figure1.png Implementing The Strategy ~~~~~~~~~~~~~~~~~~~~~~~~~ In general almost all automated trading strategies can be broken down into 4 parts: 1. Prepare the data (prices, position size) 2. Calculate the indicator(s) (spread) 3. Generate the signals (entry/exit signals outlined above) 4. Trade the signals Thus we start with the template file: .. literalinclude:: examples/pairs/version0-0.py :caption: strategy_template.py :linenos: :lineno-match: Prepare The Data ---------------- We want to trade on bar close, thus we use the handy async :func:`~trade29.sc.SCBridge.subscribe_to_chart_data` method to serve us data on bar close. Make sure you remember the ID of the ``Add Additional Symbol`` study for the constant! .. literalinclude:: examples/pairs/version1-0.py :caption: pairs_version1.py :linenos: :lineno-match: Running this script, we can see the prices for asset A in the last 15 bars followed by the prices for asset B in the last 15 bars for every new bar that comes in. Now we need to decide how we will fetch position data. We can either fetch position data asynchonously through the response queue like we do price data, or on demand through the :func:`~trade29.sc.SCBridge.get_position_status` method. Doing it asynchonously introduces the complexity of filtering the response queue and keeping track of our position. Getting it on demand takes a little more time when running, but ensures it is up to date and immediate, and since we are trading on bar close for very long hourly bars, that isn't really a problem. Therefore we choose to fetch position data synchonously upon recieving new data: .. literalinclude:: examples/pairs/version1-1.py :caption: pairs_version1.py :linenos: :lineno-match: :start-after: S0 :emphasize-lines: 7 Calculate The Indicator(s) -------------------------- With both of these sets of prices in Pandas series, we can quickly calculate the spreads and their 15-bar z-score: .. literalinclude:: examples/pairs/version2-0.py :caption: pairs_version2.py :linenos: :lineno-match: :start-after: S0 Like the previous script, we can run this one and it will print the z-score of the spread each time a new bar arrives. Generate The Signals -------------------- We sketch 2 functions for generating entry and exit signals on a crossover: .. literalinclude:: examples/pairs/version3-0.py :caption: pairs_version3.py :linenos: :lineno-match: :end-before: S0 We see now that we must keep track of the last-zscore and also implement the timeout signal: .. literalinclude:: examples/pairs/version3-0.py :caption: pairs_version3.py :linenos: :lineno-match: :start-after: S1 :emphasize-lines: 3, 5, 26, 28, 30 Now we can implement the crossover signal functions: .. literalinclude:: examples/pairs/version3-1.py :caption: pairs_version3.py :linenos: :lineno-match: :end-before: S0 Trade The Signals ----------------- First let's sketch the logic for entering and exiting: .. literalinclude:: examples/pairs/version4-0.py :caption: pairs_version4.py :linenos: :lineno-match: :start-after: S1 Simple enough. Now all that's left is to add the buy/sell orders using :func:`~trade29.sc.SCBridge.submit_order` and the flattening using :func:`~trade29.sc.SCBridge.flatten_and_cancel`. Note that when you set the ``is_buy`` parameter for :func:`~trade29.sc.SCBridge.submit_order` to ``False`` it will short. Since we have our two charts and SC-dX studies open, we can simply pass the key ``A`` or ``B`` to trade on the corresponding symbol. .. literalinclude:: examples/pairs/version4-1.py :caption: pairs_version4.py :linenos: :lineno-match: :start-after: S1 Conclusion ~~~~~~~~~~ And thus we are done our pairs trading strategy! Congratulations, you have started with a strategy specification, merged data from two charts in the same timeframe, traded said charts, learned a useful implementation flow that you can hopefully apply to your own endeavors in the future, and of course you now have a basic pairs trading strategy which is given in full below to check against. .. literalinclude:: examples/pairs/version4-2.py :caption: pairs_final.py :linenos: :lineno-match: