Completed
Push — master ( a28236...71395d )
by
unknown
01:27
created

zipline.examples.analyze()   A

Complexity

Conditions 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 13
rs 9.4286
1
#!/usr/bin/env python
2
#
3
# Copyright 2013 Quantopian, Inc.
4
#
5
# Licensed under the Apache License, Version 2.0 (the "License");
6
# you may not use this file except in compliance with the License.
7
# You may obtain a copy of the License at
8
#
9
#     http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
16
17
import logbook
18
import matplotlib.pyplot as plt
19
import numpy as np
20
import statsmodels.api as sm
21
from datetime import datetime
22
import pytz
23
24
from zipline.algorithm import TradingAlgorithm
25
from zipline.transforms import batch_transform
26
from zipline.utils.factory import load_from_yahoo
27
from zipline.api import symbol
28
29
30
@batch_transform
31
def ols_transform(data, sid1, sid2):
32
    """Computes regression coefficient (slope and intercept)
33
    via Ordinary Least Squares between two SIDs.
34
    """
35
    p0 = data.price[sid1]
36
    p1 = sm.add_constant(data.price[sid2], prepend=True)
37
    slope, intercept = sm.OLS(p0, p1).fit().params
38
39
    return slope, intercept
40
41
42
class Pairtrade(TradingAlgorithm):
43
    """Pairtrading relies on cointegration of two stocks.
44
45
    The expectation is that once the two stocks drifted apart
46
    (i.e. there is spread), they will eventually revert again. Thus,
47
    if we short the upward drifting stock and long the downward
48
    drifting stock (in short, we buy the spread) once the spread
49
    widened we can sell the spread with profit once they converged
50
    again. A nice property of this algorithm is that we enter the
51
    market in a neutral position.
52
53
    This specific algorithm tries to exploit the cointegration of
54
    Pepsi and Coca Cola by estimating the correlation between the
55
    two. Divergence of the spread is evaluated by z-scoring.
56
    """
57
58
    def initialize(self, window_length=100):
59
        self.spreads = []
60
        self.invested = 0
61
        self.window_length = window_length
62
        self.ols_transform = ols_transform(refresh_period=self.window_length,
63
                                           window_length=self.window_length)
64
        self.PEP = self.symbol('PEP')
65
        self.KO = self.symbol('KO')
66
67
    def handle_data(self, data):
68
        ######################################################
69
        # 1. Compute regression coefficients between PEP and KO
70
        params = self.ols_transform.handle_data(data, self.PEP, self.KO)
71
        if params is None:
72
            return
73
        intercept, slope = params
74
75
        ######################################################
76
        # 2. Compute spread and zscore
77
        zscore = self.compute_zscore(data, slope, intercept)
78
        self.record(zscores=zscore,
79
                    PEP=data[symbol('PEP')].price,
80
                    KO=data[symbol('KO')].price)
81
82
        ######################################################
83
        # 3. Place orders
84
        self.place_orders(data, zscore)
85
86
    def compute_zscore(self, data, slope, intercept):
87
        """1. Compute the spread given slope and intercept.
88
           2. zscore the spread.
89
        """
90
        spread = (data[self.PEP].price -
91
                  (slope * data[self.KO].price + intercept))
92
        self.spreads.append(spread)
93
        spread_wind = self.spreads[-self.window_length:]
94
        zscore = (spread - np.mean(spread_wind)) / np.std(spread_wind)
95
        return zscore
96
97
    def place_orders(self, data, zscore):
98
        """Buy spread if zscore is > 2, sell if zscore < .5.
99
        """
100
        if zscore >= 2.0 and not self.invested:
101
            self.order(self.PEP, int(100 / data[self.PEP].price))
102
            self.order(self.KO, -int(100 / data[self.KO].price))
103
            self.invested = True
104
        elif zscore <= -2.0 and not self.invested:
105
            self.order(self.PEP, -int(100 / data[self.PEP].price))
106
            self.order(self.KO, int(100 / data[self.KO].price))
107
            self.invested = True
108
        elif abs(zscore) < .5 and self.invested:
109
            self.sell_spread()
110
            self.invested = False
111
112
    def sell_spread(self):
113
        """
114
        decrease exposure, regardless of position long/short.
115
        buy for a short position, sell for a long.
116
        """
117
        ko_amount = self.portfolio.positions[self.KO].amount
118
        self.order(self.KO, -1 * ko_amount)
119
        pep_amount = self.portfolio.positions[self.PEP].amount
120
        self.order(self.PEP, -1 * pep_amount)
121
122
123
# Note: this function can be removed if running
124
# this algorithm on quantopian.com
125
def analyze(context=None, results=None):
126
    ax1 = plt.subplot(211)
127
    plt.title('PepsiCo & Coca-Cola Co. share prices')
128
    results[['PEP', 'KO']].plot(ax=ax1)
129
    plt.ylabel('Price (USD)')
130
    plt.setp(ax1.get_xticklabels(), visible=False)
131
132
    ax2 = plt.subplot(212, sharex=ax1)
133
    results.zscores.plot(ax=ax2, color='r')
134
    plt.ylabel('Z-scored spread')
135
136
    plt.gcf().set_size_inches(18, 8)
137
    plt.show()
138
139
140
# Note: this if-block should be removed if running
141
# this algorithm on quantopian.com
142
if __name__ == '__main__':
143
    logbook.StderrHandler().push_application()
144
145
    # Set the simulation start and end dates.
146
    start = datetime(2000, 1, 1, 0, 0, 0, 0, pytz.utc)
147
    end = datetime(2002, 1, 1, 0, 0, 0, 0, pytz.utc)
148
149
    # Load price data from yahoo.
150
    data = load_from_yahoo(stocks=['PEP', 'KO'], indexes={},
151
                           start=start, end=end)
152
153
    # Create and run the algorithm.
154
    pairtrade = Pairtrade()
155
    results = pairtrade.run(data)
156
157
    # Plot the portfolio data.
158
    analyze(results=results)
159