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,120 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Analyzer tab.
4
+
5
+ Displays results of all analyzers.
6
+ """
7
+
8
+ from ...utils.log_message import get_logger
9
+ from ..tab import BokehTab
10
+
11
+ logger = get_logger(__name__)
12
+
13
+ try:
14
+ from bokeh.layouts import column
15
+ from bokeh.models import ColumnDataSource, DataTable, TableColumn
16
+ from bokeh.models.widgets import Div
17
+
18
+ BOKEH_AVAILABLE = True
19
+ except ImportError:
20
+ BOKEH_AVAILABLE = False
21
+
22
+
23
+ class AnalyzerTab(BokehTab):
24
+ """Analyzer tab.
25
+
26
+ Displays analysis results of all analyzers in the strategy.
27
+ """
28
+
29
+ def _is_useable(self):
30
+ """Check if useable.
31
+
32
+ Useable when strategy has analyzers.
33
+ """
34
+ if not BOKEH_AVAILABLE:
35
+ return False
36
+ strategy = self.strategy
37
+ if strategy is None:
38
+ return False
39
+ return len(getattr(strategy, "analyzers", [])) > 0
40
+
41
+ def _get_panel(self):
42
+ """Get panel content.
43
+
44
+ Returns:
45
+ tuple: (widget, title)
46
+ """
47
+ strategy = self.strategy
48
+ scheme = self.scheme
49
+
50
+ # Create analyzer results display
51
+ widgets = []
52
+
53
+ for analyzer in strategy.analyzers:
54
+ analyzer_name = analyzer.__class__.__name__
55
+
56
+ # Get analysis results
57
+ try:
58
+ analysis = analyzer.get_analysis()
59
+ except Exception:
60
+ logger.warning("analyzer:56 fallback on Exception")
61
+ analysis = {}
62
+
63
+ # Create title
64
+ title_div = Div(
65
+ text=f'<h3 style="color: {scheme.text_color if scheme else "#333"};">{analyzer_name}</h3>',
66
+ sizing_mode="stretch_width",
67
+ )
68
+ widgets.append(title_div)
69
+
70
+ # Convert analysis results to table data
71
+ data = self._flatten_analysis(analysis)
72
+
73
+ if data:
74
+ source = ColumnDataSource(
75
+ data={"key": list(data.keys()), "value": [str(v) for v in data.values()]}
76
+ )
77
+
78
+ columns = [
79
+ TableColumn(field="key", title="Metric"),
80
+ TableColumn(field="value", title="Value"),
81
+ ]
82
+
83
+ table = DataTable(
84
+ source=source,
85
+ columns=columns,
86
+ width=400,
87
+ height=min(len(data) * 25 + 30, 300),
88
+ index_position=None,
89
+ )
90
+ widgets.append(table)
91
+ else:
92
+ empty_div = Div(text="<p>No data available</p>")
93
+ widgets.append(empty_div)
94
+
95
+ content = column(*widgets, sizing_mode="stretch_width")
96
+ return content, "Analyzers"
97
+
98
+ def _flatten_analysis(self, analysis, prefix=""):
99
+ """Flatten nested analysis results.
100
+
101
+ Args:
102
+ analysis: Analysis result dictionary
103
+ prefix: Key prefix
104
+
105
+ Returns:
106
+ dict: Flattened dictionary
107
+ """
108
+ result = {}
109
+
110
+ if isinstance(analysis, dict):
111
+ for key, value in analysis.items():
112
+ new_key = f"{prefix}.{key}" if prefix else str(key)
113
+ if isinstance(value, dict):
114
+ result.update(self._flatten_analysis(value, new_key))
115
+ else:
116
+ result[new_key] = value
117
+ else:
118
+ result[prefix or "value"] = analysis
119
+
120
+ return result
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Configuration tab.
4
+
5
+ Displays configuration info for strategy and data.
6
+ """
7
+
8
+ from backtrader.utils.log_message import get_logger
9
+
10
+ from ..tab import BokehTab
11
+
12
+ logger = get_logger(__name__)
13
+
14
+ try:
15
+ from bokeh.layouts import column
16
+ from bokeh.models import ColumnDataSource, DataTable, TableColumn
17
+ from bokeh.models.widgets import Div
18
+
19
+ BOKEH_AVAILABLE = True
20
+ except ImportError:
21
+ BOKEH_AVAILABLE = False
22
+
23
+
24
+ class ConfigTab(BokehTab):
25
+ """Configuration tab.
26
+
27
+ Displays strategy parameters, data configuration and other info.
28
+ """
29
+
30
+ def _is_useable(self):
31
+ """Config tab is always useable."""
32
+ return BOKEH_AVAILABLE
33
+
34
+ def _get_panel(self):
35
+ """Get panel content.
36
+
37
+ Returns:
38
+ tuple: (widget, title)
39
+ """
40
+ strategy = self.strategy
41
+ scheme = self.scheme
42
+
43
+ widgets = []
44
+ text_color = scheme.text_color if scheme else "#333"
45
+
46
+ # Strategy parameters + data feeds
47
+ if strategy is not None:
48
+ widgets.extend(self._build_params_widgets(strategy, text_color))
49
+ widgets.extend(self._build_data_widgets(strategy, text_color))
50
+
51
+ if not widgets:
52
+ widgets.append(Div(text="<p>No configuration available</p>"))
53
+
54
+ content = column(*widgets, sizing_mode="stretch_width")
55
+ return content, "Config"
56
+
57
+ def _build_params_widgets(self, strategy, text_color):
58
+ """Build the strategy-name heading + parameters table widgets."""
59
+ widgets = []
60
+ # Strategy name
61
+ strategy_name = strategy.__class__.__name__
62
+ widgets.append(
63
+ Div(
64
+ text=f'<h3 style="color: {text_color};">Strategy: {strategy_name}</h3>',
65
+ sizing_mode="stretch_width",
66
+ )
67
+ )
68
+
69
+ # Strategy parameters
70
+ params = {}
71
+ if hasattr(strategy, "params"):
72
+ for name in dir(strategy.params):
73
+ if not name.startswith("_"):
74
+ try:
75
+ value = getattr(strategy.params, name)
76
+ if not callable(value):
77
+ params[name] = value
78
+ except Exception as e:
79
+ logger.warning("Failed to get param '%s': %s", name, e)
80
+
81
+ if params:
82
+ source = ColumnDataSource(
83
+ data={
84
+ "parameter": list(params.keys()),
85
+ "value": [str(v) for v in params.values()],
86
+ }
87
+ )
88
+
89
+ columns = [
90
+ TableColumn(field="parameter", title="Parameter"),
91
+ TableColumn(field="value", title="Value"),
92
+ ]
93
+
94
+ table = DataTable(
95
+ source=source,
96
+ columns=columns,
97
+ width=400,
98
+ height=min(len(params) * 25 + 30, 300),
99
+ index_position=None,
100
+ )
101
+ widgets.append(table)
102
+ return widgets
103
+
104
+ def _build_data_widgets(self, strategy, text_color):
105
+ """Build the data-feeds heading + data-info table widgets."""
106
+ widgets = []
107
+ if hasattr(strategy, "datas") and strategy.datas:
108
+ widgets.append(
109
+ Div(
110
+ text=f'<h3 style="color: {text_color};">Data Feeds</h3>',
111
+ sizing_mode="stretch_width",
112
+ )
113
+ )
114
+
115
+ data_info = []
116
+ for i, data in enumerate(strategy.datas):
117
+ name = data._name if hasattr(data, "_name") else f"Data{i}"
118
+ length = len(data) if hasattr(data, "__len__") else "N/A"
119
+ data_info.append(
120
+ {
121
+ "name": name,
122
+ "length": str(length),
123
+ "timeframe": str(
124
+ getattr(data._timeframe, "name", "N/A")
125
+ if hasattr(data, "_timeframe")
126
+ else "N/A"
127
+ ),
128
+ }
129
+ )
130
+
131
+ if data_info:
132
+ source = ColumnDataSource(
133
+ data={
134
+ "name": [d["name"] for d in data_info],
135
+ "length": [d["length"] for d in data_info],
136
+ "timeframe": [d["timeframe"] for d in data_info],
137
+ }
138
+ )
139
+
140
+ columns = [
141
+ TableColumn(field="name", title="Name"),
142
+ TableColumn(field="length", title="Length"),
143
+ TableColumn(field="timeframe", title="Timeframe"),
144
+ ]
145
+
146
+ table = DataTable(
147
+ source=source,
148
+ columns=columns,
149
+ width=400,
150
+ height=min(len(data_info) * 25 + 30, 200),
151
+ index_position=None,
152
+ )
153
+ widgets.append(table)
154
+ return widgets
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Live configuration tab.
4
+ For configuration adjustments in live plotting mode.
5
+ """
6
+
7
+ from ..tab import BokehTab
8
+
9
+ try:
10
+ from bokeh.layouts import column
11
+ from bokeh.models.widgets import Button, Div, Select, Slider
12
+
13
+ BOKEH_AVAILABLE = True
14
+ except ImportError:
15
+ BOKEH_AVAILABLE = False
16
+
17
+
18
+ class LiveTab(BokehTab):
19
+ """Live configuration tab.
20
+
21
+ Provides configuration options for live plotting mode:
22
+ - Lookback adjustment
23
+ - Auto-refresh interval
24
+ - Filter options
25
+ """
26
+
27
+ def _is_useable(self):
28
+ """Useable in live mode."""
29
+ if not BOKEH_AVAILABLE:
30
+ return False
31
+ return self._client is not None
32
+
33
+ def _get_panel(self):
34
+ """Get panel content.
35
+
36
+ Returns:
37
+ tuple: (widget, title)
38
+ """
39
+ scheme = self.scheme
40
+ text_color = scheme.text_color if scheme else "#333"
41
+
42
+ widgets = []
43
+
44
+ # Title
45
+ widgets.append(
46
+ Div(
47
+ text=f'<h3 style="color: {text_color};">Live Plot Settings</h3>',
48
+ sizing_mode="stretch_width",
49
+ )
50
+ )
51
+
52
+ # Lookback settings
53
+ lookback_value = getattr(self._client, "lookback", 100) if self._client else 100
54
+ lookback_slider = Slider(
55
+ start=10, end=500, value=lookback_value, step=10, title="Lookback (candles)", width=300
56
+ )
57
+
58
+ def on_lookback_change(attr, old, new):
59
+ if self._client is not None:
60
+ self._client.lookback = new
61
+
62
+ lookback_slider.on_change("value", on_lookback_change)
63
+ widgets.append(lookback_slider)
64
+
65
+ # Fill Gaps option
66
+ fill_gaps = getattr(self._client, "fill_gaps", False) if self._client else False
67
+ fill_gaps_select = Select(
68
+ title="Fill Gaps", value="Yes" if fill_gaps else "No", options=["Yes", "No"], width=150
69
+ )
70
+
71
+ def on_fill_gaps_change(attr, old, new):
72
+ if self._client is not None:
73
+ self._client.fill_gaps = new == "Yes"
74
+
75
+ fill_gaps_select.on_change("value", on_fill_gaps_change)
76
+ widgets.append(fill_gaps_select)
77
+
78
+ # Plot Group settings
79
+ plotgroup = getattr(self._client, "plotgroup", "") if self._client else ""
80
+ plotgroup_select = Select(
81
+ title="Plot Group",
82
+ value=plotgroup or "All",
83
+ options=["All"], # Can dynamically add more options
84
+ width=200,
85
+ )
86
+ widgets.append(plotgroup_select)
87
+
88
+ # Refresh button
89
+ refresh_btn = Button(label="Refresh", button_type="primary", width=100)
90
+
91
+ def on_refresh_click():
92
+ if self._client is not None:
93
+ self._client.updatemodel()
94
+
95
+ refresh_btn.on_click(on_refresh_click)
96
+ widgets.append(refresh_btn)
97
+
98
+ # Status info
99
+ status_text = (
100
+ "Running" if self._client and not getattr(self._client, "_paused", True) else "Paused"
101
+ )
102
+ status_div = Div(
103
+ text=f'<p style="color: {text_color};">Status: <strong>{status_text}</strong></p>',
104
+ sizing_mode="stretch_width",
105
+ )
106
+ widgets.append(status_div)
107
+
108
+ content = column(*widgets, sizing_mode="stretch_width")
109
+ return content, "Live"
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Log tab.
4
+
5
+ Displays strategy execution logs.
6
+ """
7
+
8
+ import logging
9
+ from collections import deque
10
+
11
+ from ..tab import BokehTab
12
+
13
+ try:
14
+ from bokeh.layouts import column
15
+ from bokeh.models import ColumnDataSource, DataTable, TableColumn
16
+ from bokeh.models.widgets import Div
17
+
18
+ BOKEH_AVAILABLE = True
19
+ except ImportError:
20
+ BOKEH_AVAILABLE = False
21
+
22
+ # Global log storage
23
+ _log_storage: dict = {}
24
+
25
+
26
+ class LogHandler(logging.Handler):
27
+ """Log handler.
28
+
29
+ Captures log messages and stores them in specified storage.
30
+ """
31
+
32
+ def __init__(self, storage_key, max_records=1000):
33
+ """Initialize log handler.
34
+
35
+ Args:
36
+ storage_key: Key to identify log storage in global dictionary.
37
+ max_records: Maximum number of log records to keep (default: 1000).
38
+ """
39
+ super().__init__()
40
+ self.storage_key = storage_key
41
+ self.max_records = max_records
42
+ if storage_key not in _log_storage:
43
+ _log_storage[storage_key] = deque(maxlen=max_records)
44
+
45
+ def emit(self, record):
46
+ """Emit a log record.
47
+
48
+ Args:
49
+ record: Log record to process.
50
+ """
51
+ log_entry = {
52
+ "time": self.format(record).split(" - ")[0] if " - " in self.format(record) else "",
53
+ "level": record.levelname,
54
+ "message": record.getMessage(),
55
+ }
56
+ _log_storage[self.storage_key].append(log_entry)
57
+
58
+
59
+ def getlogger(name="backtrader", col=None):
60
+ """Get logger with log handler.
61
+
62
+ Args:
63
+ name: Logger name
64
+ col: Custom columns (optional)
65
+
66
+ Returns:
67
+ logging.Logger instance
68
+ """
69
+ logger = logging.getLogger(name)
70
+
71
+ # Check if LogHandler already added
72
+ has_handler = any(isinstance(h, LogHandler) for h in logger.handlers)
73
+ if not has_handler:
74
+ handler = LogHandler(name)
75
+ handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
76
+ logger.addHandler(handler)
77
+
78
+ return logger
79
+
80
+
81
+ class LogTab(BokehTab):
82
+ """Log tab.
83
+
84
+ Displays log information during strategy execution.
85
+
86
+ Attributes:
87
+ cols: Column configuration for display
88
+ """
89
+
90
+ cols = ["Time", "Level", "Message"] # Default columns
91
+
92
+ def __init__(self, app, figurepage, client=None, cols=None):
93
+ """Initialize log tab.
94
+
95
+ Args:
96
+ app: Bokeh application instance.
97
+ figurepage: Figure page instance.
98
+ client: Optional client instance.
99
+ cols: Custom column configuration for log display.
100
+ """
101
+ super().__init__(app, figurepage, client)
102
+ if cols is not None:
103
+ self.cols = cols
104
+
105
+ def _is_useable(self):
106
+ """Log tab is always useable."""
107
+ return BOKEH_AVAILABLE
108
+
109
+ def _get_panel(self):
110
+ """Get panel content.
111
+
112
+ Returns:
113
+ tuple: (widget, title)
114
+ """
115
+ scheme = self.scheme
116
+ text_color = scheme.text_color if scheme else "#333"
117
+
118
+ widgets = []
119
+
120
+ # Title
121
+ widgets.append(
122
+ Div(
123
+ text=f'<h3 style="color: {text_color};">Log Messages</h3>',
124
+ sizing_mode="stretch_width",
125
+ )
126
+ )
127
+
128
+ # Get log data
129
+ log_data = self._get_log_data()
130
+
131
+ if log_data:
132
+ # Create data source
133
+ source = ColumnDataSource(data=log_data)
134
+
135
+ # Create columns
136
+ columns = []
137
+ for col_name in log_data.keys():
138
+ columns.append(TableColumn(field=col_name, title=col_name.capitalize()))
139
+
140
+ # Create table
141
+ table = DataTable(
142
+ source=source, columns=columns, width=800, height=400, index_position=None
143
+ )
144
+ widgets.append(table)
145
+ else:
146
+ widgets.append(Div(text="<p>No log messages available</p>"))
147
+
148
+ content = column(*widgets, sizing_mode="stretch_width")
149
+ return content, "Log"
150
+
151
+ def _get_log_data(self):
152
+ """Get log data.
153
+
154
+ Returns:
155
+ dict: Log data dictionary
156
+ """
157
+ # Get data from global log storage
158
+ all_logs = []
159
+ for key, logs in _log_storage.items():
160
+ all_logs.extend(list(logs))
161
+
162
+ if not all_logs:
163
+ return None
164
+
165
+ # Sort by time (newest first)
166
+ all_logs = list(reversed(all_logs))
167
+
168
+ # Convert to column data format
169
+ return {
170
+ "time": [log.get("time", "") for log in all_logs],
171
+ "level": [log.get("level", "") for log in all_logs],
172
+ "message": [log.get("message", "") for log in all_logs],
173
+ }
174
+
175
+
176
+ def LogTabs(cols):
177
+ """Create log tab class with custom columns.
178
+
179
+ Args:
180
+ cols: Column configuration
181
+
182
+ Returns:
183
+ Custom LogTab class
184
+ """
185
+ return type("LogTab", (LogTab,), {"cols": cols})