@@ -102,11 +102,13 @@ def plot(*, results, df, indicators, filename='', plot_width=None,
102
102
103
103
COLORS = [BEAR_COLOR , BULL_COLOR ]
104
104
105
- orig_trade_data = trade_data = results ._trade_data .copy (False )
105
+ equity_data = results ['_equity_curve' ].copy (False )
106
+ trades = results ['_trades' ]
106
107
107
108
orig_df = df = df .copy (False )
108
109
df .index .name = None # Provides source name @index
109
110
index = df .index
111
+ assert df .index .equals (equity_data .index )
110
112
time_resolution = getattr (index , 'resolution' , None )
111
113
is_datetime_index = index .is_all_dates
112
114
@@ -128,7 +130,7 @@ def plot(*, results, df, indicators, filename='', plot_width=None,
128
130
if omit_missing :
129
131
bar_width = .8
130
132
df = df .reset_index (drop = True )
131
- trade_data = trade_data .reset_index (drop = True )
133
+ equity_data = equity_data .reset_index (drop = True )
132
134
index = df .index
133
135
134
136
new_bokeh_figure = partial (
@@ -150,19 +152,23 @@ def plot(*, results, df, indicators, filename='', plot_width=None,
150
152
151
153
source = ColumnDataSource (df )
152
154
source .add ((df .Close >= df .Open ).values .astype (np .uint8 ).astype (str ), 'inc' )
153
- returns = trade_data ['Returns' ].dropna ()
155
+ trades_index = trades ['ExitBar' ]
156
+ if not omit_missing :
157
+ trades_index = index [trades_index .astype (int )]
158
+
154
159
trade_source = ColumnDataSource (dict (
155
- index = returns .index ,
156
- datetime = orig_trade_data ['Returns' ].dropna ().index ,
157
- exit_price = trade_data ['Exit Price' ].dropna (),
158
- returns_pos = (returns > 0 ).astype (np .int8 ).astype (str ),
160
+ index = trades_index ,
161
+ datetime = trades ['ExitTime' ],
162
+ exit_price = trades ['ExitPrice' ],
163
+ size = trades ['Size' ],
164
+ returns_positive = (trades ['ReturnPct' ] > 0 ).astype (int ).astype (str ),
159
165
))
160
166
161
167
inc_cmap = factor_cmap ('inc' , COLORS , ['0' , '1' ])
162
- cmap = factor_cmap ('returns_pos ' , COLORS , ['0' , '1' ])
168
+ cmap = factor_cmap ('returns_positive ' , COLORS , ['0' , '1' ])
163
169
colors_darker = [lightness (BEAR_COLOR , .35 ),
164
170
lightness (BULL_COLOR , .35 )]
165
- trades_cmap = factor_cmap ('returns_pos ' , colors_darker , ['0' , '1' ])
171
+ trades_cmap = factor_cmap ('returns_positive ' , colors_darker , ['0' , '1' ])
166
172
167
173
if is_datetime_index and omit_missing :
168
174
fig_ohlc .xaxis .formatter = FuncTickFormatter (
@@ -216,8 +222,8 @@ def set_tooltips(fig, tooltips=(), vline=True, renderers=(), show_arrow=True):
216
222
def _plot_equity_section ():
217
223
"""Equity section"""
218
224
# Max DD Dur. line
219
- equity = trade_data ['Equity' ]
220
- argmax = trade_data [ 'Drawdown Duration' ] .idxmax ()
225
+ equity = equity_data ['Equity' ]. reset_index ( drop = True )
226
+ argmax = equity_data [ 'DrawdownDuration' ]. reset_index ( drop = True ) .idxmax ()
221
227
try :
222
228
dd_start = equity [:argmax ].idxmax ()
223
229
except Exception : # ValueError: attempt to get argmax of an empty sequence
@@ -231,21 +237,25 @@ def _plot_equity_section():
231
237
else :
232
238
timedelta = dd_end - dd_start
233
239
# Get point intersection
234
- if dd_end != index [- 1 ]:
235
- x1 , x2 = index . get_loc ( dd_end ) - 1 , index . get_loc ( dd_end )
240
+ if dd_end != equity . index [- 1 ]:
241
+ x1 , x2 = dd_end - 1 , dd_end
236
242
y , y1 , y2 = equity [dd_start ], equity [x1 ], equity [x2 ]
237
- dd_end -= (1 - (y - y1 ) / (y2 - y1 )) * (dd_end - index [ x1 ] ) # y = a x + b
243
+ dd_end -= (1 - (y - y1 ) / (y2 - y1 )) * (dd_end - x1 ) # y = a x + b
238
244
239
245
if smooth_equity :
240
- select = (trade_data [['Entry Price' ,
241
- 'Exit Price' ]].dropna (how = 'all' ).index |
242
- # Include beginning
243
- equity .index [:1 ] |
244
- # Include max dd end points. Otherwise, the MaxDD line looks amiss.
245
- pd .Index ([dd_start , dd_end ]))
246
- equity = equity [select ].reindex (equity .index )
246
+ select = (pd .Index (trades ['ExitBar' ]) |
247
+ # Include beginning and end
248
+ equity .index [:1 ] | equity .index [- 1 :] |
249
+ # Include peak equity and peak DD
250
+ pd .Index ([equity .idxmax (), argmax ]) |
251
+ # Include max dd end points. Otherwise the MaxDD line looks amiss.
252
+ pd .Index ([dd_start , int (dd_end ), min (equity .size - 1 , int (dd_end + 1 ))]))
253
+ select = select .unique ().dropna ()
254
+ equity = equity .iloc [select ].reindex (equity .index )
247
255
equity .interpolate (inplace = True )
248
256
257
+ equity .index = equity_data .index
258
+
249
259
if relative_equity :
250
260
equity /= equity .iloc [0 ]
251
261
@@ -287,12 +297,12 @@ def _plot_equity_section():
287
297
color = 'blue' , size = 8 )
288
298
289
299
if not plot_drawdown :
290
- drawdown = trade_data [ 'Drawdown ' ]
300
+ drawdown = equity_data [ 'DrawdownPct ' ]
291
301
argmax = drawdown .idxmax ()
292
302
fig .scatter (argmax , equity [argmax ],
293
303
legend_label = 'Max Drawdown (-{:.1f}%)' .format (100 * drawdown [argmax ]),
294
304
color = 'red' , size = 8 )
295
- fig .line ([dd_start , dd_end ] , equity [dd_start ],
305
+ fig .line ([index [ dd_start ], index [ int ( dd_end )]] , equity . iloc [dd_start ],
296
306
line_color = 'red' , line_width = 2 ,
297
307
legend_label = 'Max Dd Dur. ({})' .format (timedelta )
298
308
.replace (' 00:00:00' , '' )
@@ -303,7 +313,7 @@ def _plot_equity_section():
303
313
def _plot_drawdown_section ():
304
314
"""Drawdown section"""
305
315
fig = new_indicator_figure (y_axis_label = "Drawdown" )
306
- drawdown = trade_data [ 'Drawdown ' ]
316
+ drawdown = equity_data [ 'DrawdownPct ' ]
307
317
argmax = drawdown .idxmax ()
308
318
source .add (drawdown , 'drawdown' )
309
319
r = fig .line ('index' , 'drawdown' , source = source , line_width = 1.3 )
@@ -319,20 +329,22 @@ def _plot_pl_section():
319
329
fig = new_indicator_figure (y_axis_label = "Profit / Loss" )
320
330
fig .add_layout (Span (location = 0 , dimension = 'width' , line_color = '#666666' ,
321
331
line_dash = 'dashed' , line_width = 1 ))
322
- position = trade_data ['Exit Position' ].dropna ()
323
- returns_long = returns .copy ()
324
- returns_short = returns .copy ()
325
- returns_long [position < 0 ] = np .nan
326
- returns_short [position > 0 ] = np .nan
332
+ returns_long = np .where (trades ['Size' ] > 0 , trades ['ReturnPct' ], np .nan )
333
+ returns_short = np .where (trades ['Size' ] < 0 , trades ['ReturnPct' ], np .nan )
334
+ size = trades ['Size' ].abs ()
335
+ size = np .interp (size , (size .min (), size .max ()), (10 , 20 ))
327
336
trade_source .add (returns_long , 'returns_long' )
328
337
trade_source .add (returns_short , 'returns_short' )
329
- MARKER_SIZE = 13
338
+ trade_source . add ( size , 'marker_size' )
330
339
r1 = fig .scatter ('index' , 'returns_long' , source = trade_source , fill_color = cmap ,
331
- marker = 'triangle' , line_color = 'black' , size = MARKER_SIZE )
340
+ marker = 'triangle' , line_color = 'black' , size = 'marker_size' )
332
341
r2 = fig .scatter ('index' , 'returns_short' , source = trade_source , fill_color = cmap ,
333
- marker = 'inverted_triangle' , line_color = 'black' , size = MARKER_SIZE )
334
- set_tooltips (fig , [("P/L" , "@returns_long{+0.[000]%}" )], vline = False , renderers = [r1 ])
335
- set_tooltips (fig , [("P/L" , "@returns_short{+0.[000]%}" )], vline = False , renderers = [r2 ])
342
+ marker = 'inverted_triangle' , line_color = 'black' , size = 'marker_size' )
343
+ tooltips = [("Size" , "@size{0,0}" )]
344
+ set_tooltips (fig , tooltips + [("P/L" , "@returns_long{+0.[000]%}" )],
345
+ vline = False , renderers = [r1 ])
346
+ set_tooltips (fig , tooltips + [("P/L" , "@returns_short{+0.[000]%}" )],
347
+ vline = False , renderers = [r2 ])
336
348
fig .yaxis .formatter = NumeralTickFormatter (format = "0.[00]%" )
337
349
return fig
338
350
@@ -409,15 +421,11 @@ def _plot_ohlc():
409
421
410
422
def _plot_ohlc_trades ():
411
423
"""Trade entry / exit markers on OHLC plot"""
412
- exit_price = trade_data ['Exit Price' ].dropna ()
413
- entry_price = trade_data ['Entry Price' ].dropna ().iloc [:exit_price .size ] # entry can be one more at the end # noqa: E501
414
- trade_source .add (np .column_stack ((entry_price .index , exit_price .index )).tolist (),
415
- 'position_lines_xs' )
416
- trade_source .add (np .column_stack ((entry_price , exit_price )).tolist (),
417
- 'position_lines_ys' )
424
+ trade_source .add (trades [['EntryBar' , 'ExitBar' ]].values .tolist (), 'position_lines_xs' )
425
+ trade_source .add (trades [['EntryPrice' , 'ExitPrice' ]].values .tolist (), 'position_lines_ys' )
418
426
fig_ohlc .multi_line (xs = 'position_lines_xs' , ys = 'position_lines_ys' ,
419
427
source = trade_source , line_color = trades_cmap ,
420
- legend_label = 'Trades ({})' .format (len (trade_data )),
428
+ legend_label = 'Trades ({})' .format (len (trades )),
421
429
line_width = 8 , line_alpha = 1 , line_dash = 'dotted' )
422
430
423
431
def _plot_indicators ():
0 commit comments