1
+ import os
2
+ import pandas as pd
3
+ from datetime import datetime
4
+ import backtrader as bt
5
+
6
+ class BuyAndHold (bt .Strategy ):
7
+ def __init__ (self ):
8
+ # To keep track of pending orders and buy price/commission
9
+ self .order = None
10
+
11
+ def log (self , txt , dt = None ):
12
+ ''' Logging function fot this strategy'''
13
+ dt = dt or self .datas [0 ].datetime .date (0 )
14
+ print ('%s, %s' % (dt .isoformat (), txt ))
15
+
16
+ def start (self ):
17
+ self .val_start = self .broker .get_cash () # keep the starting cash
18
+
19
+ def notify_trade (self , trade ):
20
+ if not trade .isclosed :
21
+ return
22
+ self .log ('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade .pnl , trade .pnlcomm ))
23
+
24
+ def notify_order (self , order ):
25
+ if order .status in [order .Submitted , order .Accepted ]:
26
+ # Buy/Sell order submitted/accepted to/by broker - Nothing to do
27
+ return
28
+
29
+ # Check if an order has been completed
30
+ # Attention: broker could reject order if not enough cash
31
+ if order .status in [order .Completed ]:
32
+ if order .isbuy ():
33
+ self .log (
34
+ 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
35
+ (order .executed .price ,
36
+ order .executed .value ,
37
+ order .executed .comm ))
38
+
39
+ self .buyprice = order .executed .price
40
+ self .buycomm = order .executed .comm
41
+ else : # Sell
42
+ self .log ('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
43
+ (order .executed .price ,
44
+ order .executed .value ,
45
+ order .executed .comm ))
46
+
47
+ self .bar_executed = len (self )
48
+
49
+ def next (self ):
50
+ # Simply log the closing price of the series from the reference
51
+ # self.log('Close, %.2f' % self.data.close[0])
52
+ # Buy all the available cash
53
+ # self.order_target_value(target=self.broker.get_cash())
54
+ #size = int(self.broker.get_cash() / self.data)
55
+ #self.order = self.buy(size=size)
56
+ self .buy ()
57
+
58
+ def stop (self ):
59
+ # calculate the actual returns
60
+ self .roi = (self .broker .get_value () / self .val_start ) - 1.0
61
+ print ('ROI: {:.2f}%' .format (100.0 * self .roi ))
62
+
63
+
64
+ if __name__ == '__main__' :
65
+ # Create a cerebro entitysetsizing
66
+ cerebro = bt .Cerebro ()
67
+
68
+ datapath = os .path .join ('../data/' , 'SPX.csv' )
69
+
70
+ # Create a Data Feed
71
+ data = bt .feeds .YahooFinanceCSVData (
72
+ dataname = datapath ,
73
+ # Do not pass values before this date
74
+ fromdate = datetime (2010 , 1 , 1 ),
75
+ # Do not pass values before this date
76
+ todate = datetime (2019 , 12 , 31 ),
77
+ # Do not pass values after this date
78
+ reverse = False )
79
+
80
+ # Add the Data Feed to Cerebro
81
+ cerebro .adddata (data )
82
+
83
+ # Set our desired cash start
84
+ cerebro .broker .setcash (100000.0 )
85
+
86
+ # Add a FixedSize sizer according to the stake
87
+ # cerebro.addsizer(bt.sizers.FixedSize, stake=10)
88
+ cerebro .addsizer (bt .sizers .PercentSizer , percents = 95 )
89
+
90
+ # Set the commission - 0.1% ... divide by 100 to remove the %
91
+ cerebro .broker .setcommission (commission = 0.001 )
92
+ # cheat-on-close
93
+ cerebro .broker .set_coc (True ) # doesn't seems to be working
94
+
95
+ # Print out the starting conditions
96
+ print ('Starting Portfolio Value: %.2f' % cerebro .broker .getvalue ())
97
+
98
+ # Add Analyzer
99
+ cerebro .addanalyzer (bt .analyzers .PyFolio , _name = 'pyfolio' )
100
+
101
+ # Add a strategy
102
+ cerebro .addstrategy (BuyAndHold )
103
+
104
+ # Run over everything
105
+ results = cerebro .run ()
106
+
107
+ # Print out the final result
108
+ print ('Final Portfolio Value: %.2f' % cerebro .broker .getvalue ())
109
+ cerebro .plot (style = 'candlestick' )
110
+
111
+ strat = results [0 ]
112
+ pyfoliozer = strat .analyzers .getbyname ('pyfolio' )
113
+ returns , positions , transactions , gross_lev = pyfoliozer .get_pf_items ()
114
+ print ('-------------- RETURNS ----------------' )
115
+ print (returns )
116
+ print ('-------------- POSITIONS ----------------' )
117
+ print (positions )
118
+ print ('-------------- TRANSACTIONS ----------------' )
119
+ print (transactions )
120
+ print ('-------------- GROSS LEVERAGE ----------------' )
121
+ print (gross_lev )
122
+
123
+ import empyrical as ep
124
+ import pyfolio as pf
125
+ import matplotlib .pyplot as plt
126
+ perf_stats_strat = pf .timeseries .perf_stats (returns )
127
+ drawdown_table = pf .timeseries .gen_drawdown_table (returns , 5 )
128
+ monthly_ret_table = ep .aggregate_returns (returns , 'monthly' )
129
+ monthly_ret_table = monthly_ret_table .unstack ().round (3 )
130
+ ann_ret_df = pd .DataFrame (ep .aggregate_returns (returns , 'yearly' ))
131
+ ann_ret_df = ann_ret_df .unstack ().round (3 )
132
+ print ('-------------- PERFORMANCE ----------------' )
133
+ print (perf_stats_strat )
134
+ print ('-------------- DRAWDOWN ----------------' )
135
+ print (drawdown_table )
136
+ print ('-------------- MONTHLY RETURN ----------------' )
137
+ print (monthly_ret_table )
138
+ print ('-------------- ANNUAL RETURN ----------------' )
139
+ print (ann_ret_df )
140
+
141
+ pf .create_full_tear_sheet (
142
+ returns ,
143
+ positions = positions ,
144
+ transactions = transactions ,
145
+ #live_start_date='2005-05-01',
146
+ round_trips = False )
147
+ plt .show ()
0 commit comments