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,1218 @@
1
+ #!/usr/bin/env python
2
+ """btrun - Command-line runner for Backtrader backtesting framework.
3
+
4
+ This module provides a command-line interface for running backtrader strategies
5
+ from the command line. It supports loading data feeds, strategies, indicators,
6
+ observers, analyzers, and signals from both built-in modules and external files.
7
+
8
+ The main entry point is the `btrun()` function which parses command-line arguments
9
+ and executes a complete backtest with the specified configuration.
10
+
11
+ Example:
12
+ Running from command line:
13
+ $ python -m backtrader.btrun.btrun --data data.csv --strategy MyStrategy
14
+
15
+ Running programmatically:
16
+ >>> from backtrader.btrun import btrun
17
+ >>> btrun('--data data.csv --strategy MyStrategy')
18
+ """
19
+
20
+ import argparse
21
+ import ast
22
+ import datetime
23
+ import inspect
24
+ import random
25
+ import string
26
+ import sys
27
+
28
+ from .. import analyzers as analyzers_module
29
+ from .. import indicators as indicators_module
30
+ from .. import observers as observers_module
31
+ from .. import signal as signal_module
32
+ from .. import signals as signals_module
33
+ from .. import strategies as strategies_module
34
+ from ..analyzer import Analyzer
35
+ from ..cerebro import Cerebro
36
+ from ..dataseries import TimeFrame
37
+ from ..feeds.btcsv import BacktraderCSVData
38
+ from ..feeds.mt4csv import MT4CSVData
39
+ from ..feeds.sierrachart import SierraChartCSVData
40
+ from ..feeds.vchartcsv import VChartCSVData
41
+ from ..feeds.vchartfile import VChartFile
42
+ from ..feeds.yahoo import YahooFinanceCSVData, YahooFinanceData
43
+ from ..feeds.yahoounreversed import YahooFinanceCSVData as YahooFinanceCSVDataUnreversed
44
+ from ..indicator import Indicator
45
+ from ..observer import Observer
46
+ from ..strategy import Strategy
47
+ from ..utils.log_message import get_logger
48
+ from ..writer import WriterFile
49
+
50
+ logger = get_logger(__name__)
51
+
52
+ try:
53
+ from ..feeds.btapifeed import BtApiFeed
54
+ except ImportError:
55
+ BtApiFeed = None
56
+
57
+
58
+ DATAFORMATS = {
59
+ "btcsv": BacktraderCSVData,
60
+ "vchartcsv": VChartCSVData,
61
+ "vcfile": VChartFile,
62
+ "sierracsv": SierraChartCSVData,
63
+ "mt4csv": MT4CSVData,
64
+ "yahoocsv": YahooFinanceCSVData,
65
+ "yahoocsv_unreversed": YahooFinanceCSVDataUnreversed,
66
+ "yahoo": YahooFinanceData,
67
+ }
68
+
69
+ if BtApiFeed is not None:
70
+ DATAFORMATS["btapi"] = BtApiFeed
71
+
72
+ TIMEFRAMES = {
73
+ "microseconds": TimeFrame.MicroSeconds,
74
+ "seconds": TimeFrame.Seconds,
75
+ "minutes": TimeFrame.Minutes,
76
+ "days": TimeFrame.Days,
77
+ "weeks": TimeFrame.Weeks,
78
+ "months": TimeFrame.Months,
79
+ "years": TimeFrame.Years,
80
+ }
81
+
82
+
83
+ def _safe_parse_kwargs(kwtext: str) -> dict:
84
+ """Safely parse kwargs string without using eval().
85
+
86
+ Parses a string like "a=1,b=2,c='hello'" into a dictionary.
87
+ Uses ast.literal_eval for safe evaluation of values.
88
+
89
+ Args:
90
+ kwtext: String containing key=value pairs separated by commas.
91
+ Values can be integers, floats, booleans, strings, or None.
92
+
93
+ Returns:
94
+ dict: Parsed keyword arguments.
95
+
96
+ Example:
97
+ >>> _safe_parse_kwargs("a=1,b=2.5,c=True,d='hello'")
98
+ {'a': 1, 'b': 2.5, 'c': True, 'd': 'hello'}
99
+ """
100
+ if not kwtext or not kwtext.strip():
101
+ return {}
102
+
103
+ result = {}
104
+
105
+ # Try ast.literal_eval first for the entire dict expression
106
+ try:
107
+ # Wrap in dict() and try literal_eval
108
+ parsed = ast.literal_eval(f"dict({kwtext})")
109
+ if isinstance(parsed, dict):
110
+ return parsed
111
+ except (ValueError, SyntaxError, TypeError):
112
+ # Not a clean dict literal; fall back to manual key=value parsing below.
113
+ logger.debug("btrun:112 ignored ValueError,SyntaxError,TypeError")
114
+
115
+ # Fall back to manual parsing
116
+ # Handle nested parentheses and quoted strings properly
117
+ items = _split_kwargs(kwtext)
118
+
119
+ for item in items:
120
+ item = item.strip()
121
+ if not item:
122
+ continue
123
+
124
+ if "=" not in item:
125
+ continue
126
+
127
+ key, value = item.split("=", 1)
128
+ key = key.strip()
129
+ value = value.strip()
130
+
131
+ result[key] = _convert_value(value)
132
+
133
+ return result
134
+
135
+
136
+ def _split_kwargs(kwtext: str) -> list:
137
+ """Split kwargs string by commas, respecting quotes and parentheses.
138
+
139
+ Args:
140
+ kwtext: String containing key=value pairs.
141
+
142
+ Returns:
143
+ list: List of individual key=value strings.
144
+ """
145
+ items = []
146
+ current: list = []
147
+ depth = 0
148
+ in_string = False
149
+ string_char = None
150
+
151
+ for char in kwtext:
152
+ if char in ('"', "'") and (not current or current[-1] != "\\"):
153
+ if not in_string:
154
+ in_string = True
155
+ string_char = char
156
+ elif char == string_char:
157
+ in_string = False
158
+ string_char = None
159
+ current.append(char)
160
+ elif char in ("(", "[", "{") and not in_string:
161
+ depth += 1
162
+ current.append(char)
163
+ elif char in (")", "]", "}") and not in_string:
164
+ depth -= 1
165
+ current.append(char)
166
+ elif char == "," and depth == 0 and not in_string:
167
+ items.append("".join(current))
168
+ current = []
169
+ else:
170
+ current.append(char)
171
+
172
+ if current:
173
+ items.append("".join(current))
174
+
175
+ return items
176
+
177
+
178
+ def _convert_value(value: str):
179
+ """Convert a string value to its appropriate Python type.
180
+
181
+ Args:
182
+ value: String representation of a value.
183
+
184
+ Returns:
185
+ The value converted to int, float, bool, None, or string.
186
+ """
187
+ # Try ast.literal_eval first (handles most cases safely)
188
+ try:
189
+ return ast.literal_eval(value)
190
+ except (ValueError, SyntaxError):
191
+ # Not a Python literal; try the explicit conversions below.
192
+ logger.debug("btrun:191 ignored ValueError,SyntaxError")
193
+
194
+ # Handle boolean values (case-insensitive)
195
+ if value.lower() == "true":
196
+ return True
197
+ if value.lower() == "false":
198
+ return False
199
+ if value.lower() == "none":
200
+ return None
201
+
202
+ # Handle integers
203
+ try:
204
+ return int(value)
205
+ except ValueError:
206
+ # Not an integer; try float next.
207
+ logger.debug("btrun:206 ignored ValueError")
208
+
209
+ # Handle floats
210
+ try:
211
+ return float(value)
212
+ except ValueError:
213
+ # Not a float; treat as a (possibly quoted) string below.
214
+ logger.debug("btrun:213 ignored ValueError")
215
+
216
+ # Remove surrounding quotes if present
217
+ if len(value) >= 2:
218
+ if (value[0] == '"' and value[-1] == '"') or (value[0] == "'" and value[-1] == "'"):
219
+ return value[1:-1]
220
+
221
+ # Return as string
222
+ return value
223
+
224
+
225
+ def btrun(pargs=""):
226
+ """Run a backtest with the specified configuration.
227
+
228
+ This is the main entry point for command-line backtesting. It parses arguments,
229
+ sets up the Cerebro engine with data feeds, strategies, indicators, observers,
230
+ and analyzers, then executes the backtest and optionally displays results.
231
+
232
+ Args:
233
+ pargs (str or list, optional): Command-line arguments as a string or list.
234
+ If empty, uses sys.argv. Defaults to "".
235
+
236
+ The function performs the following steps:
237
+ 1. Parse command-line arguments
238
+ 2. Create Cerebro instance with specified parameters
239
+ 3. Add data feeds (with optional resampling/replaying)
240
+ 4. Add signals, strategies, indicators, observers, and analyzers
241
+ 5. Configure broker settings (cash, commission, slippage)
242
+ 6. Add writers for output
243
+ 7. Run the backtest
244
+ 8. Optionally print analyzer results and plot results
245
+ """
246
+ args = parse_args(pargs)
247
+
248
+ if args.flush:
249
+ pass
250
+
251
+ stdstats = not args.nostdstats
252
+
253
+ cer_kwargs_str = args.cerebro
254
+ cer_kwargs = _safe_parse_kwargs(cer_kwargs_str)
255
+ if "stdstats" not in cer_kwargs:
256
+ cer_kwargs.update(stdstats=stdstats)
257
+
258
+ cerebro = Cerebro(**cer_kwargs)
259
+
260
+ _add_datas(args, cerebro)
261
+
262
+ # get and add signals
263
+ signals = getobjects(args.signals, Indicator, signals_module, issignal=True)
264
+ for sig, kwargs, sigtype in signals:
265
+ stype = getattr(signal_module, "SIGNAL_" + sigtype.upper())
266
+ cerebro.add_signal(stype, sig, **kwargs)
267
+
268
+ # get and add strategies
269
+ strategies = getobjects(args.strategies, Strategy, strategies_module)
270
+ for strat, kwargs in strategies:
271
+ cerebro.addstrategy(strat, **kwargs)
272
+
273
+ inds = getobjects(args.indicators, Indicator, indicators_module)
274
+ for ind, kwargs in inds:
275
+ cerebro.addindicator(ind, **kwargs)
276
+
277
+ obs = getobjects(args.observers, Observer, observers_module)
278
+ for ob, kwargs in obs:
279
+ cerebro.addobserver(ob, **kwargs)
280
+
281
+ ans = getobjects(args.analyzers, Analyzer, analyzers_module)
282
+ for an, kwargs in ans:
283
+ cerebro.addanalyzer(an, **kwargs)
284
+
285
+ setbroker(args, cerebro)
286
+
287
+ for wrkwargs_str in args.writers or []:
288
+ wrkwargs = _safe_parse_kwargs(wrkwargs_str)
289
+ cerebro.addwriter(WriterFile, **wrkwargs)
290
+
291
+ ans = getfunctions(args.hooks, Cerebro)
292
+ for hook, kwargs in ans:
293
+ hook(cerebro, **kwargs)
294
+ runsts = cerebro.run()
295
+ runst = runsts[0] # single strategy and no optimization
296
+
297
+ if args.pranalyzer or args.ppranalyzer:
298
+ _print_analyzers(args, runst)
299
+
300
+ if args.plot:
301
+ pkwargs = {"style": "bar"}
302
+ if args.plot is not True:
303
+ # evaluates to True but is not "True" - args were passed
304
+ ekwargs = _safe_parse_kwargs(args.plot)
305
+ pkwargs.update(ekwargs)
306
+
307
+ # cerebro.plot(numfigs=args.plotfigs, style=args.plotstyle)
308
+ cerebro.plot(**pkwargs)
309
+
310
+
311
+ def _add_datas(args, cerebro):
312
+ """Add data feeds to cerebro, honoring --resample / --replay options."""
313
+ tf = cp = None
314
+ if args.resample is not None or args.replay is not None:
315
+ if args.resample is not None:
316
+ tfcp = args.resample.split(":")
317
+ elif args.replay is not None:
318
+ tfcp = args.replay.split(":")
319
+
320
+ # compression may be skipped and it will default to 1
321
+ if len(tfcp) == 1 or tfcp[1] == "":
322
+ tf, cp = tfcp[0], 1
323
+ else:
324
+ tf, cp = tfcp
325
+
326
+ cp = int(cp) # convert any value to int
327
+ tf = TIMEFRAMES.get(tf)
328
+
329
+ for data in getdatas(args):
330
+ if args.resample is not None:
331
+ cerebro.resampledata(data, timeframe=tf, compression=cp)
332
+ elif args.replay is not None:
333
+ cerebro.replaydata(data, timeframe=tf, compression=cp)
334
+ else:
335
+ cerebro.adddata(data)
336
+
337
+
338
+ def _print_analyzers(args, runst):
339
+ """Print analyzer results for the finished strategy (pranalyzer/ppranalyzer)."""
340
+ if not runst.analyzers:
341
+ return
342
+ print("====================")
343
+ print("== Analyzers")
344
+ print("====================")
345
+ for name, analyzer in runst.analyzers.getitems():
346
+ if args.pranalyzer:
347
+ analyzer.print()
348
+ elif args.ppranalyzer:
349
+ print("##########")
350
+ print(name)
351
+ print("##########")
352
+ analyzer.pprint()
353
+
354
+
355
+ def setbroker(args, cerebro):
356
+ """Configure broker settings from parsed command-line arguments.
357
+
358
+ Sets broker cash, commission scheme parameters, and slippage settings
359
+ on the cerebro instance's broker.
360
+
361
+ Args:
362
+ args (argparse.Namespace): Parsed command-line arguments containing
363
+ broker configuration options like cash, commission, margin, mult,
364
+ interest, slippage, etc.
365
+ cerebro (Cerebro): Cerebro instance whose broker will be configured.
366
+
367
+ The following broker settings are configured:
368
+ - Cash: Initial capital via args.cash
369
+ - Commission: Trading commission via args.commission, args.margin, args.mult
370
+ - Interest: Credit interest rate via args.interest, args.interest_long
371
+ - Slippage: Price slippage model via args.slip_perc or args.slip_fixed
372
+ """
373
+ broker = cerebro.getbroker()
374
+
375
+ if args.cash is not None:
376
+ broker.setcash(args.cash)
377
+
378
+ commkwargs = {}
379
+ if args.commission is not None:
380
+ commkwargs["commission"] = args.commission
381
+ if args.margin is not None:
382
+ commkwargs["margin"] = args.margin
383
+ if args.mult is not None:
384
+ commkwargs["mult"] = args.mult
385
+ if args.interest is not None:
386
+ commkwargs["interest"] = args.interest
387
+ if args.interest_long is not None:
388
+ commkwargs["interest_long"] = args.interest_long
389
+
390
+ if commkwargs:
391
+ broker.setcommission(**commkwargs)
392
+
393
+ if args.slip_perc is not None:
394
+ cerebro.broker.set_slippage_perc(
395
+ args.slip_perc,
396
+ slip_open=args.slip_open,
397
+ slip_match=not args.no_slip_match,
398
+ slip_out=args.slip_out,
399
+ )
400
+ elif args.slip_fixed is not None:
401
+ cerebro.broker.set_slippage_fixed(
402
+ args.slip_fixed,
403
+ slip_open=args.slip_open,
404
+ slip_match=not args.no_slip_match,
405
+ slip_out=args.slip_out,
406
+ )
407
+
408
+
409
+ def getdatas(args):
410
+ """Create data feed instances from command-line arguments.
411
+
412
+ Parses the data format, date range, timeframe, and compression settings
413
+ from the arguments, then creates data feed instances for each specified
414
+ data file.
415
+
416
+ Args:
417
+ args (argparse.Namespace): Parsed command-line arguments containing
418
+ data configuration options including:
419
+ - data: List of data file paths
420
+ - format: Data format type (e.g., 'btcsv', 'yahoo')
421
+ - fromdate: Start date filter
422
+ - todate: End date filter
423
+ - timeframe: Timeframe for the data
424
+ - compression: Compression factor
425
+
426
+ Returns:
427
+ list: List of data feed instances ready to be added to Cerebro.
428
+ """
429
+ # Get the data feed class from the global dictionary
430
+ dfcls = DATAFORMATS[args.format]
431
+
432
+ # Prepare some args
433
+ dfkwargs = {}
434
+ if args.format == "yahoo_unreversed":
435
+ dfkwargs["reverse"] = True
436
+
437
+ fmtstr = "%Y-%m-%d"
438
+ if args.fromdate:
439
+ dtsplit = args.fromdate.split("T")
440
+ if len(dtsplit) > 1:
441
+ fmtstr += "T%H:%M:%S"
442
+
443
+ fromdate = datetime.datetime.strptime(args.fromdate, fmtstr)
444
+ dfkwargs["fromdate"] = fromdate
445
+
446
+ fmtstr = "%Y-%m-%d"
447
+ if args.todate:
448
+ dtsplit = args.todate.split("T")
449
+ if len(dtsplit) > 1:
450
+ fmtstr += "T%H:%M:%S"
451
+ todate = datetime.datetime.strptime(args.todate, fmtstr)
452
+ dfkwargs["todate"] = todate
453
+
454
+ if args.timeframe is not None:
455
+ dfkwargs["timeframe"] = TIMEFRAMES[args.timeframe]
456
+
457
+ if args.compression is not None:
458
+ dfkwargs["compression"] = args.compression
459
+
460
+ datas = []
461
+ for dname in args.data:
462
+ dfkwargs["dataname"] = dname
463
+ data = dfcls(**dfkwargs)
464
+ datas.append(data)
465
+
466
+ return datas
467
+
468
+
469
+ def getmodclasses(mod, clstype, clsname=None):
470
+ """Get classes from a module that match a specific type.
471
+
472
+ Searches through a module to find all classes that are subclasses of
473
+ a given type. Optionally filters by class name.
474
+
475
+ Args:
476
+ mod (module): Python module to search for classes.
477
+ clstype (type): Base class type to filter by (e.g., Strategy, Indicator).
478
+ clsname (str, optional): Specific class name to find. If None,
479
+ returns all matching classes. Defaults to None.
480
+
481
+ Returns:
482
+ list: List of class objects that match the criteria. If clsname is
483
+ specified, returns a list with at most one element.
484
+ """
485
+ clsmembers = inspect.getmembers(mod, inspect.isclass)
486
+
487
+ clslist = []
488
+ for name, cls in clsmembers:
489
+ if not issubclass(cls, clstype):
490
+ continue
491
+
492
+ if clsname:
493
+ if clsname == name:
494
+ clslist.append(cls)
495
+ break
496
+ else:
497
+ clslist.append(cls)
498
+
499
+ return clslist
500
+
501
+
502
+ def getmodfunctions(mod, funcname=None):
503
+ """Get functions from a module, optionally filtering by name.
504
+
505
+ Searches through a module to find all functions and methods. Optionally
506
+ filters by function name.
507
+
508
+ Args:
509
+ mod (module): Python module to search for functions.
510
+ funcname (str, optional): Specific function name to find. If None,
511
+ returns all functions and methods. Defaults to None.
512
+
513
+ Returns:
514
+ list: List of function/method objects that match the criteria. If
515
+ funcname is specified, returns a list with at most one element.
516
+ """
517
+ members = inspect.getmembers(mod, inspect.isfunction) + inspect.getmembers(
518
+ mod, inspect.ismethod
519
+ )
520
+
521
+ funclist = []
522
+ for name, member in members:
523
+ if funcname:
524
+ if name == funcname:
525
+ funclist.append(member)
526
+ break
527
+ else:
528
+ funclist.append(member)
529
+
530
+ return funclist
531
+
532
+
533
+ def loadmodule(modpath, modname=""):
534
+ """Load a Python module from a file path.
535
+
536
+ Dynamically loads a Python module from a file path. Supports both Python 2
537
+ and Python 3 using different loading mechanisms. If no module name is provided,
538
+ generates a random 10-character alphanumeric name.
539
+
540
+ Args:
541
+ modpath (str): Path to the Python module file. If it doesn't end with
542
+ '.py', the extension will be automatically appended.
543
+ modname (str, optional): Name to assign to the loaded module. If None or
544
+ empty, a random name is generated. Defaults to "".
545
+
546
+ Returns:
547
+ tuple: A tuple containing:
548
+ - mod (module or None): The loaded module object, or None if loading failed.
549
+ - e (Exception or None): Exception object if loading failed, None otherwise.
550
+ """
551
+ # generate a random name for the module
552
+
553
+ if not modpath.endswith(".py"):
554
+ modpath += ".py"
555
+
556
+ if not modname:
557
+ chars = string.ascii_uppercase + string.digits
558
+ # Module name only needs to be unique, not cryptographically secure.
559
+ # This is an internal import alias, never a security boundary.
560
+ modname = "".join(random.choice(chars) for _ in range(10)) # nosec B311
561
+
562
+ try:
563
+ mod = _load_module_from_path(modpath, modname)
564
+ except Exception as e:
565
+ logger.warning("btrun:564 fallback on Exception")
566
+ return None, e
567
+
568
+ return mod, None
569
+
570
+
571
+ def _load_module_from_path(modpath, modname):
572
+ """Load a Python module from a file path using importlib.
573
+
574
+ Args:
575
+ modpath (str): Path to the Python module file.
576
+ modname (str): Name to assign to the loaded module.
577
+
578
+ Returns:
579
+ module: The loaded module object.
580
+
581
+ Raises:
582
+ Exception: If module loading fails.
583
+ """
584
+ import importlib.util
585
+
586
+ spec = importlib.util.spec_from_file_location(modname, modpath)
587
+ mod = importlib.util.module_from_spec(spec)
588
+ spec.loader.exec_module(mod)
589
+ return mod
590
+
591
+
592
+ def getobjects(iterable, clsbase, modbase, issignal=False):
593
+ """Load and instantiate objects from module specifications.
594
+
595
+ Parses a list of object specifications in the format 'module:name:kwargs',
596
+ loads the corresponding modules, finds the requested classes, and returns
597
+ them with their associated kwargs. Used for strategies, indicators,
598
+ observers, analyzers, and signals.
599
+
600
+ Args:
601
+ iterable (list): List of object specification strings. Each string can be:
602
+ - 'module:name:kwargs' - Load specific class from module with kwargs
603
+ - 'module:name' - Load specific class from module
604
+ - 'module' - Load first matching class from module
605
+ - ':name' - Load class from built-in module (modbase)
606
+ - For signals: 'signaltype+module:name:kwargs'
607
+ clsbase (type): Base class type to filter by (e.g., Strategy, Indicator).
608
+ modbase (module): Default module to use when module path is omitted.
609
+ issignal (bool, optional): Whether processing signals. If True, parses
610
+ signal type prefix (e.g., 'longshort+'). Defaults to False.
611
+
612
+ Returns:
613
+ list: List of tuples containing:
614
+ - For signals: (class, kwargs_dict, signal_type)
615
+ - For others: (class, kwargs_dict)
616
+
617
+ The function will call sys.exit(1) if module loading or class finding fails.
618
+ """
619
+ retobjects: list = []
620
+
621
+ for item in iterable or []:
622
+ if issignal:
623
+ sigtokens = item.split("+", 1)
624
+ if len(sigtokens) == 1: # no + seen
625
+ sigtype = "longshort"
626
+ else:
627
+ sigtype, item = sigtokens
628
+
629
+ tokens = item.split(":", 1)
630
+
631
+ if len(tokens) == 1:
632
+ modpath = tokens[0]
633
+ name = ""
634
+ kwargs: dict = {}
635
+ else:
636
+ modpath, name = tokens
637
+ kwtokens = name.split(":", 1)
638
+ if len(kwtokens) == 1:
639
+ # no '(' found
640
+ kwargs = {}
641
+ else:
642
+ name = kwtokens[0]
643
+ kwargs = _safe_parse_kwargs(kwtokens[1])
644
+
645
+ if modpath:
646
+ mod, e = loadmodule(modpath)
647
+
648
+ if not mod:
649
+ print("")
650
+ print("Failed to load module %s:" % modpath, e)
651
+ sys.exit(1)
652
+ else:
653
+ mod = modbase
654
+
655
+ loaded = getmodclasses(mod=mod, clstype=clsbase, clsname=name)
656
+
657
+ if not loaded:
658
+ print(f"No class {str(name)} / module {modpath}")
659
+ sys.exit(1)
660
+
661
+ if issignal:
662
+ retobjects.append((loaded[0], kwargs, sigtype))
663
+ else:
664
+ retobjects.append((loaded[0], kwargs))
665
+
666
+ return retobjects
667
+
668
+
669
+ def getfunctions(iterable, modbase):
670
+ """Load and retrieve function objects from module specifications.
671
+
672
+ Parses a list of function specifications in the format 'module:name:kwargs',
673
+ loads the corresponding modules, finds the requested functions, and returns
674
+ them with their associated kwargs. Used for cerebro hook functions.
675
+
676
+ Args:
677
+ iterable (list): List of function specification strings. Each string can be:
678
+ - 'module:name:kwargs' - Load specific function from module with kwargs
679
+ - 'module:name' - Load specific function from module
680
+ - 'module' - Load first function from module
681
+ - ':name' - Load built-in cerebro method (e.g., ':addtz')
682
+ modbase (module): Default module to use when module path is omitted
683
+ (typically Cerebro for built-in methods).
684
+
685
+ Returns:
686
+ list: List of tuples containing:
687
+ - (function, kwargs_dict): Function object and its keyword arguments
688
+
689
+ The function will call sys.exit(1) if module loading or function finding fails.
690
+ """
691
+ retfunctions = []
692
+
693
+ for item in iterable or []:
694
+ tokens = item.split(":", 1)
695
+
696
+ if len(tokens) == 1:
697
+ modpath = tokens[0]
698
+ name = ""
699
+ kwargs: dict = {}
700
+ else:
701
+ modpath, name = tokens
702
+ kwtokens = name.split(":", 1)
703
+ if len(kwtokens) == 1:
704
+ # no '(' found
705
+ kwargs = {}
706
+ else:
707
+ name = kwtokens[0]
708
+ kwargs = _safe_parse_kwargs(kwtokens[1])
709
+
710
+ if modpath:
711
+ mod, e = loadmodule(modpath)
712
+
713
+ if not mod:
714
+ print("")
715
+ print("Failed to load module %s:" % modpath, e)
716
+ sys.exit(1)
717
+ else:
718
+ mod = modbase
719
+
720
+ loaded = getmodfunctions(mod=mod, funcname=name)
721
+
722
+ if not loaded:
723
+ print(f"No function {str(name)} / module {modpath}")
724
+ sys.exit(1)
725
+
726
+ retfunctions.append((loaded[0], kwargs))
727
+
728
+ return retfunctions
729
+
730
+
731
+ def parse_args(pargs=""):
732
+ """Parse command-line arguments for the btrun script.
733
+
734
+ Creates an argument parser and defines all command-line options for the
735
+ backtrader runner, including data feeds, strategies, indicators, observers,
736
+ analyzers, signals, broker settings, and output options.
737
+
738
+ Args:
739
+ pargs (str or list, optional): Command-line arguments to parse. If empty
740
+ string, parses from sys.argv. Can be a string or list of strings.
741
+ Defaults to "".
742
+
743
+ Returns:
744
+ argparse.Namespace: Parsed command-line arguments object containing all
745
+ the configuration options for the backtest.
746
+
747
+ The parser defines the following argument groups:
748
+ - Data options: --data, --format, --fromdate, --todate, --timeframe,
749
+ --compression, --resample, --replay
750
+ - Cerebro options: --cerebro, --nostdstats
751
+ - Strategy options: --strategy
752
+ - Signals: --signal
753
+ - Observers and statistics: --observer
754
+ - Analyzers: --analyzer, --pranalyzer, --ppranalyzer
755
+ - Indicators: --indicator
756
+ - Writers: --writer
757
+ - Cash and Commission: --cash, --commission, --margin, --mult,
758
+ --interest, --interest_long
759
+ - Slippage: --slip_perc, --slip_fixed, --slip_open, --no-slip_match,
760
+ --slip_out
761
+ - Output: --flush, --plot
762
+ - Hooks: --hook
763
+ """
764
+ parser = argparse.ArgumentParser(
765
+ description="Backtrader Run Script",
766
+ formatter_class=argparse.RawTextHelpFormatter,
767
+ )
768
+
769
+ group = parser.add_argument_group(title="Data options")
770
+ # Data options
771
+ group.add_argument(
772
+ "--data", "-d", action="append", required=True, help="Data files to be added to the system"
773
+ )
774
+
775
+ group = parser.add_argument_group(title="Cerebro options")
776
+ group.add_argument(
777
+ "--cerebro",
778
+ "-cer",
779
+ metavar="kwargs",
780
+ required=False,
781
+ const="",
782
+ default="",
783
+ nargs="?",
784
+ help=(
785
+ "The argument can be specified with the following form:\n"
786
+ "\n"
787
+ " - kwargs\n"
788
+ "\n"
789
+ ' Example: "preload=True" which set its to True\n'
790
+ "\n"
791
+ "The passed kwargs will be passed directly to the cerebro\n"
792
+ "instance created for the execution\n"
793
+ "\n"
794
+ "The available kwargs to cerebro are:\n"
795
+ " - preload (default: True)\n"
796
+ " - runonce (default: True)\n"
797
+ " - maxcpus (default: None)\n"
798
+ " - stdstats (default: True)\n"
799
+ " - live (default: False)\n"
800
+ " - exactbars (default: False)\n"
801
+ " - preload (default: True)\n"
802
+ " - writer (default False)\n"
803
+ " - oldbuysell (default False)\n"
804
+ " - tradehistory (default False)\n"
805
+ ),
806
+ )
807
+
808
+ group.add_argument(
809
+ "--nostdstats", action="store_true", help="Disable the standard statistics observers"
810
+ )
811
+
812
+ datakeys = list(DATAFORMATS)
813
+ group.add_argument(
814
+ "--format",
815
+ "--csvformat",
816
+ "-c",
817
+ required=False,
818
+ default="btcsv",
819
+ choices=datakeys,
820
+ help="CSV Format",
821
+ )
822
+
823
+ group.add_argument(
824
+ "--fromdate",
825
+ "-f",
826
+ required=False,
827
+ default=None,
828
+ help="Starting date in YYYY-MM-DD[THH:MM:SS] format",
829
+ )
830
+
831
+ group.add_argument(
832
+ "--todate",
833
+ "-t",
834
+ required=False,
835
+ default=None,
836
+ help="Ending date in YYYY-MM-DD[THH:MM:SS] format",
837
+ )
838
+
839
+ group.add_argument(
840
+ "--timeframe",
841
+ "-tf",
842
+ required=False,
843
+ default="days",
844
+ choices=TIMEFRAMES.keys(),
845
+ help="Ending date in YYYY-MM-DD[THH:MM:SS] format",
846
+ )
847
+
848
+ group.add_argument(
849
+ "--compression",
850
+ "-cp",
851
+ required=False,
852
+ default=1,
853
+ type=int,
854
+ help="Ending date in YYYY-MM-DD[THH:MM:SS] format",
855
+ )
856
+
857
+ group = parser.add_mutually_exclusive_group(required=False)
858
+
859
+ group.add_argument(
860
+ "--resample",
861
+ "-rs",
862
+ required=False,
863
+ default=None,
864
+ help="resample with timeframe:compression values",
865
+ )
866
+
867
+ group.add_argument(
868
+ "--replay",
869
+ "-rp",
870
+ required=False,
871
+ default=None,
872
+ help="replay with timeframe:compression values",
873
+ )
874
+
875
+ group.add_argument(
876
+ "--hook",
877
+ dest="hooks",
878
+ action="append",
879
+ required=False,
880
+ metavar="module:hookfunction:kwargs",
881
+ help=(
882
+ "This option can be specified multiple times.\n"
883
+ "\n"
884
+ "The argument can be specified with the following form:\n"
885
+ "\n"
886
+ " - module:hookfunction:kwargs\n"
887
+ "\n"
888
+ " Example: mymod:myhook:a=1,b=2\n"
889
+ "\n"
890
+ "kwargs is optional\n"
891
+ "\n"
892
+ "If module is omitted then hookfunction will be sought\n"
893
+ "as the built-in cerebro method. Example:\n"
894
+ "\n"
895
+ " - :addtz:tz=America/St_Johns\n"
896
+ "\n"
897
+ "If name is omitted, then the 1st function found in the\n"
898
+ "mod will be used. Such as in:\n"
899
+ "\n"
900
+ " - module or module::kwargs\n"
901
+ "\n"
902
+ "The function specified will be called, with cerebro\n"
903
+ "instance passed as the first argument together with\n"
904
+ "kwargs, if any were specified. This allows to customize\n"
905
+ "cerebro, beyond options provided by this script\n\n"
906
+ ),
907
+ )
908
+
909
+ # Module where to read the strategy from
910
+ group = parser.add_argument_group(title="Strategy options")
911
+ group.add_argument(
912
+ "--strategy",
913
+ "-st",
914
+ dest="strategies",
915
+ action="append",
916
+ required=False,
917
+ metavar="module:name:kwargs",
918
+ help=(
919
+ "This option can be specified multiple times.\n"
920
+ "\n"
921
+ "The argument can be specified with the following form:\n"
922
+ "\n"
923
+ " - module:classname:kwargs\n"
924
+ "\n"
925
+ " Example: mymod:myclass:a=1,b=2\n"
926
+ "\n"
927
+ "kwargs is optional\n"
928
+ "\n"
929
+ "If module is omitted then class name will be sought in\n"
930
+ "the built-in strategies module. Such as in:\n"
931
+ "\n"
932
+ " - :name:kwargs or :name\n"
933
+ "\n"
934
+ "If name is omitted, then the 1st strategy found in the mod\n"
935
+ "will be used. Such as in:\n"
936
+ "\n"
937
+ " - module or module::kwargs"
938
+ ),
939
+ )
940
+
941
+ # Module where to read the strategy from
942
+ group = parser.add_argument_group(title="Signals")
943
+ group.add_argument(
944
+ "--signal",
945
+ "-sig",
946
+ dest="signals",
947
+ action="append",
948
+ required=False,
949
+ metavar="module:signaltype:name:kwargs",
950
+ help=(
951
+ "This option can be specified multiple times.\n"
952
+ "\n"
953
+ "The argument can be specified with the following form:\n"
954
+ "\n"
955
+ " - signaltype:module:signaltype:classname:kwargs\n"
956
+ "\n"
957
+ " Example: longshort+mymod:myclass:a=1,b=2\n"
958
+ "\n"
959
+ "signaltype may be ommited: longshort will be used\n"
960
+ "\n"
961
+ " Example: mymod:myclass:a=1,b=2\n"
962
+ "\n"
963
+ "kwargs is optional\n"
964
+ "\n"
965
+ "signaltype will be uppercased to match the defintions\n"
966
+ "fromt the backtrader.signal module\n"
967
+ "\n"
968
+ "If module is omitted then class name will be sought in\n"
969
+ "the built-in signals module. Such as in:\n"
970
+ "\n"
971
+ " - LONGSHORT::name:kwargs or :name\n"
972
+ "\n"
973
+ "If name is omitted, then the 1st signal found in the mod\n"
974
+ "will be used. Such as in:\n"
975
+ "\n"
976
+ " - module or module:::kwargs"
977
+ ),
978
+ )
979
+
980
+ # Observers
981
+ group = parser.add_argument_group(title="Observers and statistics")
982
+ group.add_argument(
983
+ "--observer",
984
+ "-ob",
985
+ dest="observers",
986
+ action="append",
987
+ required=False,
988
+ metavar="module:name:kwargs",
989
+ help=(
990
+ "This option can be specified multiple times.\n"
991
+ "\n"
992
+ "The argument can be specified with the following form:\n"
993
+ "\n"
994
+ " - module:classname:kwargs\n"
995
+ "\n"
996
+ " Example: mymod:myclass:a=1,b=2\n"
997
+ "\n"
998
+ "kwargs is optional\n"
999
+ "\n"
1000
+ "If module is omitted then class name will be sought in\n"
1001
+ "the built-in observers module. Such as in:\n"
1002
+ "\n"
1003
+ " - :name:kwargs or :name\n"
1004
+ "\n"
1005
+ "If name is omitted, then the 1st observer found in the\n"
1006
+ "will be used. Such as in:\n"
1007
+ "\n"
1008
+ " - module or module::kwargs"
1009
+ ),
1010
+ )
1011
+ # Analyzers
1012
+ group = parser.add_argument_group(title="Analyzers")
1013
+ group.add_argument(
1014
+ "--analyzer",
1015
+ "-an",
1016
+ dest="analyzers",
1017
+ action="append",
1018
+ required=False,
1019
+ metavar="module:name:kwargs",
1020
+ help=(
1021
+ "This option can be specified multiple times.\n"
1022
+ "\n"
1023
+ "The argument can be specified with the following form:\n"
1024
+ "\n"
1025
+ " - module:classname:kwargs\n"
1026
+ "\n"
1027
+ " Example: mymod:myclass:a=1,b=2\n"
1028
+ "\n"
1029
+ "kwargs is optional\n"
1030
+ "\n"
1031
+ "If module is omitted then class name will be sought in\n"
1032
+ "the built-in analyzers module. Such as in:\n"
1033
+ "\n"
1034
+ " - :name:kwargs or :name\n"
1035
+ "\n"
1036
+ "If name is omitted, then the 1st analyzer found in the\n"
1037
+ "will be used. Such as in:\n"
1038
+ "\n"
1039
+ " - module or module::kwargs"
1040
+ ),
1041
+ )
1042
+
1043
+ # Analyzer - Print
1044
+ group = parser.add_mutually_exclusive_group(required=False)
1045
+ group.add_argument(
1046
+ "--pranalyzer",
1047
+ "-pralyzer",
1048
+ required=False,
1049
+ action="store_true",
1050
+ help="Automatically print analyzers",
1051
+ )
1052
+
1053
+ group.add_argument(
1054
+ "--ppranalyzer",
1055
+ "-ppralyzer",
1056
+ required=False,
1057
+ action="store_true",
1058
+ help="Automatically PRETTY print analyzers",
1059
+ )
1060
+
1061
+ # Indicators
1062
+ group = parser.add_argument_group(title="Indicators")
1063
+ group.add_argument(
1064
+ "--indicator",
1065
+ "-ind",
1066
+ dest="indicators",
1067
+ metavar="module:name:kwargs",
1068
+ action="append",
1069
+ required=False,
1070
+ help=(
1071
+ "This option can be specified multiple times.\n"
1072
+ "\n"
1073
+ "The argument can be specified with the following form:\n"
1074
+ "\n"
1075
+ " - module:classname:kwargs\n"
1076
+ "\n"
1077
+ " Example: mymod:myclass:a=1,b=2\n"
1078
+ "\n"
1079
+ "kwargs is optional\n"
1080
+ "\n"
1081
+ "If module is omitted then class name will be sought in\n"
1082
+ "the built-in analyzers module. Such as in:\n"
1083
+ "\n"
1084
+ " - :name:kwargs or :name\n"
1085
+ "\n"
1086
+ "If name is omitted, then the 1st analyzer found in the\n"
1087
+ "will be used. Such as in:\n"
1088
+ "\n"
1089
+ " - module or module::kwargs"
1090
+ ),
1091
+ )
1092
+
1093
+ # Writer
1094
+ group = parser.add_argument_group(title="Writers")
1095
+ group.add_argument(
1096
+ "--writer",
1097
+ "-wr",
1098
+ dest="writers",
1099
+ metavar="kwargs",
1100
+ nargs="?",
1101
+ action="append",
1102
+ required=False,
1103
+ const="",
1104
+ help=(
1105
+ "This option can be specified multiple times.\n"
1106
+ "\n"
1107
+ "The argument can be specified with the following form:\n"
1108
+ "\n"
1109
+ " - kwargs\n"
1110
+ "\n"
1111
+ " Example: a=1,b=2\n"
1112
+ "\n"
1113
+ "kwargs is optional\n"
1114
+ "\n"
1115
+ "It creates a system wide writer which outputs run data\n"
1116
+ "\n"
1117
+ "Please see the documentation for the available kwargs"
1118
+ ),
1119
+ )
1120
+
1121
+ # Broker/Commissions
1122
+ group = parser.add_argument_group(title="Cash and Commission Scheme Args")
1123
+ group.add_argument(
1124
+ "--cash", "-cash", required=False, type=float, help="Cash to set to the broker"
1125
+ )
1126
+ group.add_argument(
1127
+ "--commission", "-comm", required=False, type=float, help="Commission value to set"
1128
+ )
1129
+ group.add_argument("--margin", "-marg", required=False, type=float, help="Margin type to set")
1130
+ group.add_argument("--mult", "-mul", required=False, type=float, help="Multiplier to use")
1131
+
1132
+ group.add_argument(
1133
+ "--interest",
1134
+ required=False,
1135
+ type=float,
1136
+ default=None,
1137
+ help="Credit Interest rate to apply (0.0x)",
1138
+ )
1139
+
1140
+ group.add_argument(
1141
+ "--interest_long",
1142
+ action="store_true",
1143
+ required=False,
1144
+ default=None,
1145
+ help="Apply credit interest to long positions",
1146
+ )
1147
+
1148
+ group.add_argument(
1149
+ "--slip_perc",
1150
+ required=False,
1151
+ default=None,
1152
+ type=float,
1153
+ help="Enable slippage with a percentage value",
1154
+ )
1155
+ group.add_argument(
1156
+ "--slip_fixed",
1157
+ required=False,
1158
+ default=None,
1159
+ type=float,
1160
+ help="Enable slippage with a fixed point value",
1161
+ )
1162
+
1163
+ group.add_argument(
1164
+ "--slip_open",
1165
+ required=False,
1166
+ action="store_true",
1167
+ help="enable slippage for when matching opening prices",
1168
+ )
1169
+
1170
+ group.add_argument(
1171
+ "--no-slip_match",
1172
+ required=False,
1173
+ action="store_true",
1174
+ help=(
1175
+ "Disable slip_match, ie: matching capped at \nhigh-low if slippage goes over those limits"
1176
+ ),
1177
+ )
1178
+ group.add_argument(
1179
+ "--slip_out",
1180
+ required=False,
1181
+ action="store_true",
1182
+ help="with slip_match enabled, match outside high-low",
1183
+ )
1184
+
1185
+ # Output flushing
1186
+ group.add_argument(
1187
+ "--flush",
1188
+ required=False,
1189
+ action="store_true",
1190
+ help="flush the output - useful under win32 systems",
1191
+ )
1192
+
1193
+ # Plot options
1194
+ parser.add_argument(
1195
+ "--plot",
1196
+ "-p",
1197
+ nargs="?",
1198
+ metavar="kwargs",
1199
+ default=False,
1200
+ const=True,
1201
+ required=False,
1202
+ help=(
1203
+ "Plot the read data applying any kwargs passed\n"
1204
+ "\n"
1205
+ "For example:\n"
1206
+ "\n"
1207
+ ' --plot style="candle" (to plot candlesticks)\n'
1208
+ ),
1209
+ )
1210
+
1211
+ if pargs:
1212
+ return parser.parse_args(pargs)
1213
+
1214
+ return parser.parse_args()
1215
+
1216
+
1217
+ if __name__ == "__main__":
1218
+ btrun()