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,1205 @@
1
+ #!/usr/bin/env python
2
+ """Generic CSV Data Feed Module - CSV file parsing.
3
+
4
+ This module provides the GenericCSVData feed for parsing CSV files
5
+ with customizable column mappings for backtesting.
6
+
7
+ Classes:
8
+ GenericCSVData: Parses CSV files with configurable column mappings.
9
+
10
+ Example:
11
+ >>> data = bt.feeds.GenericCSVData(
12
+ ... dataname='data.csv',
13
+ ... datetime=0,
14
+ ... open=1,
15
+ ... high=2,
16
+ ... low=3,
17
+ ... close=4,
18
+ ... volume=5
19
+ ... )
20
+ >>> cerebro.adddata(data)
21
+ """
22
+
23
+ import math
24
+ from datetime import date, datetime, timezone
25
+
26
+ from .. import feed
27
+ from ..dataseries import TimeFrame
28
+ from ..utils import date2num
29
+ from ..utils.log_message import get_logger
30
+ from ..utils.py3 import integer_types, string_types
31
+
32
+ logger = get_logger(__name__)
33
+
34
+ # Python 3.11+ has datetime.UTC, earlier versions use timezone.utc
35
+ UTC = timezone.utc
36
+ _INF = float("inf")
37
+ _NEG_INF = float("-inf")
38
+ _FLOAT = float
39
+ _OBJECT_SETATTR = object.__setattr__
40
+ _HOURS_PER_DAY = 24.0
41
+ _MINUTES_PER_DAY = 1440.0
42
+ _SECONDS_PER_DAY = 86400.0
43
+ _DAYS_BEFORE_MONTH = (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)
44
+ _DAYS_IN_MONTH = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
45
+
46
+
47
+ def _parse_ymd_compact(
48
+ date_text,
49
+ time_text=None,
50
+ fallback_format="%Y%m%d",
51
+ time_has_seconds=True,
52
+ ):
53
+ if len(date_text) == 8 and date_text.isdigit():
54
+ year = int(date_text[0:4])
55
+ month = int(date_text[4:6])
56
+ day = int(date_text[6:8])
57
+ if time_text is None:
58
+ return datetime(year, month, day)
59
+ return _parse_time(
60
+ year,
61
+ month,
62
+ day,
63
+ time_text,
64
+ date_text + "T" + time_text,
65
+ fallback_format,
66
+ time_has_seconds,
67
+ )
68
+
69
+ if time_text is None:
70
+ return datetime.strptime(date_text, fallback_format)
71
+ return datetime.strptime(date_text + "T" + time_text, fallback_format)
72
+
73
+
74
+ def _parse_ymd_separated(
75
+ date_text,
76
+ time_text=None,
77
+ separator="-",
78
+ fallback_format="%Y-%m-%d",
79
+ time_has_seconds=True,
80
+ ):
81
+ if len(date_text) == 10 and date_text[4] == separator and date_text[7] == separator:
82
+ year = int(date_text[0:4])
83
+ month = int(date_text[5:7])
84
+ day = int(date_text[8:10])
85
+ if time_text is None:
86
+ return datetime(year, month, day)
87
+ return _parse_time(
88
+ year,
89
+ month,
90
+ day,
91
+ time_text,
92
+ date_text + "T" + time_text,
93
+ fallback_format,
94
+ time_has_seconds,
95
+ )
96
+
97
+ if time_text is None:
98
+ return datetime.strptime(date_text, fallback_format)
99
+ return datetime.strptime(date_text + "T" + time_text, fallback_format)
100
+
101
+
102
+ def _parse_ymd_hms(date_text):
103
+ if (
104
+ len(date_text) == 19
105
+ and date_text[4] == "-"
106
+ and date_text[7] == "-"
107
+ and date_text[10] == " "
108
+ and date_text[13] == ":"
109
+ and date_text[16] == ":"
110
+ ):
111
+ return datetime(
112
+ int(date_text[0:4]),
113
+ int(date_text[5:7]),
114
+ int(date_text[8:10]),
115
+ int(date_text[11:13]),
116
+ int(date_text[14:16]),
117
+ int(date_text[17:19]),
118
+ )
119
+ return datetime.strptime(date_text, "%Y-%m-%d %H:%M:%S")
120
+
121
+
122
+ def _parse_time(
123
+ year,
124
+ month,
125
+ day,
126
+ time_text,
127
+ fallback_text,
128
+ fallback_format,
129
+ time_has_seconds,
130
+ ):
131
+ if not time_has_seconds and len(time_text) == 5 and time_text[2] == ":":
132
+ return datetime(
133
+ year,
134
+ month,
135
+ day,
136
+ int(time_text[0:2]),
137
+ int(time_text[3:5]),
138
+ )
139
+
140
+ if time_has_seconds and len(time_text) == 8 and time_text[2] == ":" and time_text[5] == ":":
141
+ hour = int(time_text[0:2])
142
+ minute = int(time_text[3:5])
143
+ second = int(time_text[6:8])
144
+ return datetime(year, month, day, hour, minute, second)
145
+
146
+ return datetime.strptime(fallback_text, fallback_format)
147
+
148
+
149
+ def _parse_time_num(time_text, time_has_seconds):
150
+ if not time_has_seconds and len(time_text) == 5 and time_text[2] == ":":
151
+ return int(time_text[0:2]), int(time_text[3:5]), 0
152
+
153
+ if time_has_seconds and len(time_text) == 8 and time_text[2] == ":" and time_text[5] == ":":
154
+ return int(time_text[0:2]), int(time_text[3:5]), int(time_text[6:8])
155
+
156
+ return None
157
+
158
+
159
+ def _ordinal_to_num(ordinal, hour=0, minute=0, second=0):
160
+ return math.fsum(
161
+ (
162
+ float(ordinal),
163
+ hour / _HOURS_PER_DAY,
164
+ minute / _MINUTES_PER_DAY,
165
+ second / _SECONDS_PER_DAY,
166
+ )
167
+ )
168
+
169
+
170
+ def _parse_ymd_compact_num(date_text, time_text=None, time_has_seconds=True):
171
+ if len(date_text) != 8 or not date_text.isdigit():
172
+ return None
173
+
174
+ ordinal = date(
175
+ int(date_text[0:4]),
176
+ int(date_text[4:6]),
177
+ int(date_text[6:8]),
178
+ ).toordinal()
179
+ if time_text is None:
180
+ return float(ordinal)
181
+
182
+ parsed_time = _parse_time_num(time_text, time_has_seconds)
183
+ if parsed_time is None:
184
+ return None
185
+
186
+ return _ordinal_to_num(ordinal, *parsed_time)
187
+
188
+
189
+ def _parse_ymd_separated_num(date_text, time_text=None, separator="-", time_has_seconds=True):
190
+ if not (len(date_text) == 10 and date_text[4] == separator and date_text[7] == separator):
191
+ return None
192
+
193
+ ordinal = date(
194
+ int(date_text[0:4]),
195
+ int(date_text[5:7]),
196
+ int(date_text[8:10]),
197
+ ).toordinal()
198
+ if time_text is None:
199
+ return float(ordinal)
200
+
201
+ parsed_time = _parse_time_num(time_text, time_has_seconds)
202
+ if parsed_time is None:
203
+ return None
204
+
205
+ return _ordinal_to_num(ordinal, *parsed_time)
206
+
207
+
208
+ def _parse_ymd_hms_num(date_text):
209
+ if not (
210
+ len(date_text) == 19
211
+ and date_text[4] == "-"
212
+ and date_text[7] == "-"
213
+ and date_text[10] == " "
214
+ and date_text[13] == ":"
215
+ and date_text[16] == ":"
216
+ ):
217
+ return None
218
+
219
+ ordinal = date(
220
+ int(date_text[0:4]),
221
+ int(date_text[5:7]),
222
+ int(date_text[8:10]),
223
+ ).toordinal()
224
+ return _ordinal_to_num(
225
+ ordinal,
226
+ int(date_text[11:13]),
227
+ int(date_text[14:16]),
228
+ int(date_text[17:19]),
229
+ )
230
+
231
+
232
+ def _build_datetime_parser(dtformat, tmformat, has_time):
233
+ if has_time:
234
+ fallback_format = dtformat + "T" + tmformat
235
+ if dtformat == "%Y%m%d" and tmformat in ("%H:%M", "%H:%M:%S"):
236
+ return lambda date_text, time_text: _parse_ymd_compact(
237
+ date_text,
238
+ time_text,
239
+ fallback_format,
240
+ tmformat == "%H:%M:%S",
241
+ )
242
+ if dtformat == "%Y-%m-%d" and tmformat in ("%H:%M", "%H:%M:%S"):
243
+ return lambda date_text, time_text: _parse_ymd_separated(
244
+ date_text,
245
+ time_text,
246
+ "-",
247
+ fallback_format,
248
+ tmformat == "%H:%M:%S",
249
+ )
250
+ if dtformat == "%Y.%m.%d" and tmformat in ("%H:%M", "%H:%M:%S"):
251
+ return lambda date_text, time_text: _parse_ymd_separated(
252
+ date_text,
253
+ time_text,
254
+ ".",
255
+ fallback_format,
256
+ tmformat == "%H:%M:%S",
257
+ )
258
+ if dtformat == "%Y/%m/%d" and tmformat in ("%H:%M", "%H:%M:%S"):
259
+ return lambda date_text, time_text: _parse_ymd_separated(
260
+ date_text,
261
+ time_text,
262
+ "/",
263
+ fallback_format,
264
+ tmformat == "%H:%M:%S",
265
+ )
266
+ return lambda date_text, time_text: datetime.strptime(
267
+ date_text + "T" + time_text,
268
+ fallback_format,
269
+ )
270
+
271
+ if dtformat == "%Y%m%d":
272
+ return lambda date_text, _: _parse_ymd_compact(date_text)
273
+ if dtformat == "%Y-%m-%d":
274
+ return lambda date_text, _: _parse_ymd_separated(date_text)
275
+ if dtformat == "%Y.%m.%d":
276
+ return lambda date_text, _: _parse_ymd_separated(
277
+ date_text,
278
+ separator=".",
279
+ fallback_format="%Y.%m.%d",
280
+ )
281
+ if dtformat == "%Y/%m/%d":
282
+ return lambda date_text, _: _parse_ymd_separated(
283
+ date_text,
284
+ separator="/",
285
+ fallback_format="%Y/%m/%d",
286
+ )
287
+ if dtformat == "%Y-%m-%d %H:%M:%S":
288
+ return lambda date_text, _: _parse_ymd_hms(date_text)
289
+ return lambda date_text, _: datetime.strptime(date_text, dtformat)
290
+
291
+
292
+ def _build_datetime_num_parser(dtformat, tmformat, has_time):
293
+ if has_time:
294
+ if dtformat == "%Y%m%d" and tmformat in ("%H:%M", "%H:%M:%S"):
295
+ return lambda date_text, time_text: _parse_ymd_compact_num(
296
+ date_text,
297
+ time_text,
298
+ tmformat == "%H:%M:%S",
299
+ )
300
+ if dtformat == "%Y-%m-%d" and tmformat in ("%H:%M", "%H:%M:%S"):
301
+ return lambda date_text, time_text: _parse_ymd_separated_num(
302
+ date_text,
303
+ time_text,
304
+ "-",
305
+ tmformat == "%H:%M:%S",
306
+ )
307
+ if dtformat == "%Y.%m.%d" and tmformat in ("%H:%M", "%H:%M:%S"):
308
+ return lambda date_text, time_text: _parse_ymd_separated_num(
309
+ date_text,
310
+ time_text,
311
+ ".",
312
+ tmformat == "%H:%M:%S",
313
+ )
314
+ if dtformat == "%Y/%m/%d" and tmformat in ("%H:%M", "%H:%M:%S"):
315
+ return lambda date_text, time_text: _parse_ymd_separated_num(
316
+ date_text,
317
+ time_text,
318
+ "/",
319
+ tmformat == "%H:%M:%S",
320
+ )
321
+ return None
322
+
323
+ if dtformat == "%Y%m%d":
324
+ return lambda date_text, _: _parse_ymd_compact_num(date_text)
325
+ if dtformat == "%Y-%m-%d":
326
+ return lambda date_text, _: _parse_ymd_separated_num(date_text)
327
+ if dtformat == "%Y.%m.%d":
328
+ return lambda date_text, _: _parse_ymd_separated_num(date_text, separator=".")
329
+ if dtformat == "%Y/%m/%d":
330
+ return lambda date_text, _: _parse_ymd_separated_num(date_text, separator="/")
331
+ if dtformat == "%Y-%m-%d %H:%M:%S":
332
+ return lambda date_text, _: _parse_ymd_hms_num(date_text)
333
+ return None
334
+
335
+
336
+ class GenericCSVData(feed.CSVDataBase):
337
+ """Parses a CSV file according to the order and field presence defined by the
338
+ parameters
339
+
340
+ Specific parameters (or specific meaning):
341
+
342
+ - ``dataname``: The filename to parse or a file-like object
343
+
344
+ - The lines parameters (datetime, open, high ...) take numeric values
345
+
346
+ A value of -1 indicates absence of that field in the CSV source
347
+
348
+ - If ``time`` is present (parameter time >=0), the source contains
349
+ separated fields for date and time, which will be combined
350
+
351
+ - ``nullvalue``
352
+
353
+ Value that will be used if a value which should be there is missing
354
+ (the CSV field is empty)
355
+
356
+ - ``dtformat``: Format used to parse the datetime CSV field. See the
357
+ python strptime/strftime documentation for the format.
358
+
359
+ If a numeric value is specified, it will be interpreted as follows
360
+
361
+ - ``1``: The value is a Unix timestamp of a type ``int`` representing
362
+ the number of seconds since Jan 1st, 1970
363
+
364
+ - ``2``: The value is a Unix timestamp of a type ``float``
365
+
366
+ If a **callable** is passed
367
+
368
+ - It will accept a string and return a `datetime.datetime` python
369
+ instance
370
+
371
+ - ``tmformat``: Format used to parse the time CSV field if "present"
372
+ (the default for the "time" CSV field is not to be present)
373
+
374
+ """
375
+
376
+ # Common parameters for csv data
377
+ params = (
378
+ ("nullvalue", float("NaN")),
379
+ ("dtformat", "%Y-%m-%d %H:%M:%S"),
380
+ ("tmformat", "%H:%M:%S"),
381
+ ("datetime", 0),
382
+ ("time", -1),
383
+ ("open", 1),
384
+ ("high", 2),
385
+ ("low", 3),
386
+ ("close", 4),
387
+ ("volume", 5),
388
+ ("openinterest", 6),
389
+ )
390
+
391
+ def __init__(self, *args, **kwargs):
392
+ """Initialize the Generic CSV data feed.
393
+
394
+ Args:
395
+ *args: Positional arguments for data feed configuration.
396
+ **kwargs: Keyword arguments for data feed configuration.
397
+ """
398
+ super().__init__(*args, **kwargs)
399
+ self._dtconvert = None
400
+ self._dtstr = None
401
+ self._has_time = None
402
+
403
+ def start(self):
404
+ """Start the Generic CSV data feed.
405
+
406
+ Sets up datetime conversion based on dtformat parameter.
407
+ """
408
+ super().start()
409
+ p = self.p
410
+ self._datetime_idx = p.datetime
411
+ self._time_idx = p.time
412
+ self._timeframe = p.timeframe
413
+ self._sessionend = p.sessionend
414
+ self._datetime_line = self.lines.datetime
415
+ self._nullvalue = p.nullvalue
416
+ field_cache = []
417
+ missing_field_cache = []
418
+ direct_field_cache = []
419
+ direct_missing_field_cache = []
420
+ last_alias = self._getlinealias(0)
421
+ for linefield in self.getlinealiases():
422
+ if linefield == "datetime":
423
+ continue
424
+
425
+ csvidx = getattr(p, linefield)
426
+ line = getattr(self.lines, linefield)
427
+ tick_name = "tick_" + linefield
428
+ is_last = linefield == last_alias
429
+ if csvidx is None or csvidx < 0:
430
+ value = float(p.nullvalue)
431
+ if value in (_INF, _NEG_INF):
432
+ value = line._default_value
433
+ missing_field_cache.append((line, value))
434
+ direct_missing_field_cache.append((line, value, tick_name, is_last))
435
+ else:
436
+ field_cache.append((csvidx, line))
437
+ direct_field_cache.append((csvidx, line, tick_name, is_last))
438
+ self._field_cache = tuple(field_cache)
439
+ self._missing_field_cache = tuple(missing_field_cache)
440
+ self._direct_field_cache = tuple(direct_field_cache)
441
+ self._direct_missing_field_cache = tuple(direct_missing_field_cache)
442
+ # If string type, set self._dtstr to True, otherwise default is False
443
+ self._dtstr = False
444
+ if isinstance(p.dtformat, string_types):
445
+ self._dtstr = True
446
+ self._has_time = self._time_idx >= 0
447
+ if self._has_time and p.dtformat == "%Y%m%d" and p.tmformat == "%H:%M:%S":
448
+ self._dt_num_fast = 1
449
+ elif self._has_time and p.dtformat == "%Y%m%d" and p.tmformat == "%H:%M":
450
+ self._dt_num_fast = 2
451
+ else:
452
+ self._dt_num_fast = 0
453
+ self._dtconvert = _build_datetime_parser(
454
+ p.dtformat,
455
+ p.tmformat,
456
+ self._has_time,
457
+ )
458
+ self._dtconvert_num = _build_datetime_num_parser(
459
+ p.dtformat,
460
+ p.tmformat,
461
+ self._has_time,
462
+ )
463
+ # If integer, set time conversion method based on different integer values
464
+ elif isinstance(p.dtformat, integer_types):
465
+ self._dtconvert_num = None
466
+ self._dt_num_fast = 0
467
+ idt = int(p.dtformat)
468
+ if idt == 1:
469
+ # self._dtconvert = lambda x: datetime.utcfromtimestamp(int(x))
470
+ self._dtconvert = lambda x, _: datetime.fromtimestamp(int(x), UTC)
471
+ elif idt == 2:
472
+ # self._dtconvert = lambda x: datetime.utcfromtimestamp(float(x))
473
+ self._dtconvert = lambda x, _: datetime.fromtimestamp(float(x), UTC)
474
+ # If dtformat is callable, conversion method is itself
475
+ else: # assume callable
476
+ dtformat = p.dtformat
477
+ self._dtconvert = lambda x, _: dtformat(x)
478
+ self._dtconvert_num = None
479
+ self._dt_num_fast = 0
480
+
481
+ def _runnext_direct_load_ready(self):
482
+ """Return whether Cerebro can call load() directly in single-data runnext."""
483
+ try:
484
+ return object.__getattribute__(self, "_runnext_direct_load_ready_cache")
485
+ except AttributeError:
486
+ logger.debug("csvgeneric:485 ignored AttributeError")
487
+
488
+ try:
489
+ ready = (
490
+ type(self) is GenericCSVData
491
+ and self.f is not None
492
+ and object.__getattribute__(self, "_tzinput") is None
493
+ and object.__getattribute__(self, "fromdate") == _NEG_INF
494
+ and object.__getattribute__(self, "todate") == _INF
495
+ and not self._filters
496
+ and not self._barstack
497
+ and not self._barstash
498
+ and not self.resampling
499
+ and not self.replaying
500
+ and not self._clone
501
+ )
502
+ except AttributeError:
503
+ ready = False
504
+
505
+ object.__setattr__(self, "_runnext_direct_load_ready_cache", ready)
506
+ if ready:
507
+ object.__setattr__(self, "_use_direct_csv_load", True)
508
+ try:
509
+ if self._runnext_direct_ymdhms_ohlcv_ready():
510
+ object.__setattr__(
511
+ self,
512
+ "_runnext_direct_load",
513
+ self._load_direct_ymdhms_ohlcv,
514
+ )
515
+ except AttributeError:
516
+ logger.debug("csvgeneric:515 ignored AttributeError")
517
+ return ready
518
+
519
+ def _runnext_direct_ymdhms_ohlcv_ready(self):
520
+ """Return whether the narrow runnext CSV loader can be used."""
521
+ try:
522
+ return object.__getattribute__(self, "_runnext_direct_ymdhms_ohlcv_ready_cache")
523
+ except AttributeError:
524
+ logger.debug("csvgeneric:523 ignored AttributeError")
525
+
526
+ p = self.p
527
+ try:
528
+ lines = (
529
+ self.lines.open,
530
+ self.lines.high,
531
+ self.lines.low,
532
+ self.lines.close,
533
+ self.lines.volume,
534
+ self.lines.openinterest,
535
+ self.lines.datetime,
536
+ )
537
+ line0 = lines[0]
538
+ line0_idx = line0._idx
539
+ line0_lencount = line0.lencount
540
+ ready = (
541
+ type(self) is GenericCSVData
542
+ and self.separator == ","
543
+ and self._dt_num_fast == 1
544
+ and self._timeframe < TimeFrame.Days
545
+ and self._datetime_idx == 0
546
+ and self._time_idx == 1
547
+ and p.open == 2
548
+ and p.high == 3
549
+ and p.low == 4
550
+ and p.close == 5
551
+ and p.volume == 6
552
+ and (p.openinterest is None or p.openinterest < 0)
553
+ and all(line.mode != line.QBuffer and line._clock is None for line in lines)
554
+ and all(not line.bindings for line in lines)
555
+ and lines[1]._idx == line0_idx
556
+ and lines[1].lencount == line0_lencount
557
+ and lines[2]._idx == line0_idx
558
+ and lines[2].lencount == line0_lencount
559
+ and lines[3]._idx == line0_idx
560
+ and lines[3].lencount == line0_lencount
561
+ and lines[4]._idx == line0_idx
562
+ and lines[4].lencount == line0_lencount
563
+ and lines[5]._idx == line0_idx
564
+ and lines[5].lencount == line0_lencount
565
+ and lines[6]._idx == line0_idx
566
+ and lines[6].lencount == line0_lencount
567
+ )
568
+ except AttributeError:
569
+ ready = False
570
+ lines = None
571
+
572
+ object.__setattr__(self, "_runnext_direct_ymdhms_ohlcv_ready_cache", ready)
573
+ if ready:
574
+ object.__setattr__(self, "_fast_ymdhms_lines", lines)
575
+ object.__setattr__(
576
+ self,
577
+ "_fast_ymdhms_appends",
578
+ (
579
+ lines[0].array.append,
580
+ lines[1].array.append,
581
+ lines[2].array.append,
582
+ lines[3].array.append,
583
+ lines[4].array.append,
584
+ lines[5].array.append,
585
+ lines[6].array.append,
586
+ ),
587
+ )
588
+ object.__setattr__(self, "_fast_ymdhms_ohlcv_lines", lines[:6])
589
+ object.__setattr__(
590
+ self,
591
+ "_fast_ymdhms_ohlcv_appends",
592
+ tuple(line.array.append for line in lines[:6]),
593
+ )
594
+ object.__setattr__(self, "_fast_ymdhms_datetime_append", lines[6].array.append)
595
+ object.__setattr__(self, "_fast_ymdhms_readline", self.f.readline)
596
+ object.__setattr__(self, "_fast_ymdhms_openinterest_default", lines[5]._default_value)
597
+ object.__setattr__(self, "_fast_ymdhms_tick_dict", self.__dict__)
598
+ object.__setattr__(self, "_load_forward_lines", lines)
599
+ object.__setattr__(self, "_use_direct_ymdhms_load", True)
600
+ object.__setattr__(self, "_fast_ymdhms_ohlcv_fields", True)
601
+ object.__setattr__(self, "_direct_ymdhms_last_datefield", "")
602
+ object.__setattr__(self, "_direct_ymdhms_last_ordinal", 0)
603
+ object.__setattr__(self, "_direct_ymdhms_time_fractions", {})
604
+ return ready
605
+
606
+ def _load_direct_ymdhms_ohlcv(self, _float=_FLOAT, _inf=_INF, _neg_inf=_NEG_INF):
607
+ """Load a standard YMD/HMS OHLCV CSV row for single-data runnext."""
608
+ (
609
+ open_line,
610
+ high_line,
611
+ low_line,
612
+ close_line,
613
+ volume_line,
614
+ openinterest_line,
615
+ datetime_line,
616
+ ) = self._fast_ymdhms_lines
617
+ (
618
+ open_append,
619
+ high_append,
620
+ low_append,
621
+ close_append,
622
+ volume_append,
623
+ openinterest_append,
624
+ datetime_append,
625
+ ) = self._fast_ymdhms_appends
626
+
627
+ line = self._fast_ymdhms_readline()
628
+ if not line:
629
+ return False
630
+
631
+ try:
632
+ direct_layout = (
633
+ line[8] == "," and line[17] == "," and line[11] == ":" and line[14] == ":"
634
+ )
635
+ except IndexError:
636
+ direct_layout = False
637
+
638
+ if not direct_layout:
639
+ open_line._idx += 1
640
+ open_line.lencount += 1
641
+ open_append(open_line._default_value)
642
+ high_line._idx += 1
643
+ high_line.lencount += 1
644
+ high_append(high_line._default_value)
645
+ low_line._idx += 1
646
+ low_line.lencount += 1
647
+ low_append(low_line._default_value)
648
+ close_line._idx += 1
649
+ close_line.lencount += 1
650
+ close_append(close_line._default_value)
651
+ volume_line._idx += 1
652
+ volume_line.lencount += 1
653
+ volume_append(volume_line._default_value)
654
+ openinterest_line._idx += 1
655
+ openinterest_line.lencount += 1
656
+ openinterest_append(openinterest_line._default_value)
657
+ datetime_line._idx += 1
658
+ datetime_line.lencount += 1
659
+ datetime_append(datetime_line._default_value)
660
+ loadret = self._loadline(line.rstrip("\n").split(self.separator))
661
+ if not loadret:
662
+ self.backwards(force=True)
663
+ return loadret
664
+ return True
665
+
666
+ linetokens = line.split(",", 7)
667
+ datefield = line[:8]
668
+ if datefield == self._direct_ymdhms_last_datefield:
669
+ ordinal = self._direct_ymdhms_last_ordinal
670
+ else:
671
+ year = int(line[0:4])
672
+ month = int(line[4:6])
673
+ day = int(line[6:8])
674
+ year_minus_one = year - 1
675
+ leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
676
+ if (
677
+ year < 1
678
+ or month < 1
679
+ or month > 12
680
+ or day < 1
681
+ or day > (_DAYS_IN_MONTH[month] + (1 if month == 2 and leap else 0))
682
+ ):
683
+ date(year, month, day).toordinal()
684
+ ordinal = (
685
+ year_minus_one * 365
686
+ + year_minus_one // 4
687
+ - year_minus_one // 100
688
+ + year_minus_one // 400
689
+ + _DAYS_BEFORE_MONTH[month]
690
+ + day
691
+ )
692
+ if month > 2 and leap:
693
+ ordinal += 1
694
+ _OBJECT_SETATTR(self, "_direct_ymdhms_last_datefield", datefield)
695
+ _OBJECT_SETATTR(self, "_direct_ymdhms_last_ordinal", ordinal)
696
+ timefield = line[9:17]
697
+ time_fractions = self._direct_ymdhms_time_fractions
698
+ try:
699
+ day_fraction = time_fractions[timefield]
700
+ except KeyError:
701
+ seconds = int(line[9:11]) * 3600 + int(line[12:14]) * 60 + int(line[15:17])
702
+ day_fraction = seconds / _SECONDS_PER_DAY
703
+ time_fractions[timefield] = day_fraction
704
+ dtnum = ordinal + day_fraction
705
+
706
+ nullvalue = self._nullvalue
707
+ try:
708
+ open_value = _float(linetokens[2])
709
+ high_value = _float(linetokens[3])
710
+ low_value = _float(linetokens[4])
711
+ close_value = _float(linetokens[5])
712
+ volume_value = _float(linetokens[6])
713
+ except ValueError:
714
+ open_value = _float(linetokens[2] or nullvalue)
715
+ high_value = _float(linetokens[3] or nullvalue)
716
+ low_value = _float(linetokens[4] or nullvalue)
717
+ close_value = _float(linetokens[5] or nullvalue)
718
+ volume_value = _float(linetokens[6] or nullvalue)
719
+ if open_value in (_inf, _neg_inf):
720
+ open_value = open_line._default_value
721
+ if high_value in (_inf, _neg_inf):
722
+ high_value = high_line._default_value
723
+ if low_value in (_inf, _neg_inf):
724
+ low_value = low_line._default_value
725
+ if close_value in (_inf, _neg_inf):
726
+ close_value = close_line._default_value
727
+ if volume_value in (_inf, _neg_inf):
728
+ volume_value = volume_line._default_value
729
+
730
+ openinterest_value = self._fast_ymdhms_openinterest_default
731
+ datetime_value = dtnum if dtnum >= 1.0 else 1.0
732
+ next_idx = open_line._idx + 1
733
+ next_lencount = open_line.lencount + 1
734
+ open_line._idx = next_idx
735
+ open_line.lencount = next_lencount
736
+ open_append(open_value)
737
+ high_line._idx = next_idx
738
+ high_line.lencount = next_lencount
739
+ high_append(high_value)
740
+ low_line._idx = next_idx
741
+ low_line.lencount = next_lencount
742
+ low_append(low_value)
743
+ close_line._idx = next_idx
744
+ close_line.lencount = next_lencount
745
+ close_append(close_value)
746
+ volume_line._idx = next_idx
747
+ volume_line.lencount = next_lencount
748
+ volume_append(volume_value)
749
+ openinterest_line._idx = next_idx
750
+ openinterest_line.lencount = next_lencount
751
+ openinterest_append(openinterest_value)
752
+ datetime_line._idx = next_idx
753
+ datetime_line.lencount = next_lencount
754
+ datetime_append(datetime_value)
755
+
756
+ tick_values = self._fast_ymdhms_tick_dict
757
+ tick_values["tick_open"] = open_value
758
+ tick_values["tick_high"] = high_value
759
+ tick_values["tick_low"] = low_value
760
+ tick_values["tick_close"] = close_value
761
+ tick_values["tick_volume"] = volume_value
762
+ tick_values["tick_openinterest"] = openinterest_value
763
+ tick_values["tick_last"] = close_value
764
+ tick_values["_tick_direct_filled"] = True
765
+ return True
766
+
767
+ def load(self):
768
+ """Load one CSV bar through a narrow no-filter fast path."""
769
+ try:
770
+ use_direct_csv_load = object.__getattribute__(self, "_use_direct_csv_load")
771
+ except AttributeError:
772
+ use_direct_csv_load = (
773
+ object.__getattribute__(self, "_tzinput") is None
774
+ and object.__getattribute__(self, "fromdate") == _NEG_INF
775
+ and object.__getattribute__(self, "todate") == _INF
776
+ and not self._filters
777
+ and not self._barstack
778
+ and not self._barstash
779
+ )
780
+ object.__setattr__(self, "_use_direct_csv_load", use_direct_csv_load)
781
+
782
+ if not use_direct_csv_load or self._filters or self._barstack or self._barstash:
783
+ return super().load()
784
+
785
+ try:
786
+ forward_lines = self._load_forward_lines
787
+ except AttributeError:
788
+ try:
789
+ lines = self.lines.lines
790
+ if any(line.mode == line.QBuffer or line._clock is not None for line in lines):
791
+ self._load_forward_lines = None
792
+ forward_lines = None
793
+ else:
794
+ forward_lines = tuple(lines)
795
+ self._load_forward_lines = forward_lines
796
+ except AttributeError:
797
+ self._load_forward_lines = None
798
+ forward_lines = None
799
+
800
+ if forward_lines is None:
801
+ self.forward()
802
+ else:
803
+ for line in forward_lines:
804
+ line._idx += 1
805
+ line.lencount += 1
806
+ line.array.append(line._default_value)
807
+
808
+ f = self.f
809
+ if f is None:
810
+ self.backwards(force=True)
811
+ return False
812
+
813
+ line = f.readline()
814
+ if not line:
815
+ self.backwards(force=True)
816
+ return False
817
+
818
+ linetokens = None
819
+ if forward_lines is not None:
820
+ try:
821
+ use_direct_ymdhms_load = object.__getattribute__(self, "_use_direct_ymdhms_load")
822
+ except AttributeError:
823
+ use_direct_ymdhms_load = (
824
+ type(self) is GenericCSVData
825
+ and self._dt_num_fast == 1
826
+ and self._timeframe < TimeFrame.Days
827
+ and not self._datetime_line.bindings
828
+ and all(not line.bindings for _, line, _, _ in self._direct_field_cache)
829
+ and all(not line.bindings for line, _, _, _ in self._direct_missing_field_cache)
830
+ )
831
+ object.__setattr__(self, "_use_direct_ymdhms_load", use_direct_ymdhms_load)
832
+
833
+ if use_direct_ymdhms_load:
834
+ linetokens = line.split(self.separator)
835
+ dtfield = linetokens[self._datetime_idx]
836
+ timefield = linetokens[self._time_idx]
837
+ if (
838
+ dtfield[8:9] == ""
839
+ and timefield[8:9] == ""
840
+ and timefield[2:3] == ":"
841
+ and timefield[5:6] == ":"
842
+ ):
843
+ try:
844
+ year = int(dtfield[0:4])
845
+ month = int(dtfield[4:6])
846
+ day = int(dtfield[6:8])
847
+ hour = int(timefield[0:2])
848
+ minute = int(timefield[3:5])
849
+ second = int(timefield[6:8])
850
+ except ValueError:
851
+ logger.debug("csvgeneric:850 ignored ValueError")
852
+ else:
853
+ year_minus_one = year - 1
854
+ leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
855
+ if (
856
+ year < 1
857
+ or month < 1
858
+ or month > 12
859
+ or day < 1
860
+ or day > (_DAYS_IN_MONTH[month] + (1 if month == 2 and leap else 0))
861
+ ):
862
+ date(year, month, day).toordinal()
863
+ ordinal = (
864
+ year_minus_one * 365
865
+ + year_minus_one // 4
866
+ - year_minus_one // 100
867
+ + year_minus_one // 400
868
+ + _DAYS_BEFORE_MONTH[month]
869
+ + day
870
+ )
871
+ if month > 2 and leap:
872
+ ordinal += 1
873
+ dtnum = (
874
+ float(ordinal) + (hour * 3600 + minute * 60 + second) / _SECONDS_PER_DAY
875
+ )
876
+
877
+ line_datetime = self._datetime_line
878
+ datetime_idx = line_datetime._idx
879
+ if datetime_idx < 0:
880
+ line_datetime[0] = dtnum
881
+ else:
882
+ try:
883
+ line_datetime.array[datetime_idx] = dtnum if dtnum >= 1.0 else 1.0
884
+ except IndexError:
885
+ line_datetime[0] = dtnum
886
+
887
+ nullvalue = self._nullvalue
888
+ set_attr = object.__setattr__
889
+ try:
890
+ fast_ymdhms_ohlcv_fields = self._fast_ymdhms_ohlcv_fields
891
+ except AttributeError:
892
+ p = self.p
893
+ fast_ymdhms_ohlcv_fields = (
894
+ self.separator == ","
895
+ and self._datetime_idx == 0
896
+ and self._time_idx == 1
897
+ and p.open == 2
898
+ and p.high == 3
899
+ and p.low == 4
900
+ and p.close == 5
901
+ and p.volume == 6
902
+ and (p.openinterest is None or p.openinterest < 0)
903
+ )
904
+ object.__setattr__(
905
+ self,
906
+ "_fast_ymdhms_ohlcv_fields",
907
+ fast_ymdhms_ohlcv_fields,
908
+ )
909
+ if fast_ymdhms_ohlcv_fields:
910
+ object.__setattr__(
911
+ self,
912
+ "_fast_ymdhms_ohlcv_lines",
913
+ (
914
+ self.lines.open,
915
+ self.lines.high,
916
+ self.lines.low,
917
+ self.lines.close,
918
+ self.lines.volume,
919
+ self.lines.openinterest,
920
+ ),
921
+ )
922
+
923
+ if fast_ymdhms_ohlcv_fields:
924
+ try:
925
+ open_value = float(linetokens[2] or nullvalue)
926
+ high_value = float(linetokens[3] or nullvalue)
927
+ low_value = float(linetokens[4] or nullvalue)
928
+ close_value = float(linetokens[5] or nullvalue)
929
+ volume_value = float(linetokens[6] or nullvalue)
930
+ except (IndexError, ValueError, TypeError):
931
+ logger.debug(
932
+ "csvgeneric:930 ignored IndexError,ValueError,TypeError"
933
+ )
934
+ else:
935
+ (
936
+ open_line,
937
+ high_line,
938
+ low_line,
939
+ close_line,
940
+ volume_line,
941
+ openinterest_line,
942
+ ) = self._fast_ymdhms_ohlcv_lines
943
+ if open_value in (_INF, _NEG_INF):
944
+ open_value = open_line._default_value
945
+ if high_value in (_INF, _NEG_INF):
946
+ high_value = high_line._default_value
947
+ if low_value in (_INF, _NEG_INF):
948
+ low_value = low_line._default_value
949
+ if close_value in (_INF, _NEG_INF):
950
+ close_value = close_line._default_value
951
+ if volume_value in (_INF, _NEG_INF):
952
+ volume_value = volume_line._default_value
953
+
954
+ openinterest_value = openinterest_line._default_value
955
+ open_line.array[open_line._idx] = open_value
956
+ high_line.array[high_line._idx] = high_value
957
+ low_line.array[low_line._idx] = low_value
958
+ close_line.array[close_line._idx] = close_value
959
+ volume_line.array[volume_line._idx] = volume_value
960
+ openinterest_line.array[openinterest_line._idx] = openinterest_value
961
+ set_attr(self, "tick_open", open_value)
962
+ set_attr(self, "tick_high", high_value)
963
+ set_attr(self, "tick_low", low_value)
964
+ set_attr(self, "tick_close", close_value)
965
+ set_attr(self, "tick_volume", volume_value)
966
+ set_attr(self, "tick_openinterest", openinterest_value)
967
+ set_attr(self, "tick_last", close_value)
968
+ set_attr(self, "_tick_direct_filled", True)
969
+ return True
970
+
971
+ tick_last = None
972
+ for csvidx, field_line, tick_name, is_last in self._direct_field_cache:
973
+ csvfield = linetokens[csvidx]
974
+ if csvfield == "":
975
+ csvfield = nullvalue
976
+ value = float(csvfield)
977
+ if value in (_INF, _NEG_INF):
978
+ value = field_line._default_value
979
+
980
+ field_idx = field_line._idx
981
+ if field_idx < 0:
982
+ field_line[0] = value
983
+ else:
984
+ try:
985
+ field_line.array[field_idx] = value
986
+ except IndexError:
987
+ field_line[0] = value
988
+
989
+ set_attr(self, tick_name, value)
990
+ if is_last:
991
+ tick_last = value
992
+
993
+ for (
994
+ field_line,
995
+ value,
996
+ tick_name,
997
+ is_last,
998
+ ) in self._direct_missing_field_cache:
999
+ field_idx = field_line._idx
1000
+ if field_idx < 0:
1001
+ field_line[0] = value
1002
+ else:
1003
+ try:
1004
+ field_line.array[field_idx] = value
1005
+ except IndexError:
1006
+ field_line[0] = value
1007
+
1008
+ set_attr(self, tick_name, value)
1009
+ if is_last:
1010
+ tick_last = value
1011
+
1012
+ if tick_last is None:
1013
+ tick_last = self._datetime_line.array[datetime_idx]
1014
+ set_attr(self, "tick_last", tick_last)
1015
+ set_attr(self, "_tick_direct_filled", True)
1016
+ return True
1017
+
1018
+ linetokens = line.rstrip("\n").split(self.separator)
1019
+ loadret = self._loadline(linetokens)
1020
+ if not loadret:
1021
+ self.backwards(force=True)
1022
+ return loadret
1023
+
1024
+ return True
1025
+
1026
+ # After reading csv file line, split line's data into linetokens, then further processing
1027
+ def _loadline(self, linetokens):
1028
+ line_datetime = self._datetime_line
1029
+
1030
+ # Datetime needs special treatment
1031
+ # First get specific date based on datetime order
1032
+ dtfield = linetokens[self._datetime_idx]
1033
+ timefield = linetokens[self._time_idx] if self._has_time else None
1034
+
1035
+ dtnum = None
1036
+ if not self._tzinput and self._timeframe < TimeFrame.Days:
1037
+ dt_num_fast = self._dt_num_fast
1038
+ if dt_num_fast == 1:
1039
+ if (
1040
+ dtfield[8:9] == ""
1041
+ and timefield[8:9] == ""
1042
+ and timefield[2:3] == ":"
1043
+ and timefield[5:6] == ":"
1044
+ ):
1045
+ try:
1046
+ year = int(dtfield[0:4])
1047
+ month = int(dtfield[4:6])
1048
+ day = int(dtfield[6:8])
1049
+ hour = int(timefield[0:2])
1050
+ minute = int(timefield[3:5])
1051
+ second = int(timefield[6:8])
1052
+ except ValueError:
1053
+ logger.debug("csvgeneric:1050 ignored ValueError")
1054
+ else:
1055
+ year_minus_one = year - 1
1056
+ leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
1057
+ if (
1058
+ year < 1
1059
+ or month < 1
1060
+ or month > 12
1061
+ or day < 1
1062
+ or day > (_DAYS_IN_MONTH[month] + (1 if month == 2 and leap else 0))
1063
+ ):
1064
+ date(year, month, day).toordinal()
1065
+ ordinal = (
1066
+ year_minus_one * 365
1067
+ + year_minus_one // 4
1068
+ - year_minus_one // 100
1069
+ + year_minus_one // 400
1070
+ + _DAYS_BEFORE_MONTH[month]
1071
+ + day
1072
+ )
1073
+ if month > 2 and leap:
1074
+ ordinal += 1
1075
+ seconds = hour * 3600 + minute * 60 + second
1076
+ dtnum = float(ordinal) + seconds / _SECONDS_PER_DAY
1077
+ elif dt_num_fast == 2:
1078
+ if dtfield[8:9] == "" and timefield[5:6] == "" and timefield[2:3] == ":":
1079
+ try:
1080
+ year = int(dtfield[0:4])
1081
+ month = int(dtfield[4:6])
1082
+ day = int(dtfield[6:8])
1083
+ hour = int(timefield[0:2])
1084
+ minute = int(timefield[3:5])
1085
+ except ValueError:
1086
+ logger.debug("csvgeneric:1083 ignored ValueError")
1087
+ else:
1088
+ year_minus_one = year - 1
1089
+ leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
1090
+ if (
1091
+ year < 1
1092
+ or month < 1
1093
+ or month > 12
1094
+ or day < 1
1095
+ or day > (_DAYS_IN_MONTH[month] + (1 if month == 2 and leap else 0))
1096
+ ):
1097
+ date(year, month, day).toordinal()
1098
+ ordinal = (
1099
+ year_minus_one * 365
1100
+ + year_minus_one // 4
1101
+ - year_minus_one // 100
1102
+ + year_minus_one // 400
1103
+ + _DAYS_BEFORE_MONTH[month]
1104
+ + day
1105
+ )
1106
+ if month > 2 and leap:
1107
+ ordinal += 1
1108
+ seconds = hour * 3600 + minute * 60
1109
+ dtnum = float(ordinal) + seconds / _SECONDS_PER_DAY
1110
+ else:
1111
+ dtconvert_num = self._dtconvert_num
1112
+ if dtconvert_num is not None:
1113
+ dtnum = dtconvert_num(dtfield, timefield)
1114
+
1115
+ if dtnum is None:
1116
+ dt = self._dtconvert(dtfield, timefield)
1117
+
1118
+ # If trading interval is greater than or equal to day
1119
+ if self._timeframe >= TimeFrame.Days:
1120
+ # check if the expected end of session is larger than parsed
1121
+ # If _tzinput is True, need to localize date, otherwise date remains original
1122
+ if self._tzinput:
1123
+ dtin = self._tzinput.localize(dt) # pytz compatible-ized
1124
+ else:
1125
+ dtin = dt
1126
+ # Use date2num to convert date to number
1127
+ dtnum = date2num(dtin) # utc'ize
1128
+ # Combine date and sessionend, convert to number
1129
+ dteos = datetime.combine(dt.date(), self._sessionend)
1130
+ dteosnum = self.date2num(dteos) # utc'ize
1131
+ # If number converted from combined sessionend date is greater than converted date number, use former number as time
1132
+ if dteosnum > dtnum:
1133
+ dtnum = dteosnum
1134
+ # If not greater, if self._tzinput is True, directly convert dt to time, if not True, use original dtnum
1135
+ else:
1136
+ # Avoid reconversion if already converted dtin == dt
1137
+ dtnum = date2num(dt) if self._tzinput else dtnum
1138
+ # If trading cycle is less than day, convert time directly
1139
+ else:
1140
+ dtnum = date2num(dt)
1141
+
1142
+ if line_datetime.bindings:
1143
+ line_datetime[0] = dtnum
1144
+ else:
1145
+ idx = line_datetime._idx
1146
+ if idx < 0:
1147
+ line_datetime[0] = dtnum
1148
+ else:
1149
+ try:
1150
+ line_datetime.array[idx] = dtnum if dtnum >= 1.0 else 1.0
1151
+ except IndexError:
1152
+ line_datetime[0] = dtnum
1153
+
1154
+ if not self._tzinput and (dtnum < self.fromdate or dtnum > self.todate):
1155
+ return True
1156
+
1157
+ # Process cached fields
1158
+ nullvalue = self._nullvalue
1159
+ for csvidx, line in self._field_cache:
1160
+ csvfield = linetokens[csvidx]
1161
+ if csvfield == "":
1162
+ csvfield = nullvalue
1163
+ value = float(csvfield)
1164
+ if value in (_INF, _NEG_INF):
1165
+ value = line._default_value
1166
+
1167
+ if line.bindings:
1168
+ line[0] = value
1169
+ continue
1170
+
1171
+ idx = line._idx
1172
+ if idx < 0:
1173
+ line[0] = value
1174
+ continue
1175
+
1176
+ try:
1177
+ line.array[idx] = value
1178
+ except IndexError:
1179
+ line[0] = value
1180
+
1181
+ for line, value in self._missing_field_cache:
1182
+ if line.bindings:
1183
+ line[0] = value
1184
+ continue
1185
+
1186
+ idx = line._idx
1187
+ if idx < 0:
1188
+ line[0] = value
1189
+ continue
1190
+
1191
+ try:
1192
+ line.array[idx] = value
1193
+ except IndexError:
1194
+ line[0] = value
1195
+
1196
+ return True
1197
+
1198
+
1199
+ class GenericCSV(feed.CSVFeedBase):
1200
+ """Generic CSV feed class.
1201
+
1202
+ Wrapper class for GenericCSVData feed functionality.
1203
+ """
1204
+
1205
+ DataCls = GenericCSVData