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 St(bt.Strategy): 31 params = dict( 32 ma=bt.ind.SMA, 33 p1=10, 34 p2=30, 35 stoptype=bt.Order.StopTrail, 36 trailamount=0.0, 37 trailpercent=0.0, 38 limitoffset=0.0, 39 ) 40 41 def __init__(self): 42 ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2) 43 self.crup = bt.ind.CrossUp(ma1, ma2) 44 self.order = None 45 46 def next(self): 47 if not self.position: 48 if self.crup: 49 o = self.buy() 50 self.order = None 51 print('*' * 50) 52 53 elif self.order is None: 54 if self.p.stoptype == bt.Order.StopTrailLimit: 55 price = self.data.close[0] 56 plimit = self.data.close[0] + self.p.limitoffset 57 else: 58 price = None 59 plimit = None 60 61 self.order = self.sell(exectype=self.p.stoptype, 62 price=price, 63 plimit=plimit, 64 trailamount=self.p.trailamount, 65 trailpercent=self.p.trailpercent) 66 67 if self.p.trailamount: 68 tcheck = self.data.close - self.p.trailamount 69 else: 70 tcheck = self.data.close * (1.0 - self.p.trailpercent) 71 print(','.join( 72 map(str, [self.datetime.date(), self.data.close[0], 73 self.order.created.price, tcheck]) 74 ) 75 ) 76 print('-' * 10) 77 else: 78 if self.p.trailamount: 79 tcheck = self.data.close - self.p.trailamount 80 else: 81 tcheck = self.data.close * (1.0 - self.p.trailpercent) 82 print(','.join( 83 map(str, [self.datetime.date(), self.data.close[0], 84 self.order.created.price, tcheck]) 85 ) 86 ) 87 88 89def runstrat(args=None): 90 args = parse_args(args) 91 92 cerebro = bt.Cerebro() 93 94 # Data feed kwargs 95 kwargs = dict() 96 97 # Parse from/to-date 98 dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S' 99 for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']): 100 if a: 101 strpfmt = dtfmt + tmfmt * ('T' in a) 102 kwargs[d] = datetime.datetime.strptime(a, strpfmt) 103 104 # Data feed 105 data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs) 106 cerebro.adddata(data0) 107 108 # Broker 109 cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')')) 110 111 # Sizer 112 cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')')) 113 114 # Strategy 115 cerebro.addstrategy(St, **eval('dict(' + args.strat + ')')) 116 117 # Execute 118 cerebro.run(**eval('dict(' + args.cerebro + ')')) 119 120 if args.plot: # Plot if requested to 121 cerebro.plot(**eval('dict(' + args.plot + ')')) 122 123 124def parse_args(pargs=None): 125 parser = argparse.ArgumentParser( 126 formatter_class=argparse.ArgumentDefaultsHelpFormatter, 127 description=( 128 'StopTrail Sample' 129 ) 130 ) 131 132 parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt', 133 required=False, help='Data to read in') 134 135 # Defaults for dates 136 parser.add_argument('--fromdate', required=False, default='', 137 help='Date[time] in YYYY-MM-DD[THH:MM:SS] format') 138 139 parser.add_argument('--todate', required=False, default='', 140 help='Date[time] in YYYY-MM-DD[THH:MM:SS] format') 141 142 parser.add_argument('--cerebro', required=False, default='', 143 metavar='kwargs', help='kwargs in key=value format') 144 145 parser.add_argument('--broker', required=False, default='', 146 metavar='kwargs', help='kwargs in key=value format') 147 148 parser.add_argument('--sizer', required=False, default='', 149 metavar='kwargs', help='kwargs in key=value format') 150 151 parser.add_argument('--strat', required=False, default='', 152 metavar='kwargs', help='kwargs in key=value format') 153 154 parser.add_argument('--plot', required=False, default='', 155 nargs='?', const='{}', 156 metavar='kwargs', help='kwargs in key=value format') 157 158 return parser.parse_args(pargs) 159 160 161if __name__ == '__main__': 162 runstrat() 163