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,377 @@
1
+ """Cerebro run orchestration mixin (iteration 28 split).
2
+
3
+ Moved verbatim from ``backtrader/cerebro.py``: strategy instantiation
4
+ preparation, runstrategies orchestration, writers and shared helpers.
5
+ """
6
+
7
+ import itertools
8
+ import logging
9
+ import time
10
+
11
+ from .. import errors, observers
12
+ from ..metabase import OwnerContext
13
+ from ..utils import OrderedDict, tzparse
14
+ from ..utils.log_message import _is_output_enabled_for, get_logger
15
+ from ..utils.py3 import integer_types
16
+
17
+ # Keep the historical logger name (D28-04.6): routing/filters must not change.
18
+ logger = get_logger("backtrader.cerebro")
19
+
20
+
21
+ class ExecutionMixin:
22
+ """Run orchestration half of Cerebro (see module docstring)."""
23
+
24
+ def _read_broker_lifecycle_value(self, accessor):
25
+ """Read an optional broker summary without changing the run outcome.
26
+
27
+ Lifecycle INFO logging is opt-in. A custom broker can legitimately
28
+ expose cash/value only after ``start()`` or before ``stop()``. The
29
+ summary is diagnostic data, so an unavailable accessor must not make
30
+ a backtest fail.
31
+ """
32
+ try:
33
+ return getattr(self._broker, accessor)()
34
+ except Exception:
35
+ # Broker implementations can raise third-party exceptions carrying
36
+ # account or transport details. This optional summary must never
37
+ # copy that unknown payload into a framework log.
38
+ logger.warning("broker %s unavailable for lifecycle logging", accessor)
39
+ return None
40
+
41
+ def _read_data_lifecycle_length(self, data):
42
+ """Read a data length only while its lifecycle is still active."""
43
+ try:
44
+ return len(data)
45
+ except Exception:
46
+ # ``data`` can be a live feed whose ``stop()`` releases buffers.
47
+ # Keep the event useful without writing a third-party traceback.
48
+ logger.warning("data length unavailable for lifecycle logging")
49
+ return None
50
+
51
+ @staticmethod
52
+ def _read_lifecycle_count(values):
53
+ """Read an optional count without consuming iterable strategy input."""
54
+ try:
55
+ return len(values)
56
+ except Exception:
57
+ return None
58
+
59
+ # Initialize count
60
+ def _init_stcount(self):
61
+ self.stcount = itertools.count(0)
62
+
63
+ # Call next count
64
+ def _next_stid(self):
65
+ return next(self.stcount)
66
+
67
+ def _prepare_run(self, predata=False):
68
+ """Start components and (optionally) preload data before strategies run.
69
+
70
+ Extracted from runstrategies() to keep that method readable. Starts
71
+ stores, applies cheat-on-open/fund/order-history settings, starts the
72
+ broker and feeds, writes CSV writer headers, and resets/preloads each
73
+ data feed unless ``predata`` is True.
74
+ """
75
+ # Iterate stores and start
76
+ for store in self.stores:
77
+ store.start()
78
+ # If cheat_on_open and broker_coo, set broker accordingly
79
+ if self.p.cheat_on_open and self.p.broker_coo:
80
+ # try to activate in broker
81
+ if hasattr(self._broker, "set_coo"):
82
+ self._broker.set_coo(True)
83
+ # If fund history is not None, need to set fund history
84
+ if self._fhistory is not None:
85
+ self._broker.set_fund_history(self._fhistory)
86
+ # Iterate order history
87
+ for orders, onotify in self._ohistory:
88
+ self._broker.add_order_history(orders, onotify)
89
+ # Broker start
90
+ self._broker.start()
91
+ # Feed start
92
+ for feed in self.feeds:
93
+ feed.start()
94
+ # If need to save writer data
95
+ if self.writers_csv:
96
+ # headers
97
+ wheaders = []
98
+ # Iterate data, if data csv attribute is True, get headers that need saving
99
+ for data in self.datas:
100
+ if data.csv:
101
+ wheaders.extend(data.getwriterheaders())
102
+ # Save writer headers
103
+ for writer in self.runwriters:
104
+ if writer.p.csv:
105
+ writer.addheaders(wheaders)
106
+
107
+ # If no predata, need to pre-process data, similar to run method preprocessing
108
+ if not predata:
109
+ for data in self.datas:
110
+ data.reset()
111
+ if self._exactbars < 1: # datas can be a full length
112
+ data.extend(size=self.params.lookahead)
113
+ data._start()
114
+ if self._dopreload:
115
+ data.preload()
116
+
117
+ # Run strategy
118
+ def runstrategies(self, iterstrat, predata=False):
119
+ """
120
+ Internal method invoked by ``run``` to run a set of strategies
121
+ """
122
+ # Lifecycle diagnostics are opt-in and must not probe broker state
123
+ # before start()/after stop(). Some live/custom brokers deliberately
124
+ # reject those accessors outside their active lifecycle window.
125
+ log_lifecycle = _is_output_enabled_for(logging.INFO)
126
+ t0 = time.time() if log_lifecycle else None
127
+ initial_cash = None
128
+ self._init_stcount()
129
+ # Initialize running strategy as empty list
130
+ self.runningstrats = runstrats = []
131
+ # Start stores/broker/feeds, apply fund + order history, write headers
132
+ # and (optionally) preload data. Extracted for readability.
133
+ self._prepare_run(predata)
134
+ if log_lifecycle:
135
+ initial_cash = self._read_broker_lifecycle_value("getcash")
136
+ strategy_count = self._read_lifecycle_count(iterstrat)
137
+ logger.info(
138
+ "run starting: runonce=%s oldsync=%s strategies=%s datas=%d initial cash=%s",
139
+ self.p.runonce,
140
+ self.p.oldsync,
141
+ strategy_count if strategy_count is not None else "unavailable",
142
+ len(self.datas),
143
+ initial_cash if initial_cash is not None else "unavailable",
144
+ )
145
+ for data in self.datas:
146
+ name = getattr(data, "_name", "")
147
+ bars = self._read_data_lifecycle_length(data)
148
+ logger.info(
149
+ "data loaded: name=%s bars=%s",
150
+ name,
151
+ bars if bars is not None else "unavailable",
152
+ )
153
+ # Loop through strategies
154
+ for stratcls, sargs, skwargs in iterstrat:
155
+ # Add data to strategy parameters
156
+ sargs = self.datas + list(sargs)
157
+ # Instantiate strategy with OwnerContext so findowner() can find Cerebro
158
+ try:
159
+ # Use OwnerContext so Strategy.__new__ can find Cerebro via findowner()
160
+ with OwnerContext.set_owner(self):
161
+ # Use safe strategy creation to handle parameter filtering
162
+ if hasattr(stratcls, "_create_strategy_safely"):
163
+ strat = stratcls._create_strategy_safely(*sargs, **skwargs)
164
+ else:
165
+ # Fallback to direct instantiation
166
+ strat = stratcls(*sargs, **skwargs)
167
+ except errors.StrategySkipError:
168
+ logger.warning("execution:122 suppressed bare")
169
+ continue # do not add strategy to the mix
170
+ # Old data synchronization method
171
+ if self.p.oldsync:
172
+ strat._oldsync = True # tell strategy to use old clock update
173
+ # Whether to save trade history data
174
+ if self.p.tradehistory:
175
+ strat.set_tradehistory()
176
+ # Add strategy
177
+ runstrats.append(strat)
178
+ # Get timezone info, if tz is integer, get tz at that index; otherwise use tzparse
179
+ tz = self.p.tz
180
+ if isinstance(tz, integer_types):
181
+ tz = self.datas[tz]._tz
182
+ else:
183
+ tz = tzparse(tz)
184
+ # StrategySkipError may exclude every strategy; cleanup still runs.
185
+ run_exception = None
186
+ # If runstrats is not empty list
187
+ if runstrats:
188
+ # loop separated for clarity
189
+ # Get default sizer
190
+ defaultsizer = self.sizers.get(None, (None, None, None))
191
+ # For each strategy
192
+ for idx, strat in enumerate(runstrats):
193
+ # If stdstats is True, add several observers
194
+ if self.p.stdstats:
195
+ # Add observer broker
196
+ strat._addobserver(False, observers.Broker)
197
+ # Add observers.BuySell
198
+ if self.p.oldbuysell:
199
+ strat._addobserver(True, observers.BuySell)
200
+ else:
201
+ strat._addobserver(True, observers.BuySell, barplot=True)
202
+ # Add observer trade
203
+ if self.p.oldtrades or len(self.datas) == 1:
204
+ strat._addobserver(False, observers.Trades)
205
+ else:
206
+ strat._addobserver(False, observers.DataTrades)
207
+ # Add observers and their parameters to strategy
208
+ for multi, obscls, obsargs, obskwargs in self.observers:
209
+ strat._addobserver(multi, obscls, *obsargs, **obskwargs)
210
+ # Add indicators to strategy
211
+ for indcls, indargs, indkwargs in self.indicators:
212
+ strat._addindicator(indcls, *indargs, **indkwargs)
213
+ # Add analyzers to strategy
214
+ for ancls, anargs, ankwargs in self.analyzers:
215
+ strat._addanalyzer(ancls, *anargs, **ankwargs)
216
+ # Get specific sizer, if sizer is not None, add to strategy
217
+ sizer, sargs, skwargs = self.sizers.get(idx, defaultsizer)
218
+ if sizer is not None:
219
+ strat._addsizer(sizer, *sargs, **skwargs)
220
+ # Set timezone
221
+ strat._settz(tz)
222
+ # Strategy start
223
+ strat._start()
224
+ # For running writers, if csv parameter is True, save strategy data to writer
225
+ for writer in self.runwriters:
226
+ if writer.p.csv:
227
+ writer.addheaders(strat.getwriterheaders())
228
+ # If predata is False, data not preloaded
229
+ if not predata:
230
+ # Loop each strategy, call qbuffer to cache data
231
+ for strat in runstrats:
232
+ strat.qbuffer(self._exactbars, replaying=self._doreplay)
233
+ # Loop each writer, start writer
234
+ for writer in self.runwriters:
235
+ writer.start()
236
+
237
+ # Prepare timers
238
+ self._timers = []
239
+ self._timerscheat = []
240
+ # Loop timers
241
+ for timer in self._pretimers:
242
+ # preprocess tzdata if needed
243
+ # Start timer
244
+ timer.start(self.datas[0])
245
+ # If timer parameter cheat is True, add timer to self._timerscheat, otherwise add to self._timers
246
+ if timer.params.cheat:
247
+ self._timerscheat.append(timer)
248
+ else:
249
+ self._timers.append(timer)
250
+ # Run the main loop; keep cleanup deterministic, but never turn a
251
+ # strategy/runtime exception into a successful empty backtest.
252
+ try:
253
+ # If _dopreload and _dorunonce are True
254
+ if self._dopreload and self._dorunonce:
255
+ # If old data alignment and sync method, use _runonce_old, otherwise use _runonce
256
+ if self.p.oldsync:
257
+ self._runonce_old(runstrats)
258
+ else:
259
+ self._runonce(runstrats)
260
+ # If _dopreload and _dorunonce are not both True
261
+ else:
262
+ # If old data alignment and sync method, use _runnext_old, otherwise use _runnext
263
+ if self.p.oldsync:
264
+ self._runnext_old(runstrats)
265
+ else:
266
+ self._runnext(runstrats)
267
+ except Exception as exc:
268
+ run_exception = exc
269
+ logger.exception("Unhandled exception in run loop, cleaning up before re-raising")
270
+ finally:
271
+ # Iterate strategies and stop running (always runs)
272
+ for strat in runstrats:
273
+ strat._stop()
274
+ # Capture the final value while the broker is still active. This is
275
+ # only diagnostic data, so a custom broker may decline the accessor.
276
+ final_value = None
277
+ final_bars = None
278
+ if run_exception is None and log_lifecycle:
279
+ final_value = self._read_broker_lifecycle_value("getvalue")
280
+ final_bars = self._read_data_lifecycle_length(self.datas[0]) if self.datas else 0
281
+
282
+ # Stop broker
283
+ self._broker.stop()
284
+ # If predata is False, iterate data and stop each data
285
+ if not predata:
286
+ for data in self.datas:
287
+ data.stop()
288
+ # Iterate each feed and stop feed
289
+ for feed in self.feeds:
290
+ feed.stop()
291
+ # Iterate each store and stop store
292
+ for store in self.stores:
293
+ if getattr(store, "_cerebro_managed_lifecycle", True) is False:
294
+ continue
295
+ store.stop()
296
+ # Stop writer
297
+ self.stop_writers(runstrats)
298
+ if run_exception is None and log_lifecycle:
299
+ if final_value is None or initial_cash is None:
300
+ pnl = "unavailable"
301
+ else:
302
+ try:
303
+ pnl = final_value - initial_cash
304
+ except Exception:
305
+ logger.warning("broker pnl unavailable for lifecycle logging")
306
+ pnl = "unavailable"
307
+ logger.info(
308
+ "run finished: final value=%s pnl=%s bars=%d elapsed=%.2fs",
309
+ final_value if final_value is not None else "unavailable",
310
+ pnl,
311
+ final_bars if final_bars is not None else 0,
312
+ time.time() - t0,
313
+ )
314
+ if run_exception is not None:
315
+ raise run_exception
316
+ # If doing parameter optimization and optreturn is True, build lightweight
317
+ # OptReturn results (detached from data) instead of full strategy objects.
318
+ if self._dooptimize and self.p.optreturn:
319
+ return self._build_optreturn_results(runstrats)
320
+
321
+ return runstrats
322
+
323
+ # Stop writer
324
+ def stop_writers(self, runstrats):
325
+ """Stop all writers and write final information.
326
+
327
+ Args:
328
+ runstrats: List of strategy instances that were run.
329
+
330
+ Collects information from data feeds and strategies, writes
331
+ the information to all registered writers, and stops them.
332
+ """
333
+ # Cerebro info
334
+ cerebroinfo = OrderedDict()
335
+ # Data info
336
+ datainfos = OrderedDict()
337
+ # Get info for each data, save to datainfos, then save to cerebroinfo
338
+ for i, data in enumerate(self.datas):
339
+ datainfos["Data%d" % i] = data.getwriterinfo()
340
+
341
+ cerebroinfo["Datas"] = datainfos
342
+ # Get strategy info and save to stratinfos and cerebroinfo
343
+ stratinfos = {}
344
+ for strat in runstrats:
345
+ stname = strat.__class__.__name__
346
+ stratinfos[stname] = strat.getwriterinfo()
347
+
348
+ cerebroinfo["Strategies"] = stratinfos
349
+ # Write cerebroinfo to file
350
+ for writer in self.runwriters:
351
+ writer.writedict({"Cerebro": cerebroinfo})
352
+ writer.stop()
353
+
354
+ # Run writer's next
355
+ def _next_writers(self, runstrats):
356
+ if not self.runwriters:
357
+ return
358
+
359
+ if self.writers_csv:
360
+ wvalues = []
361
+ for data in self.datas:
362
+ if data.csv:
363
+ wvalues.extend(data.getwritervalues())
364
+
365
+ for strat in runstrats:
366
+ wvalues.extend(strat.getwritervalues())
367
+
368
+ for writer in self.runwriters:
369
+ if writer.p.csv:
370
+ writer.addvalues(wvalues)
371
+
372
+ writer.next()
373
+
374
+ # Disable runonce
375
+ def _disable_runonce(self):
376
+ """API for lineiterators to disable runonce (see HeikinAshi)"""
377
+ self._dorunonce = False
@@ -0,0 +1,143 @@
1
+ """Cerebro run-scope lifecycle mixin (iteration 28 split).
2
+
3
+ Moved verbatim from ``backtrader/cerebro.py``: run scope begin/end,
4
+ external channel scope retention and runstop publication.
5
+ """
6
+
7
+ # pylint: disable=no-member
8
+ # Mixin state (``_run_scope_token`` etc.) is created by ``Cerebro.__init__``
9
+ # on the assembled class; it cannot be seen from this partial class alone.
10
+ import threading
11
+
12
+ from ..utils.log_message import get_logger
13
+
14
+ logger = get_logger(__name__)
15
+
16
+
17
+ class RunLifecycleMixin:
18
+ """Run-scope lifecycle half of Cerebro (see module docstring)."""
19
+
20
+ def _begin_run(self):
21
+ """Start one synchronized run-stop scope for this Cerebro instance."""
22
+ with self._runstop_lock:
23
+ if self._run_active:
24
+ raise RuntimeError("Cerebro is already running")
25
+ self._event_stop.clear()
26
+ self._run_scope_token += 1
27
+ self._run_scope_owner = threading.get_ident()
28
+ self._run_active = True
29
+ return self._run_scope_token
30
+
31
+ def _open_run_scope(self):
32
+ """Open a run scope and roll it back if an overridden start hook fails."""
33
+ with self._runstop_lock:
34
+ previous_token = self._run_scope_token
35
+
36
+ try:
37
+ self._begin_run()
38
+ with self._runstop_lock:
39
+ if not self._run_active or self._run_scope_owner != threading.get_ident():
40
+ raise RuntimeError("Cerebro run scope was not published by the calling thread")
41
+ return self._run_scope_token
42
+ except BaseException:
43
+ # A subclass can call ``super()._begin_run()`` and then fail. Only
44
+ # retire a scope created by this thread after the snapshot; never
45
+ # clear another thread's active run after a rejected re-entry.
46
+ logger.error("lifecycle:41 exception before re-raise (BaseException)", exc_info=True)
47
+ self._end_run_if_started_by_current_thread(previous_token)
48
+ raise
49
+
50
+ def _end_run_if_started_by_current_thread(self, previous_token):
51
+ """Undo a partially opened scope without touching a different active run."""
52
+ with self._runstop_lock:
53
+ if (
54
+ self._run_active
55
+ and self._run_scope_owner == threading.get_ident()
56
+ and self._run_scope_token != previous_token
57
+ ):
58
+ self._retire_run_scope_locked()
59
+
60
+ def _retire_run_scope_locked(self):
61
+ """Clear one active run scope while ``_runstop_lock`` is held."""
62
+ self._run_active = False
63
+ self._run_scope_owner = None
64
+ self._event_stop.clear()
65
+ self._external_channel_token = None
66
+ self._external_channel_runstrats = None
67
+ self._external_channel_closing = False
68
+
69
+ def _end_run(self, token):
70
+ """Retire only this caller's run-stop scope.
71
+
72
+ A timer that fires after another run has already opened remains an
73
+ ordinary stop request for that later active scope; callers must cancel
74
+ or generation-bind such timers before reusing the instance.
75
+ """
76
+ with self._runstop_lock:
77
+ if (
78
+ not self._run_active
79
+ or self._run_scope_owner != threading.get_ident()
80
+ or self._run_scope_token != token
81
+ ):
82
+ return
83
+ self._retire_run_scope_locked()
84
+
85
+ def _retain_external_channel_scope(self, token, runstrats):
86
+ """Keep a ``run(channel=True)`` session active until its owner closes it."""
87
+ with self._runstop_lock:
88
+ if (
89
+ not self._run_active
90
+ or self._run_scope_owner != threading.get_ident()
91
+ or self._run_scope_token != token
92
+ ):
93
+ raise RuntimeError("Cerebro external channel scope was not published by its owner")
94
+ self._external_channel_token = token
95
+ self._external_channel_runstrats = runstrats
96
+ self._external_channel_closing = False
97
+
98
+ def close_channel(self):
99
+ """Tear down an external ``run(channel=True)`` session on its owner thread.
100
+
101
+ ``runstop()`` only publishes a stop request. The thread which called
102
+ ``run(channel=True)`` must call this method after its external driver
103
+ has stopped dispatching callbacks. This keeps broker and strategy
104
+ teardown out of foreign Timer or worker threads.
105
+
106
+ Returns:
107
+ ``True`` if an external channel session was closed, otherwise
108
+ ``False`` when no such session is active.
109
+
110
+ Raises:
111
+ RuntimeError: If a different thread tries to close the active
112
+ external channel session.
113
+ """
114
+ with self._runstop_lock:
115
+ token = self._external_channel_token
116
+ if token is None or not self._run_active or self._run_scope_token != token:
117
+ return False
118
+ if self._run_scope_owner != threading.get_ident():
119
+ raise RuntimeError("Cerebro external channel must be closed by its owner thread")
120
+ if self._external_channel_closing:
121
+ return False
122
+
123
+ self._external_channel_closing = True
124
+ self._event_stop.set()
125
+ runstrats = self._external_channel_runstrats
126
+
127
+ try:
128
+ self._teardown_channel(runstrats)
129
+ finally:
130
+ self._end_run(token)
131
+ return True
132
+
133
+ # When called from within a strategy or elsewhere, stops execution quickly
134
+ def runstop(self):
135
+ """Request prompt termination of the currently active run.
136
+
137
+ Calls from a strategy or another thread are safe. Calls made while
138
+ no ``run`` / optimization worker is active are ignored so a delayed
139
+ ``threading.Timer`` cannot stop a later, unrelated run.
140
+ """
141
+ with self._runstop_lock:
142
+ if self._run_active:
143
+ self._event_stop.set()
@@ -0,0 +1,150 @@
1
+ """Cerebro notification dispatch mixin (iteration 28 split).
2
+
3
+ Moved verbatim from ``backtrader/cerebro.py``: store/data callbacks and
4
+ broker notification delivery.
5
+ """
6
+
7
+ from ..brokers import BackBroker
8
+ from ..feed import AbstractDataBase
9
+
10
+
11
+ class NotificationMixin:
12
+ """Notification dispatch half of Cerebro (see module docstring)."""
13
+
14
+ def addstorecb(self, callback):
15
+ """Adds a callback to get messages which would be handled by the
16
+ notify_store method
17
+
18
+ The signature of the callback must support the following:
19
+
20
+ - callback(msg, *args, *kwargs)
21
+
22
+ The actual ``msg``, ``*args`` and ``**kwargs`` received are
23
+ implementation defined (depend entirely on the *data/broker/store*) but
24
+ in general one should expect them to be *printable* to allow for
25
+ reception and experimentation.
26
+ """
27
+ self.storecbs.append(callback)
28
+
29
+ def _notify_store(self, msg, *args, **kwargs):
30
+ """Internal method to dispatch store notifications."""
31
+ for callback in self.storecbs:
32
+ callback(msg, *args, **kwargs)
33
+
34
+ self.notify_store(msg, *args, **kwargs)
35
+
36
+ def notify_store(self, msg, *args, **kwargs):
37
+ """Receive store notifications in cerebro
38
+
39
+ This method can be overridden in ``Cerebro`` subclasses
40
+
41
+ The actual ``msg``, ``*args`` and ``**kwargs`` received are
42
+ implementation defined (depend entirely on the *data/broker/store*) but
43
+ in general one should expect them to be *printable* to allow for
44
+ reception and experimentation.
45
+ """
46
+
47
+ def _storenotify(self):
48
+ """Process and dispatch store notifications to strategies."""
49
+ for store in self.stores:
50
+ for notif in store.get_notifications():
51
+ msg, args, kwargs = notif
52
+
53
+ self._notify_store(msg, *args, **kwargs)
54
+ for strat in self.runningstrats:
55
+ strat.notify_store(msg, *args, **kwargs)
56
+ if hasattr(strat, "_notify_store_to_observers"):
57
+ strat._notify_store_to_observers(msg, *args, **kwargs)
58
+
59
+ def adddatacb(self, callback):
60
+ """Adds a callback to get messages which would be handled by the
61
+ notify_data method
62
+
63
+ The signature of the callback must support the following:
64
+
65
+ - callback(data, status, *args, *kwargs)
66
+
67
+ The actual ``*args`` and ``**kwargs`` received are implementation
68
+ defined (depend entirely on the *data/broker/store*), but in general one
69
+ should expect them to be *printable* to allow for reception and
70
+ experimentation.
71
+ """
72
+ self.datacbs.append(callback)
73
+
74
+ def _datanotify(self):
75
+ """Process and dispatch data notifications to strategies."""
76
+ for data in self.datas:
77
+ if type(data).get_notifications is AbstractDataBase.get_notifications:
78
+ notifications = data.notifs
79
+ if not notifications:
80
+ continue
81
+
82
+ notifications.append(None)
83
+ while True:
84
+ notif = notifications.popleft()
85
+ if notif is None:
86
+ break
87
+ status, args, kwargs = notif
88
+ self._notify_data(data, status, *args, **kwargs)
89
+ for strat in self.runningstrats:
90
+ strat.notify_data(data, status, *args, **kwargs)
91
+ if hasattr(strat, "_notify_data_to_observers"):
92
+ strat._notify_data_to_observers(data, status, *args, **kwargs)
93
+ else:
94
+ for notif in data.get_notifications():
95
+ status, args, kwargs = notif
96
+ self._notify_data(data, status, *args, **kwargs)
97
+ for strat in self.runningstrats:
98
+ strat.notify_data(data, status, *args, **kwargs)
99
+ if hasattr(strat, "_notify_data_to_observers"):
100
+ strat._notify_data_to_observers(data, status, *args, **kwargs)
101
+
102
+ def _notify_data(self, data, status, *args, **kwargs):
103
+ """Internal method to dispatch data notifications."""
104
+ for callback in self.datacbs:
105
+ callback(data, status, *args, **kwargs)
106
+
107
+ self.notify_data(data, status, *args, **kwargs)
108
+
109
+ def notify_data(self, data, status, *args, **kwargs):
110
+ """Receive data notifications in cerebro
111
+
112
+ This method can be overridden in ``Cerebro`` subclasses
113
+
114
+ The actual ``*args`` and ``**kwargs`` received are
115
+ implementation defined (depend entirely on the *data/broker/store*), but
116
+ in general one should expect them to be *printable* to allow for
117
+ reception and experimentation.
118
+ """
119
+
120
+ # Notify broker info
121
+ def _brokernotify(self):
122
+ """
123
+ Internal method which kicks the broker and delivers any broker
124
+ notification to the strategy
125
+ """
126
+ # Call broker's next
127
+ broker = self._broker
128
+ broker.next()
129
+ if type(broker).get_notification is BackBroker.get_notification:
130
+ notifications = broker.notifs
131
+ while notifications:
132
+ order = notifications.popleft()
133
+ owner = order.owner
134
+ if owner is None:
135
+ owner = self.runningstrats[0] # default
136
+ # Notify order info through first strategy
137
+ owner._addnotification(order, quicknotify=self.p.quicknotify)
138
+ else:
139
+ while True:
140
+ # Get order info to notify, if order is None break loop, otherwise get order's owner.
141
+ # If owner is None, default to first strategy
142
+ order = broker.get_notification()
143
+ if order is None:
144
+ break
145
+
146
+ owner = order.owner
147
+ if owner is None:
148
+ owner = self.runningstrats[0] # default
149
+ # Notify order info through first strategy
150
+ owner._addnotification(order, quicknotify=self.p.quicknotify)