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
backtrader/lineroot.py ADDED
@@ -0,0 +1,1106 @@
1
+ #!/usr/bin/env python
2
+ """LineRoot Module - Base classes for line-based data structures.
3
+
4
+ This module defines the base class LineRoot and derived classes LineSingle
5
+ and LineMultiple that provide the foundation and interfaces for all
6
+ line-based objects in backtrader.
7
+
8
+ Key Classes:
9
+ LineRoot: Common base for all line objects with period management.
10
+ LineSingle: Base for single-line objects.
11
+ LineMultiple: Base for multi-line objects.
12
+ LineRootMixin: Mixin providing owner-finding functionality.
13
+
14
+ The module provides:
15
+ - Period management (minperiod)
16
+ - Iteration management
17
+ - Operation management (binary/unary operations)
18
+ - Rich comparison operators
19
+
20
+ Example:
21
+ Period management:
22
+ >>> obj.setminperiod(20) # Set minimum period to 20
23
+ >>> obj.updateminperiod(30) # Update to max(current, 30)
24
+ """
25
+
26
+ import operator
27
+
28
+ from . import metabase
29
+ from .utils.log_message import get_logger, throttled_warning
30
+ from .utils.py3 import range
31
+
32
+ logger = get_logger(__name__)
33
+
34
+
35
+ class LineRootMixin:
36
+ """Mixin to provide LineRoot functionality without metaclass"""
37
+
38
+ @classmethod
39
+ def donew(cls, *args, **kwargs):
40
+ """Create new instance with owner finding logic"""
41
+ _obj, args, kwargs = (
42
+ super().donew(*args, **kwargs)
43
+ if hasattr(super(), "donew")
44
+ else (cls.__new__(cls), args, kwargs)
45
+ )
46
+
47
+ # Find the owner and store it
48
+ # startlevel = 4 ... to skip intermediate call stacks
49
+ ownerskip = kwargs.pop("_ownerskip", None)
50
+ # Import LineMultiple here to avoid circular imports
51
+ from .lineroot import LineMultiple
52
+
53
+ _obj._owner = metabase.findowner(_obj, _obj._OwnerCls or LineMultiple, skip=ownerskip)
54
+
55
+ # Parameter values have now been set before __init__
56
+ return _obj, args, kwargs
57
+
58
+
59
+ class LineRoot(LineRootMixin, metabase.BaseMixin):
60
+ """
61
+ Defines a common base and interfaces for Single and Multiple
62
+ LineXXX instances
63
+
64
+ Period management
65
+ Iteration management
66
+ Operation (dual/single operand) Management
67
+ Rich Comparison operator definition
68
+ """
69
+
70
+ # Class attributes during initialization
71
+ _OwnerCls = None # Default parent instance is None
72
+ _minperiod = 1 # Minimum period is 1
73
+ _opstage = 1 # Operation state defaults to 1
74
+
75
+ # Indicator type, strategy type, and observer type values are 0, 1, 2 respectively
76
+ IndType, StratType, ObsType = range(3)
77
+
78
+ # Change operation state to 1
79
+ def _stage1(self):
80
+ self._opstage = 1
81
+
82
+ # Change operation state to 2
83
+ def _stage2(self):
84
+ self._opstage = 2
85
+
86
+ # Decide which operation algorithm to call based on line operation state
87
+ def _operation(self, other, operation, r=False, intify=False):
88
+ if self._opstage == 1:
89
+ return self._operation_stage1(other, operation, r=r, intify=intify)
90
+
91
+ return self._operation_stage2(other, operation, r=r)
92
+
93
+ # Self operation
94
+ def _operationown(self, operation):
95
+ if self._opstage == 1:
96
+ return self._operationown_stage1(operation)
97
+
98
+ return self._operationown_stage2(operation)
99
+
100
+ # Change lines to implement minimum buffer scheme
101
+ def qbuffer(self, savemem=0):
102
+ """Change the lines to implement a minimum size qbuffer scheme"""
103
+ raise NotImplementedError
104
+
105
+ # Minimum buffer required
106
+ def minbuffer(self, size):
107
+ """Receive notification of how large the buffer must at least be"""
108
+ raise NotImplementedError
109
+
110
+ # Can be used to set minimum period in strategy, can start running without waiting for indicators to produce specific values
111
+ def setminperiod(self, minperiod):
112
+ """
113
+ Direct minperiod manipulation.It could be used, for example,
114
+ by a strategy
115
+ to not wait for all indicators to produce a value
116
+
117
+ """
118
+ self._minperiod = minperiod
119
+
120
+ # Update minimum period, minimum period may have been calculated elsewhere, compare with existing minimum period, choose the largest one as minimum period
121
+ def updateminperiod(self, minperiod):
122
+ """
123
+ Update the minperiod if needed. The minperiod will have been
124
+ calculated elsewhere
125
+ and has to take over if greater that self's
126
+ """
127
+ self._minperiod = max(self._minperiod, minperiod)
128
+
129
+ # Add minimum period
130
+ def addminperiod(self, minperiod):
131
+ """
132
+ Add a minperiod to own ... to be defined by subclasses
133
+ """
134
+ raise NotImplementedError
135
+
136
+ # Increase minimum period
137
+ def incminperiod(self, minperiod):
138
+ """
139
+ Increment the minperiod with no considerations
140
+ """
141
+ raise NotImplementedError
142
+
143
+ # This function will be called during iteration within minimum period
144
+ def prenext(self):
145
+ """
146
+ It will be called during the "minperiod" phase of an iteration.
147
+ """
148
+
149
+ # Called once when minimum period iteration ends, about to start next
150
+ def nextstart(self):
151
+ """
152
+ It will be called when the minperiod phase is over for the 1st
153
+ post-minperiod value. Only called once and defaults to automatically
154
+ calling next
155
+ """
156
+ self.next()
157
+
158
+ # Start calling next after minimum period iteration ends
159
+ def next(self):
160
+ """
161
+ Called to calculate values when the minperiod is over
162
+ """
163
+
164
+ # Call preonce during minimum period iteration
165
+ def preonce(self, start, end):
166
+ """
167
+ It will be called during the "minperiod" phase of a "once" iteration
168
+ """
169
+
170
+ # Run once when minimum period ends, call once
171
+ def oncestart(self, start, end):
172
+ """
173
+ It will be called when the minperiod phase is over for the 1st
174
+ post-minperiod value
175
+
176
+ Only called once and defaults to automatically calling once
177
+
178
+ """
179
+ self.once(start, end)
180
+
181
+ # Called to calculate results when minimum period iteration ends
182
+ def once(self, start, end):
183
+ """
184
+ Called to calculate values at "once" when the minperiod is over
185
+
186
+ """
187
+
188
+ def size(self):
189
+ """Return the number of lines in this object"""
190
+ # This method provides a size() interface for all LineRoot objects
191
+ # It will be overridden by specific implementations as needed
192
+ if hasattr(self, "lines") and hasattr(self.lines, "size"):
193
+ return self.lines.size()
194
+ if hasattr(self, "lines") and hasattr(self.lines, "__len__"):
195
+ return len(self.lines)
196
+ return 1 # Default to 1 line if no lines object available
197
+
198
+ # Arithmetic operators
199
+ # Some arithmetic operations
200
+ def _makeoperation(self, other, operation, r=False, _ownerskip=None, original_other=None):
201
+ # For LineMultiple, we can implement a basic operation using the first line
202
+ # This provides a fallback when operations are needed
203
+ if hasattr(self, "lines") and self.lines:
204
+ # Use the first line for operations
205
+ from .linebuffer import LinesOperation
206
+
207
+ # CRITICAL FIX: Pass parent indicators so LinesOperation can call their _once
208
+ parent_a = self if hasattr(self, "_once") else None
209
+ # Use original_other (before lines[0] extraction) to get the indicator reference
210
+ parent_b_candidate = original_other if original_other is not None else other
211
+ parent_b = parent_b_candidate if hasattr(parent_b_candidate, "_once") else None
212
+ return LinesOperation(
213
+ self.lines[0], other, operation, r=r, parent_a=parent_a, parent_b=parent_b
214
+ )
215
+ # If no lines, return a simple operation result
216
+ try:
217
+ if r:
218
+ return operation(other, 0) # Use 0 as default value
219
+ return operation(0, other) # Use 0 as default value
220
+ except Exception:
221
+ throttled_warning(
222
+ logger,
223
+ "lineroot.line_root.makeoperation_recovery",
224
+ "LineRoot operation fallback failed; returning safe default",
225
+ exc_info=False,
226
+ )
227
+ # If operation fails, return False for bool operations
228
+ if operation is bool:
229
+ return False
230
+ return 0
231
+
232
+ # Perform self operation
233
+ def _makeoperationown(self, operation, _ownerskip=None):
234
+ # CRITICAL FIX: For bool operations, return a simple boolean result instead of creating objects
235
+ if operation is bool:
236
+ # For bool operations, check if we have any lines and if they have data
237
+ if hasattr(self, "lines") and self.lines:
238
+ try:
239
+ # Try to get the current value from the first line
240
+ if hasattr(self.lines, "__getitem__") and len(self.lines) > 0:
241
+ line = self.lines[0]
242
+ if hasattr(line, "__getitem__") and hasattr(line, "__len__"):
243
+ if len(line) > 0:
244
+ value = line[0]
245
+ # Return True if value is not None, not NaN and not 0
246
+ if value is None:
247
+ return False
248
+ if isinstance(value, float):
249
+ import math
250
+
251
+ if not math.isfinite(value):
252
+ return False
253
+ # bool() for the same reason as in __nonzero__:
254
+ # numpy.float64 subclasses float, so it reaches
255
+ # this branch and `!= 0.0` yields numpy.bool_.
256
+ # This path returns a value rather than going
257
+ # through the __bool__ protocol, so a leak here
258
+ # would propagate silently instead of raising.
259
+ return bool(value != 0.0)
260
+ return bool(value)
261
+ return False
262
+ except Exception:
263
+ throttled_warning(
264
+ logger,
265
+ "lineroot.line_root.makeoperationown_boolean_recovery",
266
+ "LineRoot boolean operation fallback failed; returning False",
267
+ exc_info=False,
268
+ )
269
+ return False
270
+ elif hasattr(self, "__getitem__") and hasattr(self, "__len__"):
271
+ # For LineSingle objects, check the current value directly
272
+ try:
273
+ if len(self) > 0:
274
+ value = self[0]
275
+ if value is None:
276
+ return False
277
+ if isinstance(value, float):
278
+ import math
279
+
280
+ if not math.isfinite(value):
281
+ return False
282
+ # See the note above: guard against numpy.bool_ leaking
283
+ # out of this non-__bool__ return path.
284
+ return bool(value != 0.0)
285
+ return bool(value)
286
+ return False
287
+ except Exception:
288
+ throttled_warning(
289
+ logger,
290
+ "lineroot.line_root.makeoperationown_boolean_recovery",
291
+ "LineRoot boolean operation fallback failed; returning False",
292
+ exc_info=False,
293
+ )
294
+ return False
295
+ else:
296
+ return False
297
+
298
+ # For other operations, use the original approach but only if really needed
299
+ if hasattr(self, "lines") and self.lines:
300
+ # Use the first line for self-operations
301
+ from .linebuffer import LineOwnOperation
302
+
303
+ return LineOwnOperation(self.lines[0], operation)
304
+ # If no lines, return a simple operation result
305
+ try:
306
+ return operation(0) # Use 0 as default value
307
+ except Exception:
308
+ throttled_warning(
309
+ logger,
310
+ "lineroot.line_root.makeoperationown_recovery",
311
+ "LineRoot self-operation fallback failed; returning 0",
312
+ exc_info=False,
313
+ )
314
+ # If operation fails, return 0 for most operations
315
+ return 0
316
+
317
+ # Self operation stage 1
318
+ def _operationown_stage1(self, operation):
319
+ """
320
+ Operation with single operand which is "self"
321
+ """
322
+ return self._makeoperationown(operation, _ownerskip=self)
323
+
324
+ # Self operation stage 2
325
+ def _operationown_stage2(self, operation):
326
+ return operation(self[0])
327
+
328
+ # Right operation
329
+ def _roperation(self, other, operation, intify=False):
330
+ """
331
+ Relies on self._operation to and passes "r" True to define a
332
+ reverse operation
333
+ """
334
+ return self._operation(other, operation, r=True, intify=intify)
335
+
336
+ # Stage 1 operation, determine if other contains multiple lines, if multiple lines, take the first line and perform operation
337
+ def _operation_stage1(self, other, operation, r=False, intify=False):
338
+ """
339
+ Two operands' operations.Scanning of other happens to understand
340
+ if other must be directly an operand or rather a subitem thereof
341
+ """
342
+ # CRITICAL FIX: Preserve original indicator reference before extracting lines[0]
343
+ original_other = other
344
+ if isinstance(other, LineMultiple):
345
+ other = other.lines[0]
346
+
347
+ return self._makeoperation(other, operation, r, self, original_other=original_other)
348
+
349
+ # Stage 2 operation, if other is a line, take the current value and perform operation
350
+ def _operation_stage2(self, other, operation, r=False):
351
+ """
352
+ Rich Comparison operators. Scans other and returns either an
353
+ operation with other directly or a subitem from other
354
+ """
355
+ if isinstance(other, LineRoot):
356
+ other = other[0]
357
+
358
+ # operation(float, other) ... expecting other to be a float
359
+ # CRITICAL FIX: Handle None values in comparisons to prevent errors
360
+ self_value = self[0]
361
+
362
+ # CRITICAL FIX: Convert None to 0.0 to prevent None vs float comparison errors
363
+ if self_value is None:
364
+ self_value = 0.0
365
+ elif isinstance(self_value, float):
366
+ import math
367
+
368
+ if not math.isfinite(self_value):
369
+ self_value = 0.0
370
+
371
+ # Also handle None in other value
372
+ if other is None:
373
+ other = 0.0
374
+ elif isinstance(other, float):
375
+ import math
376
+
377
+ if not math.isfinite(other):
378
+ other = 0.0
379
+
380
+ # CRITICAL FIX: Actually perform the operation and return the result
381
+ # Don't create LinesOperation objects in stage2 - return actual values
382
+ try:
383
+ if r:
384
+ result = operation(other, self_value)
385
+ else:
386
+ result = operation(self_value, other)
387
+ if isinstance(result, float):
388
+ import math
389
+
390
+ if not math.isfinite(result):
391
+ return 0.0
392
+ return result
393
+ except Exception:
394
+ throttled_warning(
395
+ logger,
396
+ "lineroot.line_root.operation_stage2_recovery",
397
+ "LineRoot stage-2 operation failed; returning safe default",
398
+ exc_info=False,
399
+ )
400
+ # If operation fails, return appropriate default
401
+ if operation in [
402
+ operator.__lt__,
403
+ operator.__le__,
404
+ operator.__gt__,
405
+ operator.__ge__,
406
+ operator.__eq__,
407
+ operator.__ne__,
408
+ ]:
409
+ return False # For comparison operations, return False on error
410
+ return 0.0 # For arithmetic operations, return 0.0 on error
411
+
412
+ # Addition
413
+ def __add__(self, other):
414
+ return self._operation(other, operator.__add__)
415
+
416
+ # Right addition
417
+ def __radd__(self, other):
418
+ return self._roperation(other, operator.__add__)
419
+
420
+ # Subtraction
421
+ def __sub__(self, other):
422
+ return self._operation(other, operator.__sub__)
423
+
424
+ # Right subtraction
425
+ def __rsub__(self, other):
426
+ return self._roperation(other, operator.__sub__)
427
+
428
+ # Multiplication
429
+ def __mul__(self, other):
430
+ return self._operation(other, operator.__mul__)
431
+
432
+ # Right multiplication
433
+ def __rmul__(self, other):
434
+ return self._roperation(other, operator.__mul__)
435
+
436
+ # Division (Py2 alias; mapped to true division for Py3 correctness)
437
+ def __div__(self, other):
438
+ return self._operation(other, operator.__truediv__)
439
+
440
+ # Right division
441
+ def __rdiv__(self, other):
442
+ return self._roperation(other, operator.__truediv__)
443
+
444
+ # Floor division
445
+ def __floordiv__(self, other):
446
+ return self._operation(other, operator.__floordiv__)
447
+
448
+ # Right floor division
449
+ def __rfloordiv__(self, other):
450
+ return self._roperation(other, operator.__floordiv__)
451
+
452
+ # True division
453
+ def __truediv__(self, other):
454
+ return self._operation(other, operator.__truediv__)
455
+
456
+ # Right true division
457
+ def __rtruediv__(self, other):
458
+ return self._roperation(other, operator.__truediv__)
459
+
460
+ # Power
461
+ def __pow__(self, other):
462
+ return self._operation(other, operator.__pow__)
463
+
464
+ # Right power
465
+ def __rpow__(self, other):
466
+ return self._roperation(other, operator.__pow__)
467
+
468
+ # Absolute value
469
+ def __abs__(self):
470
+ return self._operationown(operator.__abs__)
471
+
472
+ # Negation result
473
+ def __neg__(self):
474
+ return self._operationown(operator.__neg__)
475
+
476
+ # a<b
477
+ def __lt__(self, other):
478
+ # CRITICAL FIX: Always check opstage first to determine behavior
479
+ if self._opstage == 2:
480
+ # In stage2, return actual boolean values for direct use in strategies
481
+ self_value = self[0] if hasattr(self, "__getitem__") else 0.0
482
+
483
+ # Handle None values and convert to floats for comparison
484
+ if self_value is None:
485
+ self_value = 0.0
486
+ elif isinstance(self_value, float):
487
+ import math
488
+
489
+ if not math.isfinite(self_value):
490
+ self_value = 0.0
491
+
492
+ if other is None:
493
+ other = 0.0
494
+ elif isinstance(other, float):
495
+ import math
496
+
497
+ if not math.isfinite(other):
498
+ other = 0.0
499
+
500
+ # Return actual boolean for direct strategy use
501
+ try:
502
+ return float(self_value) < float(other)
503
+ except (ValueError, TypeError):
504
+ return False
505
+ else:
506
+ # In stage1, use normal operation creation
507
+ return self._operation(other, operator.__lt__)
508
+
509
+ # a>b
510
+ def __gt__(self, other):
511
+ # CRITICAL FIX: Always check opstage first to determine behavior
512
+ if self._opstage == 2:
513
+ # In stage2, return actual boolean values for direct use in strategies
514
+ self_value = self[0] if hasattr(self, "__getitem__") else 0.0
515
+
516
+ # Handle None values and convert to floats for comparison
517
+ if self_value is None:
518
+ self_value = 0.0
519
+ elif isinstance(self_value, float):
520
+ import math
521
+
522
+ if not math.isfinite(self_value):
523
+ self_value = 0.0
524
+
525
+ if other is None:
526
+ other = 0.0
527
+ elif isinstance(other, float):
528
+ import math
529
+
530
+ if not math.isfinite(other):
531
+ other = 0.0
532
+
533
+ # Return actual boolean for direct strategy use
534
+ try:
535
+ return float(self_value) > float(other)
536
+ except (ValueError, TypeError):
537
+ return False
538
+ else:
539
+ # In stage1, use normal operation creation
540
+ return self._operation(other, operator.__gt__)
541
+
542
+ # a<=b
543
+ def __le__(self, other):
544
+ # CRITICAL FIX: Always check opstage first to determine behavior
545
+ if self._opstage == 2:
546
+ # In stage2, return actual boolean values for direct use in strategies
547
+ self_value = self[0] if hasattr(self, "__getitem__") else 0.0
548
+
549
+ # Handle None values and convert to floats for comparison
550
+ if self_value is None:
551
+ self_value = 0.0
552
+ elif isinstance(self_value, float):
553
+ import math
554
+
555
+ if not math.isfinite(self_value):
556
+ self_value = 0.0
557
+
558
+ if other is None:
559
+ other = 0.0
560
+ elif isinstance(other, float):
561
+ import math
562
+
563
+ if not math.isfinite(other):
564
+ other = 0.0
565
+
566
+ # Return actual boolean for direct strategy use
567
+ try:
568
+ return float(self_value) <= float(other)
569
+ except (ValueError, TypeError):
570
+ return False
571
+ else:
572
+ # In stage1, use normal operation creation
573
+ return self._operation(other, operator.__le__)
574
+
575
+ # a>=b
576
+ def __ge__(self, other):
577
+ # CRITICAL FIX: Always check opstage first to determine behavior
578
+ if self._opstage == 2:
579
+ # In stage2, return actual boolean values for direct use in strategies
580
+ self_value = self[0] if hasattr(self, "__getitem__") else 0.0
581
+
582
+ # Handle None values and convert to floats for comparison
583
+ if self_value is None:
584
+ self_value = 0.0
585
+ elif isinstance(self_value, float):
586
+ import math
587
+
588
+ if not math.isfinite(self_value):
589
+ self_value = 0.0
590
+
591
+ if other is None:
592
+ other = 0.0
593
+ elif isinstance(other, float):
594
+ import math
595
+
596
+ if not math.isfinite(other):
597
+ other = 0.0
598
+
599
+ # Return actual boolean for direct strategy use
600
+ try:
601
+ return float(self_value) >= float(other)
602
+ except (ValueError, TypeError):
603
+ return False
604
+ else:
605
+ # In stage1, use normal operation creation
606
+ return self._operation(other, operator.__ge__)
607
+
608
+ # a = b
609
+ def __eq__(self, other):
610
+ return self._operation(other, operator.__eq__)
611
+
612
+ # a!=b
613
+ def __ne__(self, other):
614
+ return self._operation(other, operator.__ne__)
615
+
616
+ # a!=0
617
+ def __nonzero__(self):
618
+ # CRITICAL FIX: __bool__ MUST return a boolean, not a LineOwnOperation object
619
+ # This was causing "TypeError: __bool__ should return bool, returned LineOwnOperation"
620
+ try:
621
+ if hasattr(self, "lines") and self.lines:
622
+ # For LineMultiple objects, check the first line
623
+ if hasattr(self.lines, "__getitem__") and len(self.lines) > 0:
624
+ line = self.lines[0]
625
+ if hasattr(line, "__getitem__") and hasattr(line, "__len__"):
626
+ if len(line) > 0:
627
+ value = line[0]
628
+ # Return True if value exists and is not 0
629
+ if value is None:
630
+ return False
631
+ if isinstance(value, float):
632
+ import math
633
+
634
+ if not math.isfinite(value):
635
+ return False
636
+ # bool() is required, not cosmetic: numpy.float64
637
+ # subclasses float, so numpy scalars reach this branch
638
+ # and `np.float64 != 0.0` yields numpy.bool_, which
639
+ # CPython rejects from __bool__. Such scalars come from
640
+ # PandasData and survive only in QBuffer/exactbars mode,
641
+ # where lines are backed by a deque rather than
642
+ # array.array("d") (which coerces them to float).
643
+ return bool(value != 0.0)
644
+ return bool(value)
645
+ return False
646
+ if hasattr(self, "__getitem__") and hasattr(self, "__len__"):
647
+ # For LineSingle objects, check the current value
648
+ if len(self) > 0:
649
+ value = self[0]
650
+ if value is None:
651
+ return False
652
+ if isinstance(value, float):
653
+ import math
654
+
655
+ if not math.isfinite(value):
656
+ return False
657
+ # See the note above: bool() guards against numpy.float64
658
+ # reaching the isinstance(value, float) branch and making
659
+ # `!=` return numpy.bool_.
660
+ return bool(value != 0.0)
661
+ return bool(value)
662
+ return False
663
+ # Fallback: if no data available, return False
664
+ return False
665
+ except Exception:
666
+ throttled_warning(
667
+ logger,
668
+ "lineroot.line_root.nonzero_recovery",
669
+ "LineRoot boolean evaluation failed; returning False",
670
+ exc_info=False,
671
+ )
672
+ # If any error occurs during boolean evaluation, return False
673
+ # This prevents crashes in strategies when doing "if self.cross > 0:"
674
+ return False
675
+
676
+ __bool__ = __nonzero__
677
+
678
+ # Python 3 forces explicit implementation of hash if
679
+ # the class has redefined __eq__
680
+ __hash__ = object.__hash__
681
+
682
+
683
+ class LineMultiple(LineRoot):
684
+ """Base class for objects containing multiple lines.
685
+
686
+ LineMultiple is the base class for objects that manage multiple
687
+ line instances, such as indicators with multiple outputs. It provides
688
+ common functionality for period management, staging, and operations
689
+ across all contained lines.
690
+
691
+ Attributes:
692
+ lines: Collection of line objects managed by this instance.
693
+ _ltype: Line type indicator (None for base LineMultiple).
694
+ _clock: Clock reference for synchronization.
695
+ _lineiterators: Dictionary tracking registered lineiterators.
696
+
697
+ Example:
698
+ >>> class MyIndicator(LineMultiple):
699
+ ... lines = ('output1', 'output2')
700
+ """
701
+
702
+ def __init__(self):
703
+ """Initialize a LineMultiple instance.
704
+
705
+ Sets up the internal state for managing multiple lines, including
706
+ line type indicator, lines collection, clock reference, and
707
+ line iterator tracking.
708
+
709
+ Initializes:
710
+ _ltype: Line type indicator (None for base LineMultiple).
711
+ lines: Collection of line objects (creates if not exists).
712
+ _clock: Clock reference for synchronization.
713
+ _lineiterators: Dictionary tracking registered lineiterators.
714
+ _minperiod: Minimum period requirement (defaults to 1).
715
+ """
716
+ super().__init__()
717
+ # CRITICAL FIX: Initialize _ltype for proper strategy/indicator identification
718
+ self._ltype = None
719
+ # CRITICAL FIX: Initialize lines list to prevent index errors
720
+ if not hasattr(self, "lines") or self.lines is None:
721
+ from . import lineseries
722
+
723
+ self.lines = lineseries.Lines()
724
+
725
+ # CRITICAL FIX: Set up minimal clock for timing
726
+ if not hasattr(self, "_clock"):
727
+ self._clock = None
728
+
729
+ # CRITICAL FIX: Initialize line iterators tracking
730
+ if not hasattr(self, "_lineiterators"):
731
+ self._lineiterators = {}
732
+
733
+ # CRITICAL FIX: Ensure minperiod is set
734
+ if not hasattr(self, "_minperiod"):
735
+ self._minperiod = 1
736
+
737
+ def reset(self):
738
+ """Reset the line multiple to initial state.
739
+
740
+ Resets the operation stage to stage 1 and resets all managed
741
+ lines to their initial state. This is typically called before
742
+ starting a new backtest run.
743
+ """
744
+ self._stage1()
745
+ self.lines.reset()
746
+
747
+ def _stage1(self):
748
+ super()._stage1()
749
+ for line in self.lines:
750
+ line._stage1()
751
+
752
+ def _stage2(self):
753
+ super()._stage2()
754
+ for line in self.lines:
755
+ line._stage2()
756
+
757
+ def addminperiod(self, minperiod):
758
+ """
759
+ The passed minperiod is fed to the lines
760
+ """
761
+ has_child_iterators = False
762
+ try:
763
+ has_child_iterators = any(self._lineiterators.values())
764
+ except (AttributeError, TypeError):
765
+ # No sub-iterator registry yet; treat as having no child iterators.
766
+ pass
767
+
768
+ has_linebinding_output = any(
769
+ getattr(line, "_linebinding_assigned", False) for line in getattr(self, "lines", [])
770
+ )
771
+
772
+ if has_child_iterators and not has_linebinding_output:
773
+ self._minperiod = max(self._minperiod, minperiod)
774
+ for line in self.lines:
775
+ line.updateminperiod(self._minperiod)
776
+ return
777
+
778
+ # minperiod is added with -1 to account for overlapping
779
+ self._minperiod += minperiod - 1
780
+
781
+ for line in self.lines:
782
+ line.addminperiod(minperiod)
783
+
784
+ def incminperiod(self, minperiod):
785
+ """
786
+ The passed minperiod is fed to the lines
787
+ """
788
+ for line in self.lines:
789
+ line.incminperiod(minperiod)
790
+
791
+ def _makeoperation(self, other, operation, r=False, _ownerskip=None, original_other=None):
792
+ # For LineMultiple, we can implement a basic operation using the first line
793
+ # This provides a fallback when operations are needed
794
+ if hasattr(self, "lines") and self.lines:
795
+ # Use the first line for operations
796
+ from .linebuffer import LineActions, LinesOperation
797
+
798
+ # Only set parent to LineActions instances — never full indicators.
799
+ # Full indicators are processed via _lineiterators ordering in _once().
800
+ parent_a = self if isinstance(self, LineActions) else None
801
+ parent_b_candidate = original_other if original_other is not None else other
802
+ parent_b = parent_b_candidate if isinstance(parent_b_candidate, LineActions) else None
803
+ return LinesOperation(
804
+ self.lines[0], other, operation, r=r, parent_a=parent_a, parent_b=parent_b
805
+ )
806
+ # If no lines, return a simple operation result
807
+ try:
808
+ if r:
809
+ return operation(other, 0) # Use 0 as default value
810
+ return operation(0, other) # Use 0 as default value
811
+ except Exception:
812
+ throttled_warning(
813
+ logger,
814
+ "lineroot.line_multiple.makeoperation_recovery",
815
+ "LineMultiple operation fallback failed; returning safe default",
816
+ exc_info=False,
817
+ )
818
+ # If operation fails, return False for bool operations
819
+ if operation is bool:
820
+ return False
821
+ return 0
822
+
823
+ def _makeoperationown(self, operation, _ownerskip=None):
824
+ # CRITICAL FIX: For bool operations, return a simple boolean result instead of creating objects
825
+ if operation is bool:
826
+ # For bool operations, check if we have any lines and if they have data
827
+ if hasattr(self, "lines") and self.lines:
828
+ try:
829
+ # Try to get the current value from the first line
830
+ value = self.lines[0][0] if len(self.lines[0]) > 0 else 0
831
+ # Return True if value is not NaN and not 0
832
+ import math
833
+
834
+ if isinstance(value, float) and not math.isfinite(value):
835
+ return False
836
+ return bool(value)
837
+ except Exception:
838
+ throttled_warning(
839
+ logger,
840
+ "lineroot.line_multiple.makeoperationown_boolean_recovery",
841
+ "LineMultiple boolean operation fallback failed; returning False",
842
+ exc_info=False,
843
+ )
844
+ return False
845
+ else:
846
+ return False
847
+
848
+ # For other operations, use the original approach but only if really needed
849
+ if hasattr(self, "lines") and self.lines:
850
+ # Use the first line for self-operations
851
+ from .linebuffer import LineOwnOperation
852
+
853
+ return LineOwnOperation(self.lines[0], operation)
854
+ # If no lines, return a simple operation result
855
+ try:
856
+ return operation(0) # Use 0 as default value
857
+ except Exception:
858
+ throttled_warning(
859
+ logger,
860
+ "lineroot.line_multiple.makeoperationown_recovery",
861
+ "LineMultiple self-operation fallback failed; returning 0",
862
+ exc_info=False,
863
+ )
864
+ # If operation fails, return 0 for most operations
865
+ return 0
866
+
867
+ def qbuffer(self, savemem=0):
868
+ """Apply queued buffering to all managed lines.
869
+
870
+ Changes the lines to implement a minimum size qbuffer scheme
871
+ to control memory usage during backtesting.
872
+
873
+ Args:
874
+ savemem: Memory savings mode (0 = normal, higher values
875
+ reduce memory usage at the cost of performance).
876
+ """
877
+ for line in self.lines:
878
+ line.qbuffer(savemem=savemem)
879
+
880
+ def minbuffer(self, size):
881
+ """Set minimum buffer size for all managed lines.
882
+
883
+ Receives notification of how large the buffer must at least be
884
+ and propagates this requirement to all managed lines.
885
+
886
+ Args:
887
+ size: Minimum buffer size required.
888
+ """
889
+ for line in self.lines:
890
+ line.minbuffer(size)
891
+
892
+
893
+ class LineSingle(LineRoot):
894
+ """Base class for single-line objects.
895
+
896
+ LineSingle is the base class for objects that represent a single
897
+ line of time-series data. It provides the foundational interface
898
+ for period management and operations on individual lines.
899
+
900
+ Example:
901
+ >>> line = LineSingle()
902
+ >>> line.addminperiod(5) # Require 5 periods before valid
903
+ """
904
+
905
+ def addminperiod(self, minperiod):
906
+ """
907
+ Add the minperiod (substracting the overlapping 1 minimum period)
908
+ """
909
+ self._minperiod += minperiod - 1
910
+
911
+ def incminperiod(self, minperiod):
912
+ """
913
+ Increment the minperiod with no considerations
914
+ """
915
+ self._minperiod += minperiod
916
+
917
+
918
+ # CRITICAL FIX: Patch Strategy._clk_update to prevent "max() iterable argument is empty" error
919
+ def _apply_strategy_patch():
920
+ """Apply critical bug fix to Strategy class _clk_update method"""
921
+ try:
922
+ import math
923
+
924
+ # Import after a delay to ensure strategy module is loaded
925
+ def safe_clk_update(self):
926
+ """CRITICAL FIX: Safe _clk_update method that handles empty data sources"""
927
+
928
+ # CRITICAL FIX: Handle the old sync method safely
929
+ if hasattr(self, "_oldsync") and self._oldsync:
930
+ # Call parent class _clk_update if available
931
+ try:
932
+ # Use the parent class method from StrategyBase if available
933
+ from .lineiterator import StrategyBase
934
+
935
+ if hasattr(StrategyBase, "_clk_update"):
936
+ clk_len = super(type(self), self)._clk_update()
937
+ else:
938
+ clk_len = 1
939
+ except Exception:
940
+ throttled_warning(
941
+ logger,
942
+ "lineroot.strategy_patch.oldsync_clock_recovery",
943
+ "Strategy compatibility clock update failed; using length 1",
944
+ exc_info=False,
945
+ )
946
+ clk_len = 1
947
+
948
+ # CRITICAL FIX: Only set datetime if we have valid data sources with length
949
+ if hasattr(self, "datas") and self.datas:
950
+ valid_data_times = []
951
+ for d in self.datas:
952
+ try:
953
+ if (
954
+ len(d) > 0
955
+ and hasattr(d, "datetime")
956
+ and hasattr(d.datetime, "__getitem__")
957
+ ):
958
+ dt_val = d.datetime[0]
959
+ # Only add valid datetime values (not None or NaN)
960
+ if dt_val is not None and not (
961
+ isinstance(dt_val, float) and math.isnan(dt_val)
962
+ ):
963
+ valid_data_times.append(dt_val)
964
+ except (IndexError, AttributeError, TypeError):
965
+ continue
966
+
967
+ if (
968
+ valid_data_times
969
+ and hasattr(self, "lines")
970
+ and hasattr(self.lines, "datetime")
971
+ ):
972
+ try:
973
+ self.lines.datetime[0] = max(valid_data_times)
974
+ except (ValueError, IndexError, AttributeError):
975
+ # If setting datetime fails, use a default valid ordinal (1 = Jan 1, Year 1)
976
+ self.lines.datetime[0] = 1.0
977
+ elif hasattr(self, "lines") and hasattr(self.lines, "datetime"):
978
+ # No valid times, use default valid ordinal (1 = Jan 1, Year 1)
979
+ self.lines.datetime[0] = 1.0
980
+
981
+ return clk_len
982
+
983
+ # CRITICAL FIX: Handle the normal (non-oldsync) path
984
+ # Initialize _dlens if not present
985
+ if not hasattr(self, "_dlens"):
986
+ self._dlens = [
987
+ len(d) if hasattr(d, "__len__") else 0
988
+ for d in (self.datas if hasattr(self, "datas") else [])
989
+ ]
990
+
991
+ # Get current data lengths safely
992
+ if hasattr(self, "datas") and self.datas:
993
+ newdlens = []
994
+ for d in self.datas:
995
+ try:
996
+ newdlens.append(len(d) if hasattr(d, "__len__") else 0)
997
+ except Exception:
998
+ throttled_warning(
999
+ logger,
1000
+ "lineroot.strategy_patch.data_length_recovery",
1001
+ "Strategy compatibility data length lookup failed; using length 0",
1002
+ exc_info=False,
1003
+ )
1004
+ newdlens.append(0)
1005
+ else:
1006
+ newdlens = []
1007
+
1008
+ # Forward if any data source has grown
1009
+ if (
1010
+ newdlens
1011
+ and hasattr(self, "_dlens")
1012
+ and any(
1013
+ nl > old_len
1014
+ for old_len, nl in zip(self._dlens, newdlens)
1015
+ if old_len is not None and nl is not None
1016
+ )
1017
+ ):
1018
+ try:
1019
+ if hasattr(self, "forward"):
1020
+ self.forward()
1021
+ except Exception:
1022
+ throttled_warning(
1023
+ logger,
1024
+ "lineroot.strategy_patch.forward_recovery",
1025
+ "Strategy compatibility clock forward failed; continuing update",
1026
+ exc_info=False,
1027
+ )
1028
+
1029
+ # Update _dlens
1030
+ self._dlens = newdlens
1031
+
1032
+ # CRITICAL FIX: Set datetime safely - only use data sources that have valid data
1033
+ if (
1034
+ hasattr(self, "datas")
1035
+ and self.datas
1036
+ and hasattr(self, "lines")
1037
+ and hasattr(self.lines, "datetime")
1038
+ ):
1039
+ valid_data_times = []
1040
+ for d in self.datas:
1041
+ try:
1042
+ if (
1043
+ len(d) > 0
1044
+ and hasattr(d, "datetime")
1045
+ and hasattr(d.datetime, "__getitem__")
1046
+ ):
1047
+ dt_val = d.datetime[0]
1048
+ # Only add valid datetime values (not None or NaN)
1049
+ if dt_val is not None and not (
1050
+ isinstance(dt_val, float) and math.isnan(dt_val)
1051
+ ):
1052
+ valid_data_times.append(dt_val)
1053
+ except (IndexError, AttributeError, TypeError):
1054
+ continue
1055
+
1056
+ # CRITICAL FIX: This is the line that was causing the "max() iterable argument is empty" error
1057
+ # We check if valid_data_times is not empty before calling max()
1058
+ if valid_data_times:
1059
+ try:
1060
+ self.lines.datetime[0] = max(valid_data_times)
1061
+ except (ValueError, IndexError, AttributeError):
1062
+ # If setting datetime fails, use a default valid ordinal (1 = Jan 1, Year 1)
1063
+ self.lines.datetime[0] = 1.0
1064
+ else:
1065
+ # No valid times available, use a reasonable default valid ordinal
1066
+ # This is the critical fix - instead of calling max() on empty list, use default
1067
+ self.lines.datetime[0] = 1.0
1068
+
1069
+ # Return the length of this strategy (number of processed bars)
1070
+ try:
1071
+ return len(self)
1072
+ except Exception:
1073
+ throttled_warning(
1074
+ logger,
1075
+ "lineroot.strategy_patch.length_recovery",
1076
+ "Strategy compatibility length lookup failed; returning 0",
1077
+ exc_info=False,
1078
+ )
1079
+ return 0
1080
+
1081
+ # Import Strategy and patch it
1082
+ try:
1083
+ from .strategy import Strategy
1084
+
1085
+ # Monkey patch the Strategy class
1086
+ Strategy._clk_update = safe_clk_update
1087
+ except ImportError:
1088
+ # Strategy not imported yet, try to patch later when it's imported
1089
+ throttled_warning(
1090
+ logger,
1091
+ "lineroot.strategy_patch.import_recovery",
1092
+ "Strategy compatibility patch deferred because Strategy is not importable",
1093
+ exc_info=False,
1094
+ )
1095
+
1096
+ except Exception:
1097
+ throttled_warning(
1098
+ logger,
1099
+ "lineroot.strategy_patch.apply_recovery",
1100
+ "Strategy compatibility patch installation failed; continuing without patch",
1101
+ exc_info=False,
1102
+ )
1103
+
1104
+
1105
+ # Apply the patch when this module is imported
1106
+ _apply_strategy_patch()