back-trader-python 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. back_trader_python-1.4.0.dist-info/METADATA +1491 -0
  2. back_trader_python-1.4.0.dist-info/RECORD +465 -0
  3. back_trader_python-1.4.0.dist-info/WHEEL +5 -0
  4. back_trader_python-1.4.0.dist-info/licenses/LICENSE +674 -0
  5. back_trader_python-1.4.0.dist-info/top_level.txt +1 -0
  6. backtrader/__init__.py +148 -0
  7. backtrader/_cerebro/__init__.py +5 -0
  8. backtrader/_cerebro/channel.py +382 -0
  9. backtrader/_cerebro/execution.py +377 -0
  10. backtrader/_cerebro/lifecycle.py +143 -0
  11. backtrader/_cerebro/notifications.py +150 -0
  12. backtrader/_cerebro/presentation.py +230 -0
  13. backtrader/_cerebro/registry.py +593 -0
  14. backtrader/_cerebro/runnext.py +551 -0
  15. backtrader/_cerebro/runonce.py +142 -0
  16. backtrader/analyzer.py +594 -0
  17. backtrader/analyzers/__init__.py +50 -0
  18. backtrader/analyzers/annualreturn.py +226 -0
  19. backtrader/analyzers/calmar.py +165 -0
  20. backtrader/analyzers/drawdown.py +287 -0
  21. backtrader/analyzers/leverage.py +112 -0
  22. backtrader/analyzers/logreturnsrolling.py +190 -0
  23. backtrader/analyzers/periodstats.py +153 -0
  24. backtrader/analyzers/positions.py +119 -0
  25. backtrader/analyzers/pyfolio.py +470 -0
  26. backtrader/analyzers/returns.py +192 -0
  27. backtrader/analyzers/sharpe.py +307 -0
  28. backtrader/analyzers/sharpe_ratio_stats.py +534 -0
  29. backtrader/analyzers/sqn.py +112 -0
  30. backtrader/analyzers/timereturn.py +192 -0
  31. backtrader/analyzers/total_value.py +75 -0
  32. backtrader/analyzers/tradeanalyzer.py +278 -0
  33. backtrader/analyzers/transactions.py +141 -0
  34. backtrader/analyzers/vwr.py +245 -0
  35. backtrader/bokeh/__init__.py +155 -0
  36. backtrader/bokeh/analyzers/__init__.py +13 -0
  37. backtrader/bokeh/analyzers/plot.py +192 -0
  38. backtrader/bokeh/analyzers/recorder.py +181 -0
  39. backtrader/bokeh/app.py +1094 -0
  40. backtrader/bokeh/live/__init__.py +11 -0
  41. backtrader/bokeh/live/client.py +352 -0
  42. backtrader/bokeh/live/datahandler.py +346 -0
  43. backtrader/bokeh/plot_adapter.py +200 -0
  44. backtrader/bokeh/schemes/__init__.py +14 -0
  45. backtrader/bokeh/schemes/blackly.py +76 -0
  46. backtrader/bokeh/schemes/scheme.py +150 -0
  47. backtrader/bokeh/schemes/tradimo.py +82 -0
  48. backtrader/bokeh/tab.py +125 -0
  49. backtrader/bokeh/tabs/__init__.py +30 -0
  50. backtrader/bokeh/tabs/analyzer.py +120 -0
  51. backtrader/bokeh/tabs/config.py +154 -0
  52. backtrader/bokeh/tabs/live.py +109 -0
  53. backtrader/bokeh/tabs/log.py +185 -0
  54. backtrader/bokeh/tabs/metadata.py +182 -0
  55. backtrader/bokeh/tabs/performance.py +359 -0
  56. backtrader/bokeh/tabs/source.py +70 -0
  57. backtrader/bokeh/utils/__init__.py +8 -0
  58. backtrader/bokeh/utils/helpers.py +167 -0
  59. backtrader/bokeh/webapp.py +164 -0
  60. backtrader/broker.py +478 -0
  61. backtrader/brokers/__init__.py +36 -0
  62. backtrader/brokers/bbroker.py +2576 -0
  63. backtrader/brokers/btapibroker.py +8227 -0
  64. backtrader/brokers/hft/__init__.py +89 -0
  65. backtrader/brokers/hft/binance_bbo.py +625 -0
  66. backtrader/brokers/hft/binance_bbo_compare.py +1398 -0
  67. backtrader/brokers/hft/examples.py +1228 -0
  68. backtrader/brokers/hft/exchange.py +380 -0
  69. backtrader/brokers/hft/latency.py +309 -0
  70. backtrader/brokers/hft/matching_core.py +572 -0
  71. backtrader/brokers/hft/queue.py +238 -0
  72. backtrader/brokers/hft/recorder.py +88 -0
  73. backtrader/brokers/hft/state.py +138 -0
  74. backtrader/brokers/impact_models.py +118 -0
  75. backtrader/brokers/mixbroker.py +895 -0
  76. backtrader/brokers/tickbroker.py +1991 -0
  77. backtrader/btrun/__init__.py +12 -0
  78. backtrader/btrun/btrun.py +1218 -0
  79. backtrader/cerebro.py +828 -0
  80. backtrader/channel.py +682 -0
  81. backtrader/channels/__init__.py +23 -0
  82. backtrader/channels/bridge.py +186 -0
  83. backtrader/channels/funding.py +248 -0
  84. backtrader/channels/live_queue.py +216 -0
  85. backtrader/channels/live_validator.py +294 -0
  86. backtrader/channels/orderbook.py +257 -0
  87. backtrader/channels/tick.py +202 -0
  88. backtrader/comminfo.py +665 -0
  89. backtrader/commissions/__init__.py +106 -0
  90. backtrader/commissions/ctpoption.py +993 -0
  91. backtrader/configs/account_config_example.yaml +8 -0
  92. backtrader/dataseries.py +379 -0
  93. backtrader/errors.py +106 -0
  94. backtrader/events.py +980 -0
  95. backtrader/feed.py +1523 -0
  96. backtrader/feeds/__init__.py +75 -0
  97. backtrader/feeds/barrier.py +2006 -0
  98. backtrader/feeds/blaze.py +118 -0
  99. backtrader/feeds/btapifeed.py +1538 -0
  100. backtrader/feeds/btcsv.py +203 -0
  101. backtrader/feeds/chainer.py +114 -0
  102. backtrader/feeds/cryptohftdata.py +164 -0
  103. backtrader/feeds/csvgeneric.py +1205 -0
  104. backtrader/feeds/ctpcohort.py +1051 -0
  105. backtrader/feeds/influxfeed.py +158 -0
  106. backtrader/feeds/livefeed.py +71 -0
  107. backtrader/feeds/mixed_channel.py +108 -0
  108. backtrader/feeds/mt4csv.py +42 -0
  109. backtrader/feeds/pandafeed.py +381 -0
  110. backtrader/feeds/quandl.py +256 -0
  111. backtrader/feeds/rollover.py +229 -0
  112. backtrader/feeds/sierrachart.py +30 -0
  113. backtrader/feeds/vchart.py +162 -0
  114. backtrader/feeds/vchartcsv.py +84 -0
  115. backtrader/feeds/vchartfile.py +153 -0
  116. backtrader/feeds/yahoo.py +399 -0
  117. backtrader/fillers.py +148 -0
  118. backtrader/filters/__init__.py +34 -0
  119. backtrader/filters/bsplitter.py +127 -0
  120. backtrader/filters/calendardays.py +121 -0
  121. backtrader/filters/datafiller.py +192 -0
  122. backtrader/filters/datafilter.py +74 -0
  123. backtrader/filters/daysteps.py +96 -0
  124. backtrader/filters/heikinashi.py +63 -0
  125. backtrader/filters/renko.py +164 -0
  126. backtrader/filters/session.py +289 -0
  127. backtrader/flt.py +80 -0
  128. backtrader/functions.py +960 -0
  129. backtrader/indicator.py +449 -0
  130. backtrader/indicators/__init__.py +148 -0
  131. backtrader/indicators/accdecoscillator.py +110 -0
  132. backtrader/indicators/aroon.py +300 -0
  133. backtrader/indicators/atr.py +315 -0
  134. backtrader/indicators/awesomeoscillator.py +122 -0
  135. backtrader/indicators/basicops.py +834 -0
  136. backtrader/indicators/bollinger.py +223 -0
  137. backtrader/indicators/cci.py +89 -0
  138. backtrader/indicators/channels_ext.py +83 -0
  139. backtrader/indicators/contrib/__init__.py +228 -0
  140. backtrader/indicators/contrib/absolutely_no_lag_lwma.py +28 -0
  141. backtrader/indicators/contrib/absolutely_no_lag_lwma_color.py +44 -0
  142. backtrader/indicators/contrib/accumulation_distribution_line.py +92 -0
  143. backtrader/indicators/contrib/adx_cross_hull_style_indicator.py +249 -0
  144. backtrader/indicators/contrib/adxdmi.py +34 -0
  145. backtrader/indicators/contrib/ai_acceleration_deceleration_oscillator.py +34 -0
  146. backtrader/indicators/contrib/altr_trend_signal_v22.py +85 -0
  147. backtrader/indicators/contrib/anchored_momentum_line.py +115 -0
  148. backtrader/indicators/contrib/any_range_cld_tail_indicator.py +82 -0
  149. backtrader/indicators/contrib/aroon_horn_sign_indicator.py +96 -0
  150. backtrader/indicators/contrib/aroon_oscillator_sign_alert.py +50 -0
  151. backtrader/indicators/contrib/arrows_curves_indicator.py +112 -0
  152. backtrader/indicators/contrib/as_ctrend_indicator.py +143 -0
  153. backtrader/indicators/contrib/asimmetric_stoch_nr_indicator.py +187 -0
  154. backtrader/indicators/contrib/atr_normalize_histogram.py +118 -0
  155. backtrader/indicators/contrib/average_change_candle.py +165 -0
  156. backtrader/indicators/contrib/bb_squeeze_indicator.py +60 -0
  157. backtrader/indicators/contrib/bezier_st_dev_indicator.py +135 -0
  158. backtrader/indicators/contrib/binary_wave_indicator.py +233 -0
  159. backtrader/indicators/contrib/blau_c_momentum_indicator.py +123 -0
  160. backtrader/indicators/contrib/blau_cmi_indicator.py +141 -0
  161. backtrader/indicators/contrib/blau_csi.py +76 -0
  162. backtrader/indicators/contrib/blau_ergodic.py +53 -0
  163. backtrader/indicators/contrib/blau_t_stoch_i.py +72 -0
  164. backtrader/indicators/contrib/blau_ts_stochastic.py +85 -0
  165. backtrader/indicators/contrib/blau_tvi.py +55 -0
  166. backtrader/indicators/contrib/brain_trend2_indicator.py +128 -0
  167. backtrader/indicators/contrib/brain_trend_signal_proxy.py +47 -0
  168. backtrader/indicators/contrib/brake_parb_indicator.py +85 -0
  169. backtrader/indicators/contrib/breakout_bars_trend_v2.py +121 -0
  170. backtrader/indicators/contrib/bsi_indicator.py +87 -0
  171. backtrader/indicators/contrib/bulls_bears_eyes.py +67 -0
  172. backtrader/indicators/contrib/bulls_power.py +56 -0
  173. backtrader/indicators/contrib/bw_wise_man1_signal.py +102 -0
  174. backtrader/indicators/contrib/bykov_trend_indicator.py +85 -0
  175. backtrader/indicators/contrib/candle_stop_color.py +46 -0
  176. backtrader/indicators/contrib/candles_x_smoothed_indicator.py +69 -0
  177. backtrader/indicators/contrib/candlesticks_bw.py +45 -0
  178. backtrader/indicators/contrib/caudate_x_period_candle_color.py +56 -0
  179. backtrader/indicators/contrib/cci_histogram_indicator.py +53 -0
  180. backtrader/indicators/contrib/cci_woodies_indicator.py +80 -0
  181. backtrader/indicators/contrib/center_of_gravity_candle_indicator.py +83 -0
  182. backtrader/indicators/contrib/center_of_gravity_indicator.py +70 -0
  183. backtrader/indicators/contrib/cg_oscillator.py +40 -0
  184. backtrader/indicators/contrib/close_line_cci.py +38 -0
  185. backtrader/indicators/contrib/close_price_fractals.py +47 -0
  186. backtrader/indicators/contrib/color3rd_gen_xma_indicator.py +122 -0
  187. backtrader/indicators/contrib/color_bb_candles_indicator.py +108 -0
  188. backtrader/indicators/contrib/color_coppock_indicator.py +157 -0
  189. backtrader/indicators/contrib/color_hma.py +71 -0
  190. backtrader/indicators/contrib/color_j_variation_indicator.py +53 -0
  191. backtrader/indicators/contrib/color_metro_de_marker_indicator.py +78 -0
  192. backtrader/indicators/contrib/color_metro_stochastic_indicator.py +93 -0
  193. backtrader/indicators/contrib/color_metro_wpr_indicator.py +85 -0
  194. backtrader/indicators/contrib/color_schaff_de_marker_trend_cycle.py +92 -0
  195. backtrader/indicators/contrib/color_schaff_trend_cycle_indicator.py +203 -0
  196. backtrader/indicators/contrib/color_step_xccx_indicator.py +193 -0
  197. backtrader/indicators/contrib/color_x2_ma.py +49 -0
  198. backtrader/indicators/contrib/color_x_derivative.py +63 -0
  199. backtrader/indicators/contrib/color_zerolag_de_marker.py +84 -0
  200. backtrader/indicators/contrib/corrected_average_indicator.py +127 -0
  201. backtrader/indicators/contrib/darvas_boxes_system.py +73 -0
  202. backtrader/indicators/contrib/dema_range_channel_color.py +42 -0
  203. backtrader/indicators/contrib/derivative_indicator.py +95 -0
  204. backtrader/indicators/contrib/digital_ft01_indicator.py +112 -0
  205. backtrader/indicators/contrib/digital_macd.py +200 -0
  206. backtrader/indicators/contrib/donchian_channels_system.py +45 -0
  207. backtrader/indicators/contrib/dots_indicator.py +93 -0
  208. backtrader/indicators/contrib/ef_distance_indicator.py +82 -0
  209. backtrader/indicators/contrib/ema_rsi_va.py +80 -0
  210. backtrader/indicators/contrib/envelopes_jp_alonso.py +32 -0
  211. backtrader/indicators/contrib/f2a_ao_indicator.py +120 -0
  212. backtrader/indicators/contrib/fatl_filter.py +179 -0
  213. backtrader/indicators/contrib/fibo_candles_indicator.py +78 -0
  214. backtrader/indicators/contrib/fine_tuning_ma.py +100 -0
  215. backtrader/indicators/contrib/fisher_org_v1.py +102 -0
  216. backtrader/indicators/contrib/fisher_org_v1_sign.py +118 -0
  217. backtrader/indicators/contrib/force_index_ema.py +96 -0
  218. backtrader/indicators/contrib/force_index_ema_2.py +27 -0
  219. backtrader/indicators/contrib/forecast_oscilator.py +145 -0
  220. backtrader/indicators/contrib/fractal_amambk.py +81 -0
  221. backtrader/indicators/contrib/frama_series.py +84 -0
  222. backtrader/indicators/contrib/frasm_av2_indicator.py +104 -0
  223. backtrader/indicators/contrib/go_indicator.py +93 -0
  224. backtrader/indicators/contrib/hlr_indicator.py +95 -0
  225. backtrader/indicators/contrib/hma.py +50 -0
  226. backtrader/indicators/contrib/i4_drfv2.py +34 -0
  227. backtrader/indicators/contrib/i4_drfv3.py +38 -0
  228. backtrader/indicators/contrib/i_anch_mom_indicator.py +72 -0
  229. backtrader/indicators/contrib/i_de_marker_sign_indicator.py +64 -0
  230. backtrader/indicators/contrib/i_gap_indicator.py +45 -0
  231. backtrader/indicators/contrib/i_stoch_komposter_indicator.py +77 -0
  232. backtrader/indicators/contrib/i_trend_indicator.py +125 -0
  233. backtrader/indicators/contrib/iamma_indicator.py +39 -0
  234. backtrader/indicators/contrib/indexed_moving_average.py +33 -0
  235. backtrader/indicators/contrib/instantaneous_trend_filter_indicator.py +51 -0
  236. backtrader/indicators/contrib/inverse_reaction_indicator.py +41 -0
  237. backtrader/indicators/contrib/irsi_sign_indicator.py +95 -0
  238. backtrader/indicators/contrib/iwpr_sign_indicator.py +59 -0
  239. backtrader/indicators/contrib/j_brain_trend1_sig_indicator.py +233 -0
  240. backtrader/indicators/contrib/j_tpo_proxy.py +32 -0
  241. backtrader/indicators/contrib/jma_slope_indicator.py +73 -0
  242. backtrader/indicators/contrib/kalman_filter_indicator.py +119 -0
  243. backtrader/indicators/contrib/kalman_filter_line.py +127 -0
  244. backtrader/indicators/contrib/kama_indicator.py +150 -0
  245. backtrader/indicators/contrib/karacatica_indicator.py +99 -0
  246. backtrader/indicators/contrib/kdj_indicator.py +59 -0
  247. backtrader/indicators/contrib/kwan_ccc_indicator.py +195 -0
  248. backtrader/indicators/contrib/kwan_nrp_indicator.py +113 -0
  249. backtrader/indicators/contrib/kwan_rdp_indicator.py +192 -0
  250. backtrader/indicators/contrib/laguerre_adx_indicator.py +85 -0
  251. backtrader/indicators/contrib/laguerre_filter_indicator.py +66 -0
  252. backtrader/indicators/contrib/laguerre_plus_di_proxy.py +57 -0
  253. backtrader/indicators/contrib/laguerre_roc_indicator.py +81 -0
  254. backtrader/indicators/contrib/le_man_signal_indicator.py +63 -0
  255. backtrader/indicators/contrib/linear_reg_slope_v2_indicator.py +136 -0
  256. backtrader/indicators/contrib/loco_indicator.py +88 -0
  257. backtrader/indicators/contrib/lrma_indicator.py +185 -0
  258. backtrader/indicators/contrib/lsma_angle_indicator.py +106 -0
  259. backtrader/indicators/contrib/ma_rounding_channel_indicator.py +149 -0
  260. backtrader/indicators/contrib/macd2_indicator.py +61 -0
  261. backtrader/indicators/contrib/macd_candle_indicator.py +80 -0
  262. backtrader/indicators/contrib/malr_indicator.py +77 -0
  263. backtrader/indicators/contrib/momentum_candle_sign_indicator.py +51 -0
  264. backtrader/indicators/contrib/moving_average_fn_indicator.py +139 -0
  265. backtrader/indicators/contrib/mt5_stochastic_close_close.py +57 -0
  266. backtrader/indicators/contrib/muv_nor_diff_cloud_indicator.py +107 -0
  267. backtrader/indicators/contrib/non_lag_dot_indicator.py +124 -0
  268. backtrader/indicators/contrib/nrtr_extr_indicator.py +95 -0
  269. backtrader/indicators/contrib/nrtr_indicator.py +95 -0
  270. backtrader/indicators/contrib/p_channel_system.py +40 -0
  271. backtrader/indicators/contrib/percent_envelope.py +37 -0
  272. backtrader/indicators/contrib/percentage_crossover_channel.py +47 -0
  273. backtrader/indicators/contrib/pivot_zig_zag_proxy.py +47 -0
  274. backtrader/indicators/contrib/price_channel_stop_indicator.py +104 -0
  275. backtrader/indicators/contrib/price_extreme_channel.py +35 -0
  276. backtrader/indicators/contrib/qqe_cloud_indicator.py +129 -0
  277. backtrader/indicators/contrib/ravi_indicator.py +40 -0
  278. backtrader/indicators/contrib/raw_close_close_stochastic.py +74 -0
  279. backtrader/indicators/contrib/rd_trend_trigger_indicator.py +51 -0
  280. backtrader/indicators/contrib/renko_level.py +85 -0
  281. backtrader/indicators/contrib/renko_line_break.py +91 -0
  282. backtrader/indicators/contrib/rftl_indicator.py +41 -0
  283. backtrader/indicators/contrib/rkd_indicator.py +53 -0
  284. backtrader/indicators/contrib/roc2_vg_indicator.py +68 -0
  285. backtrader/indicators/contrib/rsi_histogram_indicator.py +43 -0
  286. backtrader/indicators/contrib/rsi_slowdown.py +57 -0
  287. backtrader/indicators/contrib/rsioma_v2.py +41 -0
  288. backtrader/indicators/contrib/rvi_histogram_indicator.py +107 -0
  289. backtrader/indicators/contrib/safe_adx.py +89 -0
  290. backtrader/indicators/contrib/shared_strategy_indicators.py +1651 -0
  291. backtrader/indicators/contrib/sidus_indicator.py +105 -0
  292. backtrader/indicators/contrib/silver_trend_indicator.py +79 -0
  293. backtrader/indicators/contrib/sliding_range_color.py +56 -0
  294. backtrader/indicators/contrib/slow_stoch.py +42 -0
  295. backtrader/indicators/contrib/smoothed_adx_indicator.py +86 -0
  296. backtrader/indicators/contrib/smoothed_rsi.py +31 -0
  297. backtrader/indicators/contrib/spearman_rank_correlation_histogram.py +60 -0
  298. backtrader/indicators/contrib/stalin_indicator.py +152 -0
  299. backtrader/indicators/contrib/starter_laguerre_filter.py +62 -0
  300. backtrader/indicators/contrib/step_manrtr_indicator.py +137 -0
  301. backtrader/indicators/contrib/stochastic_histogram_indicator.py +143 -0
  302. backtrader/indicators/contrib/t3_alarm_indicator.py +125 -0
  303. backtrader/indicators/contrib/t3_average.py +76 -0
  304. backtrader/indicators/contrib/t3_indicator.py +40 -0
  305. backtrader/indicators/contrib/the20s_v020_signal.py +93 -0
  306. backtrader/indicators/contrib/three_candles_indicator.py +70 -0
  307. backtrader/indicators/contrib/three_line_break_indicator.py +64 -0
  308. backtrader/indicators/contrib/time_line.py +57 -0
  309. backtrader/indicators/contrib/trading_channel_index_proxy.py +48 -0
  310. backtrader/indicators/contrib/trend_arrows_indicator.py +109 -0
  311. backtrader/indicators/contrib/trend_continuation_indicator.py +127 -0
  312. backtrader/indicators/contrib/trend_intensity_index_proxy.py +51 -0
  313. backtrader/indicators/contrib/trend_manager_indicator.py +39 -0
  314. backtrader/indicators/contrib/tri_x_candle_indicator.py +51 -0
  315. backtrader/indicators/contrib/trigger_line.py +66 -0
  316. backtrader/indicators/contrib/triple_ema_rate.py +34 -0
  317. backtrader/indicators/contrib/trvi_indicator.py +194 -0
  318. backtrader/indicators/contrib/two_pb_ideal_xosma_indicator.py +127 -0
  319. backtrader/indicators/contrib/ultra_absolutely_no_lag_lwma_color.py +92 -0
  320. backtrader/indicators/contrib/ultra_wpr_indicator.py +173 -0
  321. backtrader/indicators/contrib/up_down_candle_strength.py +68 -0
  322. backtrader/indicators/contrib/vinin_i_trend_indicator.py +139 -0
  323. backtrader/indicators/contrib/volume_weighted_ma_indicator.py +78 -0
  324. backtrader/indicators/contrib/volume_weighted_ma_st_dev_indicator.py +111 -0
  325. backtrader/indicators/contrib/vwap_close_indicator.py +65 -0
  326. backtrader/indicators/contrib/vwma_candle.py +57 -0
  327. backtrader/indicators/contrib/vwma_digit_system.py +70 -0
  328. backtrader/indicators/contrib/wami.py +43 -0
  329. backtrader/indicators/contrib/wprsi_signal_indicator.py +105 -0
  330. backtrader/indicators/contrib/x_de_marker_histogram_vol_direct_indicator.py +145 -0
  331. backtrader/indicators/contrib/x_fisher_indicator.py +64 -0
  332. backtrader/indicators/contrib/xcci_histogram_vol_direct_indicator.py +56 -0
  333. backtrader/indicators/contrib/xcci_histogram_vol_indicator.py +85 -0
  334. backtrader/indicators/contrib/xma_ichimoku.py +163 -0
  335. backtrader/indicators/contrib/xma_ishimoku_channel_indicator.py +65 -0
  336. backtrader/indicators/contrib/xma_ishimoku_line.py +68 -0
  337. backtrader/indicators/contrib/xma_range_bands_indicator.py +107 -0
  338. backtrader/indicators/contrib/xmacd_indicator.py +70 -0
  339. backtrader/indicators/contrib/xrsi_de_marker_histogram.py +67 -0
  340. backtrader/indicators/contrib/xrsi_histogram_vol_direct_indicator.py +52 -0
  341. backtrader/indicators/contrib/xrsi_histogram_vol_indicator.py +81 -0
  342. backtrader/indicators/contrib/xrvi_indicator.py +130 -0
  343. backtrader/indicators/contrib/zero_lag_macd.py +36 -0
  344. backtrader/indicators/contrib/zig_zag_recent_pivot_signal.py +90 -0
  345. backtrader/indicators/contrib/zpf_indicator.py +115 -0
  346. backtrader/indicators/crossover.py +337 -0
  347. backtrader/indicators/dema.py +175 -0
  348. backtrader/indicators/demarker.py +270 -0
  349. backtrader/indicators/deviation.py +284 -0
  350. backtrader/indicators/directionalmove.py +1071 -0
  351. backtrader/indicators/dma.py +112 -0
  352. backtrader/indicators/dpo.py +96 -0
  353. backtrader/indicators/dv2.py +56 -0
  354. backtrader/indicators/ema.py +145 -0
  355. backtrader/indicators/envelope.py +475 -0
  356. backtrader/indicators/hadelta.py +198 -0
  357. backtrader/indicators/heikinashi.py +153 -0
  358. backtrader/indicators/hma.py +153 -0
  359. backtrader/indicators/hurst.py +151 -0
  360. backtrader/indicators/ichimoku.py +267 -0
  361. backtrader/indicators/kama.py +181 -0
  362. backtrader/indicators/kst.py +159 -0
  363. backtrader/indicators/lrsi.py +125 -0
  364. backtrader/indicators/mabase.py +147 -0
  365. backtrader/indicators/macd.py +322 -0
  366. backtrader/indicators/momentum.py +267 -0
  367. backtrader/indicators/moneyflow.py +237 -0
  368. backtrader/indicators/mt5atr.py +124 -0
  369. backtrader/indicators/myind.py +179 -0
  370. backtrader/indicators/obv.py +94 -0
  371. backtrader/indicators/ols.py +265 -0
  372. backtrader/indicators/oscillator.py +161 -0
  373. backtrader/indicators/percentchange.py +83 -0
  374. backtrader/indicators/percentrank.py +46 -0
  375. backtrader/indicators/pivotpoint.py +469 -0
  376. backtrader/indicators/prettygoodoscillator.py +113 -0
  377. backtrader/indicators/priceops_ext.py +123 -0
  378. backtrader/indicators/priceoscillator.py +262 -0
  379. backtrader/indicators/psar.py +212 -0
  380. backtrader/indicators/rmi.py +69 -0
  381. backtrader/indicators/rsi.py +440 -0
  382. backtrader/indicators/sma.py +141 -0
  383. backtrader/indicators/smma.py +116 -0
  384. backtrader/indicators/spread.py +54 -0
  385. backtrader/indicators/stochastic.py +263 -0
  386. backtrader/indicators/supertrend.py +436 -0
  387. backtrader/indicators/trend_ext.py +105 -0
  388. backtrader/indicators/trix.py +202 -0
  389. backtrader/indicators/tsi.py +155 -0
  390. backtrader/indicators/ultimateoscillator.py +158 -0
  391. backtrader/indicators/vortex.py +62 -0
  392. backtrader/indicators/williams.py +194 -0
  393. backtrader/indicators/wma.py +103 -0
  394. backtrader/indicators/zlema.py +135 -0
  395. backtrader/indicators/zlind.py +104 -0
  396. backtrader/linebuffer.py +3155 -0
  397. backtrader/lineiterator.py +2911 -0
  398. backtrader/lineroot.py +1106 -0
  399. backtrader/lineseries.py +2559 -0
  400. backtrader/live_trading/__init__.py +31 -0
  401. backtrader/live_trading/interface.py +404 -0
  402. backtrader/mathsupport.py +94 -0
  403. backtrader/metabase.py +1804 -0
  404. backtrader/mixins/__init__.py +21 -0
  405. backtrader/mixins/singleton.py +118 -0
  406. backtrader/observer.py +106 -0
  407. backtrader/observers/__init__.py +45 -0
  408. backtrader/observers/benchmark.py +126 -0
  409. backtrader/observers/broker.py +184 -0
  410. backtrader/observers/buysell.py +144 -0
  411. backtrader/observers/drawdown.py +161 -0
  412. backtrader/observers/logreturns.py +113 -0
  413. backtrader/observers/timereturn.py +86 -0
  414. backtrader/observers/trade_logger.py +2972 -0
  415. backtrader/observers/tradelogger.py +6 -0
  416. backtrader/observers/trades.py +258 -0
  417. backtrader/order.py +1114 -0
  418. backtrader/parameters.py +2345 -0
  419. backtrader/plot/__init__.py +54 -0
  420. backtrader/plot/finance.py +1022 -0
  421. backtrader/plot/formatters.py +200 -0
  422. backtrader/plot/locator.py +353 -0
  423. backtrader/plot/multicursor.py +495 -0
  424. backtrader/plot/plot.py +2500 -0
  425. backtrader/plot/plot_plotly.py +1351 -0
  426. backtrader/plot/scheme.py +253 -0
  427. backtrader/plot/utils.py +104 -0
  428. backtrader/position.py +290 -0
  429. backtrader/position_modes.py +132 -0
  430. backtrader/profiles.py +254 -0
  431. backtrader/reports/__init__.py +39 -0
  432. backtrader/reports/charts.py +371 -0
  433. backtrader/reports/performance.py +620 -0
  434. backtrader/reports/reporter.py +660 -0
  435. backtrader/resamplerfilter.py +1001 -0
  436. backtrader/signal.py +118 -0
  437. backtrader/signals/__init__.py +17 -0
  438. backtrader/sizer.py +114 -0
  439. backtrader/sizers/__init__.py +26 -0
  440. backtrader/sizers/fixedsize.py +161 -0
  441. backtrader/sizers/percents_sizer.py +119 -0
  442. backtrader/store.py +221 -0
  443. backtrader/stores/__init__.py +33 -0
  444. backtrader/stores/btapistore.py +15506 -0
  445. backtrader/stores/livestore.py +137 -0
  446. backtrader/stores/vchartfile.py +96 -0
  447. backtrader/strategy.py +3655 -0
  448. backtrader/talib.py +280 -0
  449. backtrader/test_helpers.py +96 -0
  450. backtrader/timer.py +358 -0
  451. backtrader/trade.py +442 -0
  452. backtrader/tradingcal.py +361 -0
  453. backtrader/utils/__init__.py +68 -0
  454. backtrader/utils/autodict.py +251 -0
  455. backtrader/utils/date.py +71 -0
  456. backtrader/utils/dateintern.py +509 -0
  457. backtrader/utils/flushfile.py +94 -0
  458. backtrader/utils/fractal.py +101 -0
  459. backtrader/utils/get_metrics.py +101 -0
  460. backtrader/utils/load_data.py +209 -0
  461. backtrader/utils/log_message.py +998 -0
  462. backtrader/utils/ordereddefaultdict.py +75 -0
  463. backtrader/utils/py3.py +296 -0
  464. backtrader/version.py +21 -0
  465. backtrader/writer.py +372 -0
@@ -0,0 +1,470 @@
1
+ #!/usr/bin/env python
2
+ """PyFolio Analyzer Module - PyFolio integration.
3
+
4
+ This module provides the PyFolio analyzer for collecting data compatible
5
+ with the pyfolio library for performance analysis.
6
+
7
+ Classes:
8
+ PyFolio: Analyzer that collects data for pyfolio.
9
+
10
+ Example:
11
+ >>> cerebro = bt.Cerebro()
12
+ >>> cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
13
+ >>> results = cerebro.run()
14
+ >>> pyfolio_data = results[0].analyzers.pyfolio.get_analysis()
15
+ """
16
+
17
+ import datetime
18
+ import math
19
+
20
+ import numpy as np
21
+ import pandas as pd
22
+
23
+ from ..analyzer import Analyzer
24
+ from ..dataseries import TimeFrame
25
+ from ..metabase import OwnerContext
26
+ from ..utils.py3 import iteritems
27
+ from .leverage import GrossLeverage
28
+ from .positions import PositionsValue
29
+ from .timereturn import TimeReturn
30
+ from .transactions import Transactions
31
+
32
+
33
+ def _daily_returns(values, name):
34
+ """Copy a daily return series and align timezone-aware and naive date indices."""
35
+ if isinstance(values, pd.DataFrame):
36
+ if name in values:
37
+ values = values[name]
38
+ elif len(values.columns) == 1:
39
+ values = values.iloc[:, 0]
40
+ else:
41
+ raise ValueError(f"Expected one return column or a {name!r} column")
42
+ series = pd.Series(values, dtype=float).copy()
43
+ series.index = pd.to_datetime(series.index, utc=True).tz_localize(None).normalize()
44
+ if series.index.has_duplicates:
45
+ raise ValueError("Formatted PyFolio metrics require one return per day")
46
+ series = series.sort_index().dropna()
47
+ if not np.isfinite(series.to_numpy()).all() or (series < -1).any():
48
+ raise ValueError("Returns must be finite and cannot be less than -100%")
49
+ return series.rename(name)
50
+
51
+
52
+ def _ratio(numerator, denominator):
53
+ """Return NaN for a ratio whose denominator does not define a statistic."""
54
+ return numerator / denominator if denominator > 0 else float("nan")
55
+
56
+
57
+ def _drawdown_details(returns):
58
+ """Return signed maximum drawdown and (peak, trough, recovery) dates."""
59
+ if returns.empty:
60
+ return float("nan"), None
61
+ wealth = (1.0 + returns).cumprod()
62
+ peaks = wealth.cummax().clip(lower=1.0)
63
+ drawdowns = wealth.div(peaks).sub(1.0)
64
+ trough_pos = int(np.argmin(drawdowns.to_numpy()))
65
+ drawdown = float(drawdowns.iloc[trough_pos])
66
+ if drawdown == 0:
67
+ return 0.0, None
68
+ previous = wealth.iloc[:trough_pos]
69
+ peak_pos = int(np.argmax(previous.to_numpy())) if not previous.empty else 0
70
+ # None identifies starting capital before the first recorded return.
71
+ peak = previous.index[peak_pos] if not previous.empty and previous.iloc[peak_pos] >= 1 else None
72
+ recovered = wealth.iloc[trough_pos + 1 :]
73
+ recovered = recovered[recovered >= peaks.iloc[trough_pos]]
74
+ recovery = recovered.index[0] if not recovered.empty else None
75
+ return drawdown, (peak, wealth.index[trough_pos], recovery)
76
+
77
+
78
+ # pyfolio analysis module
79
+ class PyFolio(Analyzer):
80
+ """This analyzer uses 4 children analyzers to collect data and transforms it
81
+ in to a data set compatible with ``pyfolio``
82
+
83
+ Children Analyzer
84
+
85
+ - ``TimeReturn``
86
+
87
+ Used to calculate the returns of the global portfolio value
88
+
89
+ - ``PositionsValue``
90
+
91
+ Used to calculate the value of the positions per data. It sets the
92
+ ``headers`` and ``cash`` parameters to ``True``
93
+
94
+ - ``Transactions``
95
+
96
+ Used to record each transaction on a data (size, price, value). Sets
97
+ the ``headers`` parameter to ``True``
98
+
99
+ - ``GrossLeverage``
100
+
101
+ Keeps track of the gross leverage (how much the strategy is invested)
102
+
103
+ Params:
104
+ These are passed transparently to the children
105
+
106
+ - timeframe (default: ``bt.TimeFrame.Days``)
107
+
108
+ If ``None`` then the timeframe of the 1st data of the system will be
109
+ used
110
+
111
+ - compression (default: `1``)
112
+
113
+ If ``None`` then the compression of the 1st data of the system will be
114
+ used
115
+
116
+ Both ``timeframe`` and ``compression`` are set following the default
117
+ behavior of ``pyfolio`` which is working with *daily* data and upsample it
118
+ to obtaine values like yearly returns.
119
+
120
+ Methods:
121
+
122
+ - get_analysis
123
+
124
+ Returns a dictionary with returns as values and the datetime points for
125
+ each return as keys
126
+ """
127
+
128
+ # Parameters
129
+ params = (("timeframe", TimeFrame.Days), ("compression", 1))
130
+
131
+ # Initialize
132
+ def __init__(self, *args, **kwargs):
133
+ """Initialize the PyFolio analyzer.
134
+
135
+ Creates child analyzers (TimeReturn, PositionsValue, Transactions,
136
+ GrossLeverage) to collect data for pyfolio integration.
137
+
138
+ Args:
139
+ *args: Positional arguments.
140
+ **kwargs: Keyword arguments for analyzer parameters.
141
+ """
142
+ # CRITICAL FIX: Call super().__init__() first to initialize self.p
143
+ super().__init__(*args, **kwargs)
144
+ dtfcomp = {"timeframe": self.p.timeframe, "compression": self.p.compression}
145
+
146
+ # Use OwnerContext so child analyzers can find this as their parent
147
+ with OwnerContext.set_owner(self):
148
+ self._returns = TimeReturn(**dtfcomp)
149
+ self._positions = PositionsValue(headers=True, cash=True)
150
+ self._transactions = Transactions(headers=True)
151
+ self._gross_lev = GrossLeverage()
152
+
153
+ # When stopping, get several analysis results
154
+ def stop(self):
155
+ """Collect results from child analyzers when backtest ends.
156
+
157
+ Gathers returns, positions, transactions, and gross leverage data
158
+ from the child analyzers for pyfolio processing.
159
+ """
160
+ super().stop()
161
+ self.rets["returns"] = self._returns.get_analysis()
162
+ self.rets["positions"] = self._positions.get_analysis()
163
+ self.rets["transactions"] = self._transactions.get_analysis()
164
+ self.rets["gross_lev"] = self._gross_lev.get_analysis()
165
+
166
+ # Adjust the results of the above four analyzers to get the input information required by pyfolio
167
+ def get_pf_items(self):
168
+ """Returns a tuple of 4 elements which can be used for further processing with
169
+ ``pyfolio``
170
+
171
+ returns, positions, transactions, gross_leverage
172
+
173
+ Because the objects are meant to be used as direct input to ``pyfolio``
174
+ this method makes a local import of ``pandas`` to convert the internal
175
+ *backtrader* results to *pandas DataFrames* which is the expected input
176
+ by, for example, ``pyfolio.create_full_tear_sheet``
177
+
178
+ The method will break if ``pandas`` is not installed
179
+ """
180
+ # keep import local to avoid disturbing installations with no pandas
181
+ # Returns
182
+ # Process returns
183
+ cols = ["index", "return"]
184
+ returns = pd.DataFrame.from_records(
185
+ iteritems(self.rets["returns"]), index=cols[0], columns=cols
186
+ )
187
+ returns.index = pd.to_datetime(returns.index)
188
+ returns.index = returns.index.tz_localize("UTC")
189
+ rets = returns["return"]
190
+ #
191
+ # Positions
192
+ # Process position
193
+ pss = self.rets["positions"]
194
+ # ps = [[k] + v[-2:] for k, v in iteritems(pss)]
195
+ ps = [[k] + v for k, v in iteritems(pss)]
196
+ if ps:
197
+ cols = ps.pop(0) # headers are in the first entry
198
+ positions = pd.DataFrame.from_records(ps, columns=cols)
199
+ else:
200
+ positions = pd.DataFrame(columns=["Datetime"])
201
+ positions.index = pd.to_datetime(positions["Datetime"])
202
+ del positions["Datetime"]
203
+ positions.index = positions.index.tz_localize("UTC")
204
+
205
+ #
206
+ # Transactions
207
+ # Process transactions
208
+ txss = self.rets["transactions"]
209
+ txs = []
210
+ # The transactions have a common key (date) and can potentially happend
211
+ # for several assets. The dictionary has a single key and a list of
212
+ # lists. Each sublist contains the fields of a transaction
213
+ # Hence the double loop to undo the list indirection
214
+ for k, v in iteritems(txss):
215
+ for v2 in v:
216
+ txs.append([k] + v2)
217
+
218
+ if txs:
219
+ cols = txs.pop(0) # headers are in the first entry
220
+ transactions = pd.DataFrame.from_records(txs, index=cols[0], columns=cols)
221
+ else:
222
+ transactions = pd.DataFrame(
223
+ columns=["date", "amount", "price", "sid", "symbol", "value"]
224
+ ).set_index("date")
225
+ transactions.index = pd.to_datetime(transactions.index)
226
+ transactions.index = transactions.index.tz_localize("UTC")
227
+
228
+ # Gross Leverage
229
+ # Process leverage
230
+ cols = ["index", "gross_lev"]
231
+ gross_lev = pd.DataFrame.from_records(
232
+ iteritems(self.rets["gross_lev"]), index=cols[0], columns=cols
233
+ )
234
+
235
+ gross_lev.index = pd.to_datetime(gross_lev.index)
236
+ gross_lev.index = gross_lev.index.tz_localize("UTC")
237
+ glev = gross_lev["gross_lev"]
238
+
239
+ # Return all together
240
+ # Return all results
241
+ return rets, positions, transactions, glev
242
+
243
+ def _get_order_type(self, row):
244
+ """Return the execution side, including buys that close a short position."""
245
+ if row["TRADE_TYPE"] not in {"开仓", "平仓"} or not row["amount"]:
246
+ return np.nan
247
+ return "买入" if row["amount"] > 0 else "卖出"
248
+
249
+ def _compute_profit_loss(self, trade_info, open_type, close_type):
250
+ """Fill gross realized PnL for labeled opens/closes, grouped by symbol.
251
+
252
+ Quantities come from ENTRUST_NUM when present; legacy tables containing
253
+ only TRADE_AMT represent one full lot per row. Partial closes use the
254
+ position's weighted entry price, as the broker does.
255
+ """
256
+ positions = {}
257
+ direction = -1 if open_type == "开空" else 1
258
+ for index, row in trade_info.iterrows():
259
+ if row["ORDER_TYPE"] not in {open_type, close_type}:
260
+ continue
261
+ symbol = row.get("O_CODE", row.get("symbol", ""))
262
+ quantity = abs(float(row.get("ENTRUST_NUM", 1.0)))
263
+ if not quantity:
264
+ continue
265
+ price = abs(float(row["TRADE_AMT"])) / quantity
266
+ size, average = positions.get(symbol, (0.0, 0.0))
267
+ if row["ORDER_TYPE"] == open_type:
268
+ average = (size * average + quantity * price) / (size + quantity)
269
+ positions[symbol] = (size + quantity, average)
270
+ trade_info.at[index, "PROFIT_LOSS"] = 0.0
271
+ else:
272
+ if quantity > size:
273
+ raise ValueError("Closing quantity exceeds the preceding opening quantity")
274
+ trade_info.at[index, "PROFIT_LOSS"] = direction * quantity * (price - average)
275
+ positions[symbol] = (size - quantity, average)
276
+ return trade_info
277
+
278
+ def _get_trade_info(self, trade_info, symbol_name, current_user_name="", pair_num=None):
279
+ """Format signed transactions into master's 19-column trade table.
280
+
281
+ Position transitions determine opens/closes, replacing the old row-modulo
282
+ pairing (which labeled every row as an opening with pair_num=1). A reversal
283
+ is split into its closing and opening parts. pair_num remains accepted for
284
+ source compatibility. PnL is gross, uses unit price and signed quantity,
285
+ and does not infer futures multipliers absent from this transaction schema.
286
+ COMMISSION is NaN unless the input provides an actual commission column.
287
+ """
288
+ if pair_num is not None and (not isinstance(pair_num, int) or pair_num < 1):
289
+ raise ValueError("pair_num must be a positive integer or None")
290
+ columns = [
291
+ "TRADE_DATE",
292
+ "ENTRUST_DATE",
293
+ "O_CODE",
294
+ "KIND",
295
+ "TRADE_TYPE",
296
+ "ORDER_TYPE",
297
+ "TRADE_NUM",
298
+ "TRADE_PRICE",
299
+ "TRADE_AMT",
300
+ "ENTRUST_NUM",
301
+ "ENTRUST_PRICE",
302
+ "STATUS",
303
+ "PROFIT_LOSS",
304
+ "COMMISSION",
305
+ "CREATE_DATE",
306
+ "CREATE_USER",
307
+ "UPDATE_DATE",
308
+ "UPDATE_USER",
309
+ "D_FLAG",
310
+ ]
311
+ records, positions = [], {}
312
+ next_trade = 0
313
+ today = datetime.date.today()
314
+ for timestamp, row in trade_info.sort_index(kind="stable").iterrows():
315
+ amount, price = float(row["amount"]), float(row["price"])
316
+ if not math.isfinite(amount) or not math.isfinite(price):
317
+ raise ValueError("Transaction quantity and price must be finite")
318
+ if not amount:
319
+ continue
320
+ symbol = row["symbol"]
321
+ key = row.get("sid", symbol)
322
+ size, average, trade_num = positions.get(key, (0.0, 0.0, 0))
323
+ date = pd.Timestamp(timestamp).date()
324
+ commission = float(row.get("commission", np.nan))
325
+ parts = []
326
+ if size * amount < 0:
327
+ closed = min(abs(size), abs(amount))
328
+ signed_closed = math.copysign(closed, amount)
329
+ profit = closed * (price - average) * math.copysign(1.0, size)
330
+ parts.append((signed_closed, "平仓", trade_num, profit))
331
+ size += signed_closed
332
+ amount -= signed_closed
333
+ if amount:
334
+ if not size:
335
+ next_trade += 1
336
+ trade_num = next_trade
337
+ average = (abs(size) * average + abs(amount) * price) / (abs(size) + abs(amount))
338
+ size += amount
339
+ parts.append((amount, "开仓", trade_num, 0.0))
340
+ positions[key] = (size, average, trade_num)
341
+ for quantity, phase, number, profit in parts:
342
+ records.append(
343
+ [
344
+ pd.Timestamp(date),
345
+ pd.Timestamp(date),
346
+ symbol,
347
+ symbol_name,
348
+ phase,
349
+ self._get_order_type({"TRADE_TYPE": phase, "amount": quantity}),
350
+ number,
351
+ price,
352
+ quantity * price,
353
+ quantity,
354
+ price,
355
+ "完成",
356
+ profit,
357
+ commission * abs(quantity / float(row["amount"])),
358
+ today,
359
+ current_user_name,
360
+ today,
361
+ current_user_name,
362
+ 0,
363
+ ]
364
+ )
365
+ return pd.DataFrame.from_records(records, columns=columns)
366
+
367
+ def _get_performance_indicators(self, results, returns, benchmark_returns, current_user_name):
368
+ """Return master's performance fields with daily (252-session) statistics.
369
+
370
+ This compatibility report uses pandas/numpy rather than optional empyrical
371
+ APIs missing from released empyrical versions. Returns are decimals; PROFIT,
372
+ PROFIT_Y, MDD and the other legacy return fields remain percentage values.
373
+ Undefined ratios are NaN. DATE_REGION is (peak, trough, recovery), with None
374
+ for no drawdown or unavailable endpoints. TradeAnalyzer, under any name,
375
+ supplies net win/loss counts; without it nonempty trade statistics are NaN.
376
+ """
377
+ from .tradeanalyzer import TradeAnalyzer
378
+
379
+ strategy_returns = _daily_returns(returns, "return")
380
+ benchmark = _daily_returns(benchmark_returns, "returns")
381
+ paired = pd.concat([strategy_returns, benchmark], axis=1, join="inner").dropna()
382
+ active = paired["return"] - paired["returns"]
383
+ nan = float("nan")
384
+ annualizer = math.sqrt(252)
385
+
386
+ def cumulative(series):
387
+ return float((1 + series).prod() - 1) if not series.empty else nan
388
+
389
+ def sharpe(series):
390
+ return _ratio(float(series.mean()) * annualizer, float(series.std(ddof=1)))
391
+
392
+ profit = cumulative(strategy_returns)
393
+ annual_return = (
394
+ (1 + profit) ** (252.0 / len(strategy_returns)) - 1
395
+ if not strategy_returns.empty
396
+ else nan
397
+ )
398
+ baseline_profit = cumulative(paired["returns"])
399
+ maximum_drawdown, region = _drawdown_details(strategy_returns)
400
+ active_drawdown, _ = _drawdown_details(active)
401
+ variance = paired["returns"].var(ddof=1)
402
+ beta = paired["return"].cov(paired["returns"]) / variance if variance > 0 else nan
403
+ alpha = (1 + (paired["return"] - beta * paired["returns"]).mean()) ** 252 - 1
404
+ downside = (
405
+ float(np.sqrt(np.mean(np.minimum(strategy_returns.to_numpy(), 0.0) ** 2)))
406
+ if not strategy_returns.empty
407
+ else nan
408
+ )
409
+
410
+ analysis = None
411
+ if results:
412
+ for analyzer in results[0].analyzers:
413
+ if isinstance(analyzer, TradeAnalyzer):
414
+ analysis = analyzer.get_analysis()
415
+ break
416
+ win_num = loss_num = win_average = loss_average = nan
417
+ if analysis is not None:
418
+ won, lost = analysis.get("won", {}), analysis.get("lost", {})
419
+ win_num, loss_num = won.get("total", 0), lost.get("total", 0)
420
+ win_average = won.get("pnl", {}).get("average", nan)
421
+ loss_average = lost.get("pnl", {}).get("average", nan)
422
+ elif not self.get_pf_items()[2].shape[0]:
423
+ win_num = loss_num = 0
424
+ total = win_num + loss_num
425
+ win_ratio = win_num / total if total > 0 else (0.0 if total == 0 else nan)
426
+ today = datetime.date.today()
427
+ return {
428
+ "PROFIT": round(profit * 100, 2),
429
+ "PROFIT_Y": round(annual_return * 100, 2),
430
+ "SUPERIOR_PROFIT": round((profit - baseline_profit) * 100, 2),
431
+ "BASE_PROFIT": round(baseline_profit * 100, 2),
432
+ "ALPHA": round(float(alpha), 4),
433
+ "Beta": round(float(beta), 4),
434
+ "SHARPE_RATIO": round(sharpe(strategy_returns), 4),
435
+ "WIN_RATIO": round(win_ratio, 4),
436
+ "RRR": round(_ratio(win_average * 100, abs(loss_average)), 4),
437
+ "MDD": round(-maximum_drawdown * 100, 2),
438
+ "SORTINO_RATIO": round(
439
+ _ratio(float(strategy_returns.mean()) * annualizer, downside), 4
440
+ ),
441
+ "DAILY_SUPERIOR_PROFIT": round(float(active.mean()) * 100, 2),
442
+ "MDD_SUPERIOR_PROFIT": round(active_drawdown * 100, 2),
443
+ "SP_SHARPE_RATIO": round(sharpe(active), 4),
444
+ "DAILY_WIN_RATIO": round(win_ratio, 4),
445
+ "WIN_NUM": win_num,
446
+ "LOSS_NUM": loss_num,
447
+ "INFO_RATIO": round(sharpe(active), 4),
448
+ "VIX": round(float(strategy_returns.std(ddof=1)) * annualizer, 4),
449
+ "BASE_VIX": round(float(paired["returns"].std(ddof=1)) * annualizer, 4),
450
+ "DATE_REGION": region,
451
+ "CREATE_DATE": today,
452
+ "CREATE_USER": current_user_name,
453
+ "UPDATE_DATE": today,
454
+ "UPDATE_USER": current_user_name,
455
+ "D_FLAG": 0,
456
+ }
457
+
458
+ def get_format_results(self, results, benchmark_returns, symbol_name, current_user_name):
459
+ """Return (performance dict, transaction DataFrame) using master's signature.
460
+
461
+ See _get_performance_indicators and _get_trade_info for percentage units,
462
+ unavailable metrics/fees and the position-based replacement of row pairing.
463
+ Input return series and the underlying get_pf_items result are not mutated.
464
+ """
465
+ returns, _, transactions, _ = self.get_pf_items()
466
+ performance = self._get_performance_indicators(
467
+ results, returns, benchmark_returns, current_user_name
468
+ )
469
+ trades = self._get_trade_info(transactions, symbol_name, current_user_name, pair_num=1)
470
+ return performance, trades
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env python
2
+ """Returns Analyzer Module - Return statistics calculation.
3
+
4
+ This module provides the Returns analyzer for calculating total, average,
5
+ compound, and annualized returns using a logarithmic approach.
6
+
7
+ Classes:
8
+ Returns: Analyzer that calculates return statistics.
9
+
10
+ Example:
11
+ >>> cerebro = bt.Cerebro()
12
+ >>> cerebro.addanalyzer(bt.analyzers.Returns, _name='ret')
13
+ >>> results = cerebro.run()
14
+ >>> print(results[0].analyzers.ret.get_analysis())
15
+ """
16
+
17
+ import math
18
+
19
+ from ..analyzer import TimeFrameAnalyzerBase
20
+ from ..dataseries import TimeFrame
21
+
22
+
23
+ # Calculate total, average, compound and annualized returns using logarithmic method
24
+ class Returns(TimeFrameAnalyzerBase):
25
+ """
26
+ Total, Average, Compound and Annualized Returns calculated using a
27
+ logarithmic approach
28
+
29
+ See:
30
+
31
+ - https://www.crystalbull.com/sharpe-ratio-better-with-log-returns/
32
+
33
+ Params:
34
+
35
+ - ``timeframe`` (default: ``None``)
36
+
37
+ If ``None`` the `timeframe` of the first data in the system will be
38
+ used
39
+
40
+ Pass ``TimeFrame.NoTimeFrame`` to consider the entire dataset with no
41
+ time constraints
42
+
43
+ - ``compression`` (default: ``None``)
44
+
45
+ Only used for sub-day timeframes to, for example, work on an hourly
46
+ timeframe by specifying "TimeFrame.Minutes" and 60 as compression
47
+
48
+ If `None`, then the compression of the first data in the system will be
49
+ used
50
+
51
+ - ``tann`` (default: ``None``)
52
+
53
+ Number of periods to use for the annualization (normalization)
54
+
55
+ namely:
56
+
57
+ - ``days: 252``
58
+ - ``weeks: 52``
59
+ - ``months: 12``
60
+ - ``years: 1``
61
+
62
+ - ``fund`` (default: ``None``)
63
+
64
+ If `None`, the actual mode of the broker (fundmode - True/False) will
65
+ be autodetected to decide if the returns are based on the total net
66
+ asset value or on the fund value. See ``set_fundmode`` in the broker
67
+ documentation
68
+
69
+ Set it to ``True`` or ``False`` for a specific behavior
70
+
71
+ Methods:
72
+
73
+ - get_analysis
74
+
75
+ Returns a dictionary with returns as values and the datetime points for
76
+ each return as keys
77
+
78
+ The returned dict the following keys:
79
+
80
+ - ``rtot``: Total compound return
81
+ - ``ravg``: Average return for the entire period (timeframe specific)
82
+ - ``rnorm``: Annualized/Normalized return
83
+ - ``rnorm100``: Annualized/Normalized return expressed in 100%
84
+
85
+ """
86
+
87
+ # Parameters
88
+ params = (
89
+ ("tann", None),
90
+ ("fund", None),
91
+ )
92
+ # Days etc. for calculating annualization
93
+ _TANN = {
94
+ TimeFrame.Days: 252.0,
95
+ TimeFrame.Weeks: 52.0,
96
+ TimeFrame.Months: 12.0,
97
+ TimeFrame.Years: 1.0,
98
+ }
99
+
100
+ # Start
101
+ def __init__(self, *args, **kwargs):
102
+ """Initialize the Returns analyzer.
103
+
104
+ Args:
105
+ *args: Positional arguments.
106
+ **kwargs: Keyword arguments for analyzer parameters.
107
+ """
108
+ # Call parent class __init__ method to support timeframe and compression parameters
109
+ super().__init__(*args, **kwargs)
110
+
111
+ self._value_end = None
112
+ self._tcount = None
113
+ self._value_start = None
114
+ self._fundmode = None
115
+
116
+ def start(self):
117
+ """Initialize the analyzer at the start of the backtest.
118
+
119
+ Records the initial portfolio value and sets the fund mode.
120
+ """
121
+ super().start()
122
+ # If fund is None, _fundmode is broker's fundmode, otherwise equals fund
123
+ if self.p.fund is None:
124
+ self._fundmode = self.strategy.broker.fundmode
125
+ else:
126
+ self._fundmode = self.p.fund
127
+ # If fundmode is False, get value, otherwise get fundvalue
128
+ if not self._fundmode:
129
+ self._value_start = self.strategy.broker.getvalue()
130
+ else:
131
+ self._value_start = self.strategy.broker.fundvalue
132
+ # Count subperiods
133
+ self._tcount = 0
134
+
135
+ # When stopping
136
+ def stop(self):
137
+ """Calculate and store return statistics at the end of the backtest.
138
+
139
+ Calculates:
140
+ - rtot: Total compound return
141
+ - ravg: Average return for the period
142
+ - rnorm: Annualized return
143
+ - rnorm100: Annualized return in percentage form
144
+ """
145
+ super().stop()
146
+ # If fundmode is False, get value, otherwise get fundvalue
147
+ if not self._fundmode:
148
+ self._value_end = self.strategy.broker.getvalue()
149
+ else:
150
+ self._value_end = self.strategy.broker.fundvalue
151
+
152
+ # Compound return
153
+ # rtot calculates total log returns
154
+ try:
155
+ nlrtot = self._value_end / self._value_start
156
+ if isinstance(nlrtot, complex) or not math.isfinite(nlrtot) or nlrtot <= 0.0:
157
+ rtot = float("-inf")
158
+ else:
159
+ rtot = math.log(nlrtot)
160
+ if not math.isfinite(rtot):
161
+ rtot = float("-inf")
162
+ except (ZeroDivisionError, TypeError, ValueError):
163
+ rtot = float("-inf")
164
+
165
+ self.rets["rtot"] = rtot
166
+
167
+ # Average return
168
+ # Calculate average return, first calculate log returns, then calculate average log returns
169
+ if self._tcount > 0:
170
+ self.rets["ravg"] = ravg = rtot / self._tcount
171
+ else:
172
+ self.rets["ravg"] = ravg = 0.0
173
+
174
+ # Annualized normalized return
175
+ # Calculate annualized return
176
+ tann = self.p.tann or self._TANN.get(self.timeframe, None)
177
+ if tann is None:
178
+ tann = self._TANN.get(self.data._timeframe, 1.0) # assign default
179
+
180
+ if ravg > float("-inf"):
181
+ self.rets["rnorm"] = rnorm = math.expm1(ravg * tann)
182
+ else:
183
+ self.rets["rnorm"] = rnorm = ravg
184
+ # Annualized return in percentage form
185
+ self.rets["rnorm100"] = rnorm * 100.0 # human-readable %
186
+
187
+ def on_dt_over(self):
188
+ """Called when a datetime period is over.
189
+
190
+ Increments the subperiod counter.
191
+ """
192
+ self._tcount += 1 # count the subperiod