1#!/usr/bin/env python
2# -*- coding: utf-8; py-indent-offset:4 -*-
3###############################################################################
4#
5# Copyright (C) 2017 Daniel Rodriguez
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19#
20###############################################################################
21from __future__ import (absolute_import, division, print_function,
22                        unicode_literals)
23
24import argparse
25import datetime
26
27import backtrader as bt
28
29
30class NYSE_2016(bt.TradingCalendar):
31    params = dict(
32        holidays=[
33            datetime.date(2016, 1, 1),
34            datetime.date(2016, 1, 18),
35            datetime.date(2016, 2, 15),
36            datetime.date(2016, 3, 25),
37            datetime.date(2016, 5, 30),
38            datetime.date(2016, 7, 4),
39            datetime.date(2016, 9, 5),
40            datetime.date(2016, 11, 24),
41            datetime.date(2016, 12, 26),
42        ]
43    )
44
45
46class St(bt.Strategy):
47    params = dict(
48    )
49
50    def __init__(self):
51        pass
52
53    def start(self):
54        self.t0 = datetime.datetime.utcnow()
55
56    def stop(self):
57        t1 = datetime.datetime.utcnow()
58        print('Duration:', t1 - self.t0)
59
60    def prenext(self):
61        self.next()
62
63    def next(self):
64        print('Strategy len {} datetime {}'.format(
65            len(self), self.datetime.date()), end=' ')
66
67        print('Data0 len {} datetime {}'.format(
68            len(self.data0), self.data0.datetime.date()), end=' ')
69
70        if len(self.data1):
71            print('Data1 len {} datetime {}'.format(
72                len(self.data1), self.data1.datetime.date()))
73        else:
74            print()
75
76
77def runstrat(args=None):
78    args = parse_args(args)
79
80    cerebro = bt.Cerebro()
81
82    # Data feed kwargs
83    kwargs = dict()
84
85    # Parse from/to-date
86    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
87    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
88        if a:
89            strpfmt = dtfmt + tmfmt * ('T' in a)
90            kwargs[d] = datetime.datetime.strptime(a, strpfmt)
91
92    YahooData = bt.feeds.YahooFinanceData
93    if args.offline:
94        YahooData = bt.feeds.YahooFinanceCSVData  # change to read file
95
96    # Data feed
97    data0 = YahooData(dataname=args.data0, **kwargs)
98    cerebro.adddata(data0)
99
100    d1 = cerebro.resampledata(data0,
101                              timeframe=getattr(bt.TimeFrame, args.timeframe))
102    d1.plotinfo.plotmaster = data0
103    d1.plotinfo.sameaxis = True
104
105    if args.pandascal:
106        cerebro.addcalendar(args.pandascal)
107    elif args.owncal:
108        cerebro.addcalendar(NYSE_2016)
109
110    # Broker
111    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))
112
113    # Sizer
114    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))
115
116    # Strategy
117    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))
118
119    # Execute
120    cerebro.run(**eval('dict(' + args.cerebro + ')'))
121
122    if args.plot:  # Plot if requested to
123        cerebro.plot(**eval('dict(' + args.plot + ')'))
124
125
126def parse_args(pargs=None):
127    parser = argparse.ArgumentParser(
128        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
129        description=(
130            'Trading Calendar Sample'
131        )
132    )
133
134    parser.add_argument('--data0', default='YHOO',
135                        required=False, help='Data to read in')
136
137    parser.add_argument('--offline', required=False, action='store_true',
138                        help='Read from disk with same name as ticker')
139
140    # Defaults for dates
141    parser.add_argument('--fromdate', required=False, default='2016-01-01',
142                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')
143
144    parser.add_argument('--todate', required=False, default='2016-12-31',
145                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')
146
147    parser.add_argument('--cerebro', required=False, default='',
148                        metavar='kwargs', help='kwargs in key=value format')
149
150    parser.add_argument('--broker', required=False, default='',
151                        metavar='kwargs', help='kwargs in key=value format')
152
153    parser.add_argument('--sizer', required=False, default='',
154                        metavar='kwargs', help='kwargs in key=value format')
155
156    parser.add_argument('--strat', required=False, default='',
157                        metavar='kwargs', help='kwargs in key=value format')
158
159    parser.add_argument('--plot', required=False, default='',
160                        nargs='?', const='{}',
161                        metavar='kwargs', help='kwargs in key=value format')
162
163    pgroup = parser.add_mutually_exclusive_group(required=False)
164    pgroup.add_argument('--pandascal', required=False, action='store',
165                        default='', help='Name of trading calendar to use')
166
167    pgroup.add_argument('--owncal', required=False, action='store_true',
168                        help='Apply custom NYSE 2016 calendar')
169
170    parser.add_argument('--timeframe', required=False, action='store',
171                        default='Weeks', choices=['Weeks', 'Months', 'Years'],
172                        help='Timeframe to resample to')
173
174    return parser.parse_args(pargs)
175
176
177if __name__ == '__main__':
178    runstrat()
179