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,960 @@
1
+ #!/usr/bin/env python
2
+ """Functions Module - Common operations on line objects.
3
+
4
+ This module provides utility functions and classes for performing
5
+ operations on line objects. It includes arithmetic operations with
6
+ zero-division protection, logical operations, comparison operations,
7
+ and mathematical functions.
8
+
9
+ Classes:
10
+ Logic: Base class for logical operations on lines.
11
+ DivByZero: Division with zero-division protection.
12
+ DivZeroByZero: Division with zero/zero indetermination protection.
13
+ And/Or/Not/If/Max/Min/MinN/MaxN: Logical and comparison operations.
14
+ Sum/Average/StdDev/TSMean: Statistical operations.
15
+
16
+ Example:
17
+ Using indicator functions:
18
+ >>> from backtrader.functions import And, Or
19
+ >>> condition = And(indicator1 > indicator2, indicator3 > 0)
20
+ """
21
+
22
+ import functools
23
+ import math
24
+
25
+ from .linebuffer import LineActions
26
+ from .utils.log_message import get_logger, throttled_warning
27
+ from .utils.py3 import cmp, range
28
+
29
+ logger = get_logger(__name__)
30
+
31
+
32
+ def _sanitize_cmp_value(value):
33
+ if value is None:
34
+ return 0.0
35
+
36
+ if isinstance(value, float) and not math.isfinite(value):
37
+ return 0.0
38
+
39
+ return value
40
+
41
+
42
+ def _sanitize_div_value(value):
43
+ if value is None:
44
+ return 0.0
45
+
46
+ if isinstance(value, float) and not math.isfinite(value):
47
+ return 0.0
48
+
49
+ return value
50
+
51
+
52
+ def _sanitize_numeric_values(values):
53
+ return [_sanitize_div_value(value) for value in values]
54
+
55
+
56
+ def _value_at(array, index, default=0.0):
57
+ try:
58
+ return array[index]
59
+ except (IndexError, TypeError):
60
+ try:
61
+ return array[-1]
62
+ except (IndexError, TypeError):
63
+ return default
64
+
65
+
66
+ def _maxlogic(values):
67
+ return max(_sanitize_numeric_values(values))
68
+
69
+
70
+ def _minlogic(values):
71
+ return min(_sanitize_numeric_values(values))
72
+
73
+
74
+ def _sumlogic(values):
75
+ return math.fsum(_sanitize_numeric_values(values))
76
+
77
+
78
+ # Generate a List equivalent which uses "is" for contains
79
+ # Create a new List class, overriding __contains__ method, if any element in list has hash value equal to other's hash value, return True
80
+ class List(list):
81
+ """List subclass that uses hash equality for contains checks.
82
+
83
+ This class overrides __contains__ to check if any element has
84
+ the same hash value as the target, rather than using identity comparison.
85
+ """
86
+
87
+ def __contains__(self, other):
88
+ return any(x is other for x in self)
89
+
90
+
91
+ # Create a class to serialize elements within it
92
+ class Logic(LineActions):
93
+ """Base class for logical operations on line objects.
94
+
95
+ Handles argument conversion to arrays and manages minperiod
96
+ propagation from operands.
97
+ """
98
+
99
+ def __init__(self, *args):
100
+ """Initialize the Logic operation.
101
+
102
+ Converts all arguments to arrays and propagates minperiod
103
+ from operands to ensure proper synchronization.
104
+
105
+ Args:
106
+ *args: Line objects or values to operate on.
107
+ """
108
+ super().__init__()
109
+ self.args = [self.arrayize(arg) for arg in args]
110
+
111
+ # CRITICAL FIX: Collect minperiods from args and update own minperiod
112
+ # This ensures functions like And, Or, etc. inherit the max minperiod from their operands
113
+ _minperiods = []
114
+ for arg in self.args:
115
+ mp = getattr(arg, "_minperiod", 1)
116
+ _minperiods.append(mp)
117
+
118
+ if _minperiods:
119
+ max_minperiod = max(_minperiods)
120
+ self.updateminperiod(max_minperiod)
121
+
122
+ def _next(self):
123
+ clock = getattr(self, "_clock", None)
124
+ if clock is not None and clock.__class__.__name__ != "MinimalClock":
125
+ try:
126
+ if len(clock) <= len(self):
127
+ return
128
+ except Exception: # nosec B110
129
+ # Clock without a comparable length; proceed to advance below.
130
+ throttled_warning(
131
+ logger, "logic_clock_advance", "functions:130 suppressed Exception"
132
+ )
133
+
134
+ target_len = len(self) + 1
135
+ for arg in getattr(self, "args", ()):
136
+ if isinstance(arg, LineActions) and hasattr(arg, "_next"):
137
+ try:
138
+ if len(arg) < target_len:
139
+ arg._next()
140
+ except Exception as e:
141
+ logger.debug("Logic operand _next() failed: %s", e)
142
+
143
+ self.advance()
144
+ self.next()
145
+ for binding in self.bindings:
146
+ binding[0] = self[0]
147
+
148
+
149
+ # Avoid division by zero when dividing two lines, if denominator is 0, division result is 0
150
+ class DivByZero(Logic):
151
+ """This operation is a Lines object and fills it values by executing a
152
+ division on the numerator / denominator arguments and avoiding a division
153
+ by zero exception by checking the denominator
154
+
155
+ Params:
156
+ - a: numerator (numeric or iterable object ... mostly a Lines object)
157
+ - b: denominator (numeric or iterable object ... mostly a Lines object)
158
+ - zero (def: 0.0): value to apply if division by zero is raised
159
+
160
+ """
161
+
162
+ def __init__(self, a, b, zero=0.0):
163
+ """Initialize the DivByZero operation.
164
+
165
+ Args:
166
+ a: Numerator line or value.
167
+ b: Denominator line or value.
168
+ zero: Value to return when division by zero occurs.
169
+ """
170
+ super().__init__(a, b)
171
+ self.a = self.args[0]
172
+ self.b = self.args[1]
173
+ self.zero = zero
174
+
175
+ def next(self):
176
+ """Calculate the next value with zero-division protection."""
177
+ a = _sanitize_div_value(self.a[0])
178
+ b = _sanitize_div_value(self.b[0])
179
+ self[0] = a / b if b else self.zero
180
+
181
+ def once(self, start, end):
182
+ """Calculate all values at once with zero-division protection.
183
+
184
+ Args:
185
+ start: Starting index for calculation.
186
+ end: Ending index for calculation.
187
+ """
188
+ # cache python dictionary lookups
189
+ dst = self.array
190
+ srca = self.a.array
191
+ srcb = self.b.array
192
+ zero = self.zero
193
+
194
+ # Ensure destination array is properly sized
195
+ while len(dst) < end:
196
+ dst.append(0.0)
197
+
198
+ for i in range(start, end):
199
+ a = _sanitize_div_value(_value_at(srca, i))
200
+ b = _sanitize_div_value(_value_at(srcb, i))
201
+ dst[i] = a / b if b else zero
202
+
203
+
204
+ # Division operation for two lines considering both numerator and denominator may be 0
205
+ class DivZeroByZero(Logic):
206
+ """This operation is a Lines object and fills it values by executing a
207
+ division on the numerator / denominator arguments and avoiding a division
208
+ by zero exception or an indetermination by checking the
209
+ denominator/numerator pair
210
+
211
+ Params:
212
+ - a: numerator (numeric or iterable object ... mostly a Lines object)
213
+ - b: denominator (numeric or iterable object ... mostly a Lines object)
214
+ - single (def: +inf): value to apply if division is x / 0
215
+ - dual (def: 0.0): value to apply if division is 0 / 0
216
+ """
217
+
218
+ def __init__(self, a, b, single=float("inf"), dual=0.0):
219
+ """Initialize the DivZeroByZero operation.
220
+
221
+ Args:
222
+ a: Numerator line or value.
223
+ b: Denominator line or value.
224
+ single: Value to return when numerator is non-zero and denominator is zero.
225
+ dual: Value to return when both numerator and denominator are zero.
226
+ """
227
+ super().__init__(a, b)
228
+ self.a = self.args[0]
229
+ self.b = self.args[1]
230
+ self.single = single
231
+ self.dual = dual
232
+
233
+ def next(self):
234
+ """Calculate the next value with zero/zero indetermination protection."""
235
+ b = _sanitize_div_value(self.b[0])
236
+ a = _sanitize_div_value(self.a[0])
237
+ if b == 0.0:
238
+ self[0] = self.dual if a == 0.0 else self.single
239
+ else:
240
+ self[0] = a / b
241
+
242
+ def once(self, start, end):
243
+ """Calculate all values at once with zero/zero indetermination protection.
244
+
245
+ Args:
246
+ start: Starting index for calculation.
247
+ end: Ending index for calculation.
248
+ """
249
+ # cache python dictionary lookups
250
+ dst = self.array
251
+ srca = self.a.array
252
+ srcb = self.b.array
253
+ single = self.single
254
+ dual = self.dual
255
+
256
+ # Ensure destination array is properly sized
257
+ while len(dst) < end:
258
+ dst.append(0.0)
259
+
260
+ for i in range(start, end):
261
+ b = _sanitize_div_value(_value_at(srcb, i))
262
+ a = _sanitize_div_value(_value_at(srca, i))
263
+ if b == 0.0:
264
+ dst[i] = dual if a == 0.0 else single
265
+ else:
266
+ dst[i] = a / b
267
+
268
+
269
+ # Compare a and b, a and b are likely lines
270
+ class Cmp(Logic):
271
+ """Comparison operation that returns comparison results.
272
+
273
+ Compares two line objects and returns standard comparison values:
274
+ -1 if a < b, 0 if a == b, 1 if a > b.
275
+ """
276
+
277
+ def __init__(self, a, b):
278
+ """Initialize the comparison operation.
279
+
280
+ Args:
281
+ a: First line or value to compare.
282
+ b: Second line or value to compare.
283
+ """
284
+ super().__init__(a, b)
285
+ self.a = self.args[0]
286
+ self.b = self.args[1]
287
+
288
+ def next(self):
289
+ """Calculate the next comparison value."""
290
+ self[0] = cmp(_sanitize_cmp_value(self.a[0]), _sanitize_cmp_value(self.b[0]))
291
+
292
+ def once(self, start, end):
293
+ """Calculate all comparison values at once.
294
+
295
+ Args:
296
+ start: Starting index for calculation.
297
+ end: Ending index for calculation.
298
+ """
299
+ # cache python dictionary lookups
300
+ dst = self.array
301
+ srca = self.a.array
302
+ srcb = self.b.array
303
+
304
+ # Ensure destination array is properly sized
305
+ while len(dst) < end:
306
+ dst.append(0.0)
307
+
308
+ for i in range(start, end):
309
+ dst[i] = cmp(
310
+ _sanitize_cmp_value(_value_at(srca, i)),
311
+ _sanitize_cmp_value(_value_at(srcb, i)),
312
+ )
313
+
314
+
315
+ # Compare two lines, a and b, return corresponding r1 value when a<b, return r2 value when a=b, return r3 value when a>b
316
+ class CmpEx(Logic):
317
+ """Extended comparison operation with three possible return values.
318
+
319
+ Compares two line objects and returns one of three values based on
320
+ the comparison result:
321
+ - r1 if a < b
322
+ - r2 if a == b
323
+ - r3 if a > b
324
+ """
325
+
326
+ def __init__(self, a, b, r1, r2, r3):
327
+ """Initialize the extended comparison operation.
328
+
329
+ Args:
330
+ a: First line or value to compare.
331
+ b: Second line or value to compare.
332
+ r1: Value to return when a < b.
333
+ r2: Value to return when a == b.
334
+ r3: Value to return when a > b.
335
+ """
336
+ super().__init__(a, b, r1, r2, r3)
337
+ self.a = self.args[0]
338
+ self.b = self.args[1]
339
+ self.r1 = self.args[2]
340
+ self.r2 = self.args[3]
341
+ self.r3 = self.args[4]
342
+
343
+ def next(self):
344
+ """Calculate the next extended comparison value."""
345
+ # self[0] = cmp(self.a[0], self.b[0])
346
+ a0 = _sanitize_cmp_value(self.a[0])
347
+ b0 = _sanitize_cmp_value(self.b[0])
348
+
349
+ if a0 < b0:
350
+ self[0] = _sanitize_div_value(self.r1[0])
351
+ elif a0 > b0:
352
+ self[0] = _sanitize_div_value(self.r3[0])
353
+ else:
354
+ self[0] = _sanitize_div_value(self.r2[0])
355
+
356
+ def once(self, start, end):
357
+ """Calculate all extended comparison values at once.
358
+
359
+ Args:
360
+ start: Starting index for calculation.
361
+ end: Ending index for calculation.
362
+ """
363
+ # cache python dictionary lookups
364
+ dst = self.array
365
+ srca = self.a.array
366
+ srcb = self.b.array
367
+ r1 = self.r1.array
368
+ r2 = self.r2.array
369
+ r3 = self.r3.array
370
+
371
+ # Ensure destination array is properly sized
372
+ while len(dst) < end:
373
+ dst.append(0.0)
374
+
375
+ for i in range(start, end):
376
+ ai = _sanitize_cmp_value(_value_at(srca, i))
377
+ bi = _sanitize_cmp_value(_value_at(srcb, i))
378
+
379
+ if ai < bi:
380
+ dst[i] = _sanitize_div_value(_value_at(r1, i))
381
+ elif ai > bi:
382
+ dst[i] = _sanitize_div_value(_value_at(r3, i))
383
+ else:
384
+ dst[i] = _sanitize_div_value(_value_at(r2, i))
385
+
386
+
387
+ # If statement, return corresponding a value when cond is satisfied, return b value when not satisfied
388
+ class If(Logic):
389
+ """Conditional selection operation.
390
+
391
+ Returns a value from a or b based on a condition:
392
+ - Returns a if condition is True
393
+ - Returns b if condition is False
394
+ """
395
+
396
+ def __init__(self, cond, a, b):
397
+ """Initialize the conditional operation.
398
+
399
+ Args:
400
+ cond: Condition line - must evaluate to boolean.
401
+ a: Value to return when condition is True.
402
+ b: Value to return when condition is False.
403
+ """
404
+ super().__init__(cond, a, b)
405
+ self.cond = self.args[0]
406
+ self.a = self.args[1]
407
+ self.b = self.args[2]
408
+
409
+ def next(self):
410
+ """Calculate the next conditional value."""
411
+ cond_val = _sanitize_div_value(self.cond[0])
412
+ value = self.a[0] if cond_val else self.b[0]
413
+ self[0] = _sanitize_div_value(value)
414
+
415
+ def _has_self_reference(self):
416
+ """Check if this If operation has a self-referencing pattern.
417
+
418
+ Detects patterns like: self.lines.direction = bt.If(..., direction(-1))
419
+ where the output line appears as an input via _LineDelay.
420
+ """
421
+ if not self.bindings:
422
+ return False
423
+
424
+ # Get the bound line(s)
425
+ bound_lines = {id(b) for b in self.bindings}
426
+
427
+ # Check if any operand references a bound line (via _LineDelay)
428
+ def _check_ref(obj, depth=0):
429
+ if depth > 10:
430
+ return False
431
+ if hasattr(obj, "a"):
432
+ # _LineDelay: check if obj.a is one of our bound lines
433
+ if id(getattr(obj, "a", None)) in bound_lines:
434
+ return True
435
+ # Check if obj.a's array is the same as a bound line's array
436
+ obj_a = getattr(obj, "a", None)
437
+ if obj_a is not None:
438
+ for binding in self.bindings:
439
+ if hasattr(obj_a, "array") and hasattr(binding, "array"):
440
+ if obj_a.array is binding.array:
441
+ return True
442
+ if _check_ref(obj_a, depth + 1):
443
+ return True
444
+ if hasattr(obj, "b"):
445
+ obj_b = getattr(obj, "b", None)
446
+ if obj_b is not None:
447
+ if id(obj_b) in bound_lines:
448
+ return True
449
+ if hasattr(obj_b, "array"):
450
+ for binding in self.bindings:
451
+ if hasattr(binding, "array") and obj_b.array is binding.array:
452
+ return True
453
+ if _check_ref(obj_b, depth + 1):
454
+ return True
455
+ if hasattr(obj, "args"):
456
+ for arg in getattr(obj, "args", []):
457
+ if _check_ref(arg, depth + 1):
458
+ return True
459
+ if hasattr(obj, "cond"):
460
+ if _check_ref(getattr(obj, "cond", None), depth + 1):
461
+ return True
462
+ return False
463
+
464
+ return _check_ref(self.a) or _check_ref(self.b) or _check_ref(self.cond)
465
+
466
+ def once(self, start, end):
467
+ """Calculate all conditional values at once.
468
+
469
+ Supports self-referencing patterns like:
470
+ self.lines.direction = bt.If(cond, 1, bt.If(cond2, -1, self.lines.direction(-1)))
471
+
472
+ For self-referencing patterns, processes bar-by-bar using next() semantics
473
+ to ensure previously computed values are available for the next bar.
474
+
475
+ Args:
476
+ start: Starting index for calculation.
477
+ end: Ending index for calculation.
478
+ """
479
+ dst = self.array
480
+
481
+ # Ensure destination array is properly sized
482
+ while len(dst) < end:
483
+ dst.append(0.0)
484
+
485
+ # Also ensure bound line arrays are sized
486
+ for binding in self.bindings:
487
+ while len(binding.array) < end:
488
+ binding.array.append(0.0)
489
+
490
+ # For self-referencing patterns, use bar-by-bar processing
491
+ # This ensures _LineDelay can read previously computed values
492
+ if self.bindings and self._has_self_reference():
493
+ self._once_sequential(start, end)
494
+ return
495
+
496
+ # Standard If expressions can contain nested LinesOperation/_LineDelay
497
+ # operands which are not always scheduled separately by LineIterator.
498
+ # Compute them explicitly before reading their arrays below.
499
+ for operand in (self.cond, self.a, self.b):
500
+ if hasattr(operand, "once") and len(getattr(operand, "array", [])) < end:
501
+ try:
502
+ operand.once(0, end)
503
+ except Exception as e:
504
+ logger.debug("If operand once() failed: %s", e)
505
+
506
+ # Standard batch processing for non-self-referencing patterns
507
+ self._once_batch(start, end)
508
+
509
+ def _once_sequential(self, start, end):
510
+ """Process bar-by-bar for self-referencing patterns.
511
+
512
+ Ensures all operand arrays are computed first, then processes
513
+ sequentially with immediate binding propagation so _LineDelay
514
+ can read previously written values.
515
+ """
516
+ dst = self.array
517
+ has_bindings = bool(self.bindings)
518
+
519
+ # Ensure operand arrays are computed first
520
+ # The condition (LinesOperation) needs its once() called
521
+ if hasattr(self.cond, "once") and len(getattr(self.cond, "array", [])) < end:
522
+ try:
523
+ self.cond.once(start, end)
524
+ except Exception: # nosec B110
525
+ # Operand already (partially) computed or not once()-able; continue.
526
+ logger.warning("functions:524 suppressed Exception")
527
+
528
+ # The 'a' operand (could be constant or LinesOperation)
529
+ if hasattr(self.a, "once") and len(getattr(self.a, "array", [])) < end:
530
+ try:
531
+ self.a.once(start, end)
532
+ except Exception: # nosec B110
533
+ # Operand already (partially) computed or not once()-able; continue.
534
+ logger.warning("functions:532 suppressed Exception")
535
+
536
+ # The 'b' operand - for self-referencing, this is typically another bt.If
537
+ # We need to compute it BUT it contains the self-reference, so we handle it specially
538
+ if hasattr(self.b, "once") and len(getattr(self.b, "array", [])) < end:
539
+ # Check if b itself has self-reference (nested bt.If with direction(-1))
540
+ # If so, we need to compute b bar-by-bar too
541
+ if hasattr(self.b, "_has_self_reference") and self.b._has_self_reference():
542
+ # Don't call b.once() - we'll compute b[i] dynamically
543
+ pass
544
+ else:
545
+ try:
546
+ self.b.once(start, end)
547
+ except Exception: # nosec B110
548
+ # Operand already (partially) computed or not once()-able; continue.
549
+ logger.warning("functions:547 suppressed Exception")
550
+
551
+ # Get arrays for direct access where possible
552
+ cond_array = getattr(self.cond, "array", [])
553
+ cond_has_array = len(cond_array) >= end
554
+ a_array = getattr(self.a, "array", [])
555
+ a_has_array = len(a_array) >= end
556
+ # Check if a is a constant (_LineDelay wrapping PseudoArray)
557
+ a_is_constant = False
558
+ a_constant_val = None
559
+ if not a_has_array:
560
+ try:
561
+ a_constant_val = self.a[0]
562
+ a_is_constant = True
563
+ except Exception: # nosec B110
564
+ # 'a' is neither array-backed nor a constant scalar; leave defaults.
565
+ logger.warning("functions:563 suppressed Exception")
566
+
567
+ b_array = getattr(self.b, "array", [])
568
+ b_has_array = len(b_array) >= end
569
+
570
+ for i in range(start, end):
571
+ # Get condition value
572
+ if cond_has_array:
573
+ cond_val = cond_array[i]
574
+ else:
575
+ try:
576
+ cond_val = (
577
+ self.cond.array[i] if i < len(getattr(self.cond, "array", [])) else 0.0
578
+ )
579
+ except (IndexError, TypeError):
580
+ cond_val = 0.0
581
+
582
+ cond_bool = (cond_val != 0.0) and (
583
+ not (isinstance(cond_val, float) and math.isnan(cond_val))
584
+ )
585
+
586
+ if cond_bool:
587
+ # Get a value
588
+ if a_is_constant:
589
+ val = a_constant_val
590
+ elif a_has_array:
591
+ val = a_array[i]
592
+ else:
593
+ try:
594
+ val = self.a.array[i] if i < len(getattr(self.a, "array", [])) else 0.0
595
+ except (IndexError, TypeError):
596
+ val = 0.0
597
+ else:
598
+ # Get b value - for self-referencing, b is the inner bt.If
599
+ # which reads from _LineDelay(direction, -1)
600
+ if b_has_array:
601
+ val = b_array[i]
602
+ else:
603
+ # b's array isn't fully computed - compute dynamically
604
+ # For nested bt.If with self-reference, we need to evaluate it
605
+ try:
606
+ val = self._eval_operand_at(self.b, i)
607
+ except Exception:
608
+ throttled_warning(
609
+ logger, "if_sequential_operand", "functions:605 fallback on Exception"
610
+ )
611
+ val = 0.0
612
+
613
+ val = _sanitize_div_value(val)
614
+
615
+ dst[i] = val
616
+
617
+ # Propagate to bindings immediately for self-referencing
618
+ if has_bindings:
619
+ for binding in self.bindings:
620
+ binding.array[i] = val
621
+
622
+ def _eval_operand_at(self, operand, i):
623
+ """Evaluate an operand at absolute index i.
624
+
625
+ For nested bt.If with self-reference, recursively evaluates
626
+ the condition and branches at the given index.
627
+ """
628
+ if isinstance(operand, If):
629
+ # Recursively evaluate the nested If
630
+ # Get condition
631
+ cond_arr = getattr(operand.cond, "array", [])
632
+ if i < len(cond_arr):
633
+ cond_val = cond_arr[i]
634
+ else:
635
+ cond_val = 0.0
636
+
637
+ cond_bool = (cond_val != 0.0) and (
638
+ not (isinstance(cond_val, float) and math.isnan(cond_val))
639
+ )
640
+
641
+ if cond_bool:
642
+ return self._eval_operand_at(operand.a, i)
643
+ return self._eval_operand_at(operand.b, i)
644
+
645
+ # For _LineDelay, read from its source array at offset
646
+ if hasattr(operand, "ago") and hasattr(operand, "a"):
647
+ src_array = getattr(operand.a, "array", [])
648
+ src_idx = i + operand.ago
649
+ if 0 <= src_idx < len(src_array):
650
+ return src_array[src_idx]
651
+ return 0.0
652
+
653
+ # For arrays, direct access
654
+ arr = getattr(operand, "array", [])
655
+ if i < len(arr):
656
+ return arr[i]
657
+
658
+ # Constant
659
+ try:
660
+ return operand[0]
661
+ except Exception:
662
+ throttled_warning(logger, "if_operand_scalar", "functions:656 fallback on Exception")
663
+ return 0.0
664
+
665
+ def _once_batch(self, start, end):
666
+ """Standard batch processing for non-self-referencing patterns."""
667
+ dst = self.array
668
+
669
+ # Detect constants
670
+ a_is_constant = False
671
+ a_constant_val = None
672
+ try:
673
+ srca = self.a.array
674
+ a_has_array = len(srca) > 0
675
+ if not a_has_array:
676
+ try:
677
+ a_constant_val = self.a[0]
678
+ a_is_constant = True
679
+ except Exception: # nosec B110
680
+ # 'a' has an empty array and no scalar value; not a constant.
681
+ logger.warning("functions:675 suppressed Exception")
682
+ except (AttributeError, TypeError):
683
+ srca = []
684
+ a_has_array = False
685
+ try:
686
+ a_constant_val = self.a[0]
687
+ a_is_constant = True
688
+ except Exception: # nosec B110
689
+ # 'a' is neither array-backed nor a scalar constant.
690
+ logger.warning("functions:684 suppressed Exception")
691
+
692
+ b_is_constant = False
693
+ b_constant_val = None
694
+ try:
695
+ srcb = self.b.array
696
+ b_has_array = len(srcb) > 0
697
+ if not b_has_array:
698
+ try:
699
+ b_constant_val = self.b[0]
700
+ b_is_constant = True
701
+ except Exception: # nosec B110
702
+ # 'b' has an empty array and no scalar value; not a constant.
703
+ logger.warning("functions:697 suppressed Exception")
704
+ except (AttributeError, TypeError):
705
+ srcb = []
706
+ b_has_array = False
707
+ try:
708
+ b_constant_val = self.b[0]
709
+ b_is_constant = True
710
+ except Exception: # nosec B110
711
+ # 'b' is neither array-backed nor a scalar constant.
712
+ logger.warning("functions:706 suppressed Exception")
713
+
714
+ try:
715
+ cond = self.cond.array
716
+ cond_has_array = len(cond) > 0
717
+ except (AttributeError, TypeError):
718
+ cond = []
719
+ cond_has_array = False
720
+
721
+ a_use_dynamic = not a_is_constant and not a_has_array and hasattr(self.a, "__getitem__")
722
+ b_use_dynamic = not b_is_constant and not b_has_array and hasattr(self.b, "__getitem__")
723
+ has_bindings = bool(self.bindings)
724
+
725
+ for i in range(start, end):
726
+ if cond_has_array:
727
+ try:
728
+ cond_val = cond[i] if i < len(cond) else (cond[-1] if cond else 0.0)
729
+ except (IndexError, TypeError):
730
+ cond_val = 0.0
731
+ else:
732
+ try:
733
+ cond_val = self.cond[i] if hasattr(self.cond, "__getitem__") else 0.0
734
+ except Exception:
735
+ throttled_warning(
736
+ logger, "if_batch_condition", "functions:728 fallback on Exception"
737
+ )
738
+ cond_val = 0.0
739
+
740
+ cond_bool = (cond_val != 0.0) and (
741
+ not (isinstance(cond_val, float) and math.isnan(cond_val))
742
+ )
743
+
744
+ if a_is_constant:
745
+ a_val = a_constant_val
746
+ elif a_has_array:
747
+ try:
748
+ a_val = srca[i] if i < len(srca) else (srca[-1] if srca else 0.0)
749
+ except (IndexError, TypeError):
750
+ a_val = 0.0
751
+ elif a_use_dynamic:
752
+ try:
753
+ a_val = self.a[i]
754
+ except Exception:
755
+ throttled_warning(
756
+ logger, "if_batch_true_operand", "functions:745 fallback on Exception"
757
+ )
758
+ a_val = 0.0
759
+ else:
760
+ a_val = 0.0
761
+
762
+ if b_is_constant:
763
+ b_val = b_constant_val
764
+ elif b_has_array:
765
+ try:
766
+ b_val = srcb[i] if i < len(srcb) else (srcb[-1] if srcb else 0.0)
767
+ except (IndexError, TypeError):
768
+ b_val = 0.0
769
+ elif b_use_dynamic:
770
+ try:
771
+ b_val = self.b[i]
772
+ except Exception:
773
+ throttled_warning(
774
+ logger, "if_batch_false_operand", "functions:760 fallback on Exception"
775
+ )
776
+ b_val = 0.0
777
+ else:
778
+ b_val = 0.0
779
+
780
+ a_val = _sanitize_div_value(a_val)
781
+ b_val = _sanitize_div_value(b_val)
782
+
783
+ val = a_val if cond_bool else b_val
784
+ dst[i] = val
785
+
786
+ # Propagate to bindings for consistency
787
+ if has_bindings:
788
+ for binding in self.bindings:
789
+ binding.array[i] = val
790
+
791
+
792
+ # Apply one logic to multiple elements
793
+ class MultiLogic(Logic):
794
+ """Base class for operations that apply a function to multiple arguments.
795
+
796
+ The flogic attribute should be set to a callable that takes
797
+ an iterable of values and returns a single result.
798
+ """
799
+
800
+ def next(self):
801
+ """Apply the logic function to current values from all arguments."""
802
+ self[0] = self.flogic([arg[0] for arg in self.args])
803
+
804
+ def once(self, start, end):
805
+ """Apply the logic function to all values across the specified range.
806
+
807
+ Args:
808
+ start: Starting index for calculation.
809
+ end: Ending index for calculation.
810
+ """
811
+ # cache python dictionary lookups
812
+ dst = self.array
813
+
814
+ # Ensure destination array is properly sized
815
+ while len(dst) < end:
816
+ dst.append(0.0)
817
+
818
+ for arg in self.args:
819
+ if isinstance(arg, LineActions) and hasattr(arg, "once"):
820
+ try:
821
+ if len(getattr(arg, "array", [])) < end:
822
+ arg.once(0, end)
823
+ except Exception as e:
824
+ logger.debug("MultiLogic operand once() failed: %s", e)
825
+
826
+ arrays = [arg.array for arg in self.args]
827
+ flogic = self.flogic
828
+
829
+ for i in range(start, end):
830
+ dst[i] = flogic([_value_at(arr, i) for arr in arrays])
831
+
832
+
833
+ # Mainly uses functools.partial to generate partial function, functools.reduce, iterates function on a sequence
834
+ class MultiLogicReduce(MultiLogic):
835
+ """MultiLogic that uses functools.reduce for cumulative operations.
836
+
837
+ This class applies a reduction function cumulatively to all arguments,
838
+ combining them into a single result.
839
+ """
840
+
841
+ def __init__(self, *args, **kwargs):
842
+ """Initialize the reduction operation.
843
+
844
+ Args:
845
+ *args: Line objects or values to reduce.
846
+ **kwargs: Optional keyword arguments including 'initializer'.
847
+ """
848
+ super().__init__(*args)
849
+ if "initializer" not in kwargs:
850
+ self.flogic = functools.partial(functools.reduce, self.flogic)
851
+ else:
852
+ self.flogic = functools.partial(
853
+ functools.reduce, self.flogic, initializer=kwargs["initializer"]
854
+ )
855
+
856
+
857
+ # Inheritance class, process flogic
858
+ class Reduce(MultiLogicReduce):
859
+ """Generic reduction operation with a custom function.
860
+
861
+ Allows any reduction function to be applied to the arguments.
862
+ """
863
+
864
+ def __init__(self, flogic, *args, **kwargs):
865
+ """Initialize the custom reduction operation.
866
+
867
+ Args:
868
+ flogic: Function to use for reduction.
869
+ *args: Line objects or values to reduce.
870
+ **kwargs: Optional keyword arguments.
871
+ """
872
+ self.flogic = flogic
873
+ super().__init__(*args, **kwargs)
874
+
875
+
876
+ # The _xxxlogic functions are defined at module scope to make them
877
+ # pickable and therefore compatible with multiprocessing
878
+
879
+
880
+ # Determine if both x and y are True
881
+ def _andlogic(x, y):
882
+ """Logical AND operation for reduction."""
883
+ return bool(x and y)
884
+
885
+
886
+ # Determine if all elements are True
887
+ class And(MultiLogicReduce):
888
+ """Logical AND operation across all arguments.
889
+
890
+ Returns True only if all input values are truthy.
891
+ """
892
+
893
+ flogic = staticmethod(_andlogic)
894
+
895
+
896
+ # Determine if either x or y is true
897
+ def _orlogic(x, y):
898
+ """Logical OR operation for reduction."""
899
+ return bool(x or y)
900
+
901
+
902
+ # Determine if any element in the sequence is true
903
+ class Or(MultiLogicReduce):
904
+ """Logical OR operation across all arguments.
905
+
906
+ Returns True if any input value is truthy.
907
+ """
908
+
909
+ flogic = staticmethod(_orlogic)
910
+
911
+
912
+ # Find maximum value
913
+ class Max(MultiLogic):
914
+ """Maximum operation across all arguments.
915
+
916
+ Returns the maximum value from all input lines.
917
+ """
918
+
919
+ flogic = staticmethod(_maxlogic)
920
+
921
+
922
+ # Find minimum value
923
+ class Min(MultiLogic):
924
+ """Minimum operation across all arguments.
925
+
926
+ Returns the minimum value from all input lines.
927
+ """
928
+
929
+ flogic = staticmethod(_minlogic)
930
+
931
+
932
+ # Calculate sum
933
+ class Sum(MultiLogic):
934
+ """Sum operation across all arguments.
935
+
936
+ Returns the sum of all input values using math.fsum
937
+ for better floating point precision.
938
+ """
939
+
940
+ flogic = staticmethod(_sumlogic)
941
+
942
+
943
+ # Check if any exists
944
+ class Any(MultiLogic):
945
+ """Any operation across all arguments.
946
+
947
+ Returns True if any input value is truthy.
948
+ """
949
+
950
+ flogic = any
951
+
952
+
953
+ # Check if all
954
+ class All(MultiLogic):
955
+ """All operation across all arguments.
956
+
957
+ Returns True only if all input values are truthy.
958
+ """
959
+
960
+ flogic = all