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.
- back_trader_python-1.4.0.dist-info/METADATA +1491 -0
- back_trader_python-1.4.0.dist-info/RECORD +465 -0
- back_trader_python-1.4.0.dist-info/WHEEL +5 -0
- back_trader_python-1.4.0.dist-info/licenses/LICENSE +674 -0
- back_trader_python-1.4.0.dist-info/top_level.txt +1 -0
- backtrader/__init__.py +148 -0
- backtrader/_cerebro/__init__.py +5 -0
- backtrader/_cerebro/channel.py +382 -0
- backtrader/_cerebro/execution.py +377 -0
- backtrader/_cerebro/lifecycle.py +143 -0
- backtrader/_cerebro/notifications.py +150 -0
- backtrader/_cerebro/presentation.py +230 -0
- backtrader/_cerebro/registry.py +593 -0
- backtrader/_cerebro/runnext.py +551 -0
- backtrader/_cerebro/runonce.py +142 -0
- backtrader/analyzer.py +594 -0
- backtrader/analyzers/__init__.py +50 -0
- backtrader/analyzers/annualreturn.py +226 -0
- backtrader/analyzers/calmar.py +165 -0
- backtrader/analyzers/drawdown.py +287 -0
- backtrader/analyzers/leverage.py +112 -0
- backtrader/analyzers/logreturnsrolling.py +190 -0
- backtrader/analyzers/periodstats.py +153 -0
- backtrader/analyzers/positions.py +119 -0
- backtrader/analyzers/pyfolio.py +470 -0
- backtrader/analyzers/returns.py +192 -0
- backtrader/analyzers/sharpe.py +307 -0
- backtrader/analyzers/sharpe_ratio_stats.py +534 -0
- backtrader/analyzers/sqn.py +112 -0
- backtrader/analyzers/timereturn.py +192 -0
- backtrader/analyzers/total_value.py +75 -0
- backtrader/analyzers/tradeanalyzer.py +278 -0
- backtrader/analyzers/transactions.py +141 -0
- backtrader/analyzers/vwr.py +245 -0
- backtrader/bokeh/__init__.py +155 -0
- backtrader/bokeh/analyzers/__init__.py +13 -0
- backtrader/bokeh/analyzers/plot.py +192 -0
- backtrader/bokeh/analyzers/recorder.py +181 -0
- backtrader/bokeh/app.py +1094 -0
- backtrader/bokeh/live/__init__.py +11 -0
- backtrader/bokeh/live/client.py +352 -0
- backtrader/bokeh/live/datahandler.py +346 -0
- backtrader/bokeh/plot_adapter.py +200 -0
- backtrader/bokeh/schemes/__init__.py +14 -0
- backtrader/bokeh/schemes/blackly.py +76 -0
- backtrader/bokeh/schemes/scheme.py +150 -0
- backtrader/bokeh/schemes/tradimo.py +82 -0
- backtrader/bokeh/tab.py +125 -0
- backtrader/bokeh/tabs/__init__.py +30 -0
- backtrader/bokeh/tabs/analyzer.py +120 -0
- backtrader/bokeh/tabs/config.py +154 -0
- backtrader/bokeh/tabs/live.py +109 -0
- backtrader/bokeh/tabs/log.py +185 -0
- backtrader/bokeh/tabs/metadata.py +182 -0
- backtrader/bokeh/tabs/performance.py +359 -0
- backtrader/bokeh/tabs/source.py +70 -0
- backtrader/bokeh/utils/__init__.py +8 -0
- backtrader/bokeh/utils/helpers.py +167 -0
- backtrader/bokeh/webapp.py +164 -0
- backtrader/broker.py +478 -0
- backtrader/brokers/__init__.py +36 -0
- backtrader/brokers/bbroker.py +2576 -0
- backtrader/brokers/btapibroker.py +8227 -0
- backtrader/brokers/hft/__init__.py +89 -0
- backtrader/brokers/hft/binance_bbo.py +625 -0
- backtrader/brokers/hft/binance_bbo_compare.py +1398 -0
- backtrader/brokers/hft/examples.py +1228 -0
- backtrader/brokers/hft/exchange.py +380 -0
- backtrader/brokers/hft/latency.py +309 -0
- backtrader/brokers/hft/matching_core.py +572 -0
- backtrader/brokers/hft/queue.py +238 -0
- backtrader/brokers/hft/recorder.py +88 -0
- backtrader/brokers/hft/state.py +138 -0
- backtrader/brokers/impact_models.py +118 -0
- backtrader/brokers/mixbroker.py +895 -0
- backtrader/brokers/tickbroker.py +1991 -0
- backtrader/btrun/__init__.py +12 -0
- backtrader/btrun/btrun.py +1218 -0
- backtrader/cerebro.py +828 -0
- backtrader/channel.py +682 -0
- backtrader/channels/__init__.py +23 -0
- backtrader/channels/bridge.py +186 -0
- backtrader/channels/funding.py +248 -0
- backtrader/channels/live_queue.py +216 -0
- backtrader/channels/live_validator.py +294 -0
- backtrader/channels/orderbook.py +257 -0
- backtrader/channels/tick.py +202 -0
- backtrader/comminfo.py +665 -0
- backtrader/commissions/__init__.py +106 -0
- backtrader/commissions/ctpoption.py +993 -0
- backtrader/configs/account_config_example.yaml +8 -0
- backtrader/dataseries.py +379 -0
- backtrader/errors.py +106 -0
- backtrader/events.py +980 -0
- backtrader/feed.py +1523 -0
- backtrader/feeds/__init__.py +75 -0
- backtrader/feeds/barrier.py +2006 -0
- backtrader/feeds/blaze.py +118 -0
- backtrader/feeds/btapifeed.py +1538 -0
- backtrader/feeds/btcsv.py +203 -0
- backtrader/feeds/chainer.py +114 -0
- backtrader/feeds/cryptohftdata.py +164 -0
- backtrader/feeds/csvgeneric.py +1205 -0
- backtrader/feeds/ctpcohort.py +1051 -0
- backtrader/feeds/influxfeed.py +158 -0
- backtrader/feeds/livefeed.py +71 -0
- backtrader/feeds/mixed_channel.py +108 -0
- backtrader/feeds/mt4csv.py +42 -0
- backtrader/feeds/pandafeed.py +381 -0
- backtrader/feeds/quandl.py +256 -0
- backtrader/feeds/rollover.py +229 -0
- backtrader/feeds/sierrachart.py +30 -0
- backtrader/feeds/vchart.py +162 -0
- backtrader/feeds/vchartcsv.py +84 -0
- backtrader/feeds/vchartfile.py +153 -0
- backtrader/feeds/yahoo.py +399 -0
- backtrader/fillers.py +148 -0
- backtrader/filters/__init__.py +34 -0
- backtrader/filters/bsplitter.py +127 -0
- backtrader/filters/calendardays.py +121 -0
- backtrader/filters/datafiller.py +192 -0
- backtrader/filters/datafilter.py +74 -0
- backtrader/filters/daysteps.py +96 -0
- backtrader/filters/heikinashi.py +63 -0
- backtrader/filters/renko.py +164 -0
- backtrader/filters/session.py +289 -0
- backtrader/flt.py +80 -0
- backtrader/functions.py +960 -0
- backtrader/indicator.py +449 -0
- backtrader/indicators/__init__.py +148 -0
- backtrader/indicators/accdecoscillator.py +110 -0
- backtrader/indicators/aroon.py +300 -0
- backtrader/indicators/atr.py +315 -0
- backtrader/indicators/awesomeoscillator.py +122 -0
- backtrader/indicators/basicops.py +834 -0
- backtrader/indicators/bollinger.py +223 -0
- backtrader/indicators/cci.py +89 -0
- backtrader/indicators/channels_ext.py +83 -0
- backtrader/indicators/contrib/__init__.py +228 -0
- backtrader/indicators/contrib/absolutely_no_lag_lwma.py +28 -0
- backtrader/indicators/contrib/absolutely_no_lag_lwma_color.py +44 -0
- backtrader/indicators/contrib/accumulation_distribution_line.py +92 -0
- backtrader/indicators/contrib/adx_cross_hull_style_indicator.py +249 -0
- backtrader/indicators/contrib/adxdmi.py +34 -0
- backtrader/indicators/contrib/ai_acceleration_deceleration_oscillator.py +34 -0
- backtrader/indicators/contrib/altr_trend_signal_v22.py +85 -0
- backtrader/indicators/contrib/anchored_momentum_line.py +115 -0
- backtrader/indicators/contrib/any_range_cld_tail_indicator.py +82 -0
- backtrader/indicators/contrib/aroon_horn_sign_indicator.py +96 -0
- backtrader/indicators/contrib/aroon_oscillator_sign_alert.py +50 -0
- backtrader/indicators/contrib/arrows_curves_indicator.py +112 -0
- backtrader/indicators/contrib/as_ctrend_indicator.py +143 -0
- backtrader/indicators/contrib/asimmetric_stoch_nr_indicator.py +187 -0
- backtrader/indicators/contrib/atr_normalize_histogram.py +118 -0
- backtrader/indicators/contrib/average_change_candle.py +165 -0
- backtrader/indicators/contrib/bb_squeeze_indicator.py +60 -0
- backtrader/indicators/contrib/bezier_st_dev_indicator.py +135 -0
- backtrader/indicators/contrib/binary_wave_indicator.py +233 -0
- backtrader/indicators/contrib/blau_c_momentum_indicator.py +123 -0
- backtrader/indicators/contrib/blau_cmi_indicator.py +141 -0
- backtrader/indicators/contrib/blau_csi.py +76 -0
- backtrader/indicators/contrib/blau_ergodic.py +53 -0
- backtrader/indicators/contrib/blau_t_stoch_i.py +72 -0
- backtrader/indicators/contrib/blau_ts_stochastic.py +85 -0
- backtrader/indicators/contrib/blau_tvi.py +55 -0
- backtrader/indicators/contrib/brain_trend2_indicator.py +128 -0
- backtrader/indicators/contrib/brain_trend_signal_proxy.py +47 -0
- backtrader/indicators/contrib/brake_parb_indicator.py +85 -0
- backtrader/indicators/contrib/breakout_bars_trend_v2.py +121 -0
- backtrader/indicators/contrib/bsi_indicator.py +87 -0
- backtrader/indicators/contrib/bulls_bears_eyes.py +67 -0
- backtrader/indicators/contrib/bulls_power.py +56 -0
- backtrader/indicators/contrib/bw_wise_man1_signal.py +102 -0
- backtrader/indicators/contrib/bykov_trend_indicator.py +85 -0
- backtrader/indicators/contrib/candle_stop_color.py +46 -0
- backtrader/indicators/contrib/candles_x_smoothed_indicator.py +69 -0
- backtrader/indicators/contrib/candlesticks_bw.py +45 -0
- backtrader/indicators/contrib/caudate_x_period_candle_color.py +56 -0
- backtrader/indicators/contrib/cci_histogram_indicator.py +53 -0
- backtrader/indicators/contrib/cci_woodies_indicator.py +80 -0
- backtrader/indicators/contrib/center_of_gravity_candle_indicator.py +83 -0
- backtrader/indicators/contrib/center_of_gravity_indicator.py +70 -0
- backtrader/indicators/contrib/cg_oscillator.py +40 -0
- backtrader/indicators/contrib/close_line_cci.py +38 -0
- backtrader/indicators/contrib/close_price_fractals.py +47 -0
- backtrader/indicators/contrib/color3rd_gen_xma_indicator.py +122 -0
- backtrader/indicators/contrib/color_bb_candles_indicator.py +108 -0
- backtrader/indicators/contrib/color_coppock_indicator.py +157 -0
- backtrader/indicators/contrib/color_hma.py +71 -0
- backtrader/indicators/contrib/color_j_variation_indicator.py +53 -0
- backtrader/indicators/contrib/color_metro_de_marker_indicator.py +78 -0
- backtrader/indicators/contrib/color_metro_stochastic_indicator.py +93 -0
- backtrader/indicators/contrib/color_metro_wpr_indicator.py +85 -0
- backtrader/indicators/contrib/color_schaff_de_marker_trend_cycle.py +92 -0
- backtrader/indicators/contrib/color_schaff_trend_cycle_indicator.py +203 -0
- backtrader/indicators/contrib/color_step_xccx_indicator.py +193 -0
- backtrader/indicators/contrib/color_x2_ma.py +49 -0
- backtrader/indicators/contrib/color_x_derivative.py +63 -0
- backtrader/indicators/contrib/color_zerolag_de_marker.py +84 -0
- backtrader/indicators/contrib/corrected_average_indicator.py +127 -0
- backtrader/indicators/contrib/darvas_boxes_system.py +73 -0
- backtrader/indicators/contrib/dema_range_channel_color.py +42 -0
- backtrader/indicators/contrib/derivative_indicator.py +95 -0
- backtrader/indicators/contrib/digital_ft01_indicator.py +112 -0
- backtrader/indicators/contrib/digital_macd.py +200 -0
- backtrader/indicators/contrib/donchian_channels_system.py +45 -0
- backtrader/indicators/contrib/dots_indicator.py +93 -0
- backtrader/indicators/contrib/ef_distance_indicator.py +82 -0
- backtrader/indicators/contrib/ema_rsi_va.py +80 -0
- backtrader/indicators/contrib/envelopes_jp_alonso.py +32 -0
- backtrader/indicators/contrib/f2a_ao_indicator.py +120 -0
- backtrader/indicators/contrib/fatl_filter.py +179 -0
- backtrader/indicators/contrib/fibo_candles_indicator.py +78 -0
- backtrader/indicators/contrib/fine_tuning_ma.py +100 -0
- backtrader/indicators/contrib/fisher_org_v1.py +102 -0
- backtrader/indicators/contrib/fisher_org_v1_sign.py +118 -0
- backtrader/indicators/contrib/force_index_ema.py +96 -0
- backtrader/indicators/contrib/force_index_ema_2.py +27 -0
- backtrader/indicators/contrib/forecast_oscilator.py +145 -0
- backtrader/indicators/contrib/fractal_amambk.py +81 -0
- backtrader/indicators/contrib/frama_series.py +84 -0
- backtrader/indicators/contrib/frasm_av2_indicator.py +104 -0
- backtrader/indicators/contrib/go_indicator.py +93 -0
- backtrader/indicators/contrib/hlr_indicator.py +95 -0
- backtrader/indicators/contrib/hma.py +50 -0
- backtrader/indicators/contrib/i4_drfv2.py +34 -0
- backtrader/indicators/contrib/i4_drfv3.py +38 -0
- backtrader/indicators/contrib/i_anch_mom_indicator.py +72 -0
- backtrader/indicators/contrib/i_de_marker_sign_indicator.py +64 -0
- backtrader/indicators/contrib/i_gap_indicator.py +45 -0
- backtrader/indicators/contrib/i_stoch_komposter_indicator.py +77 -0
- backtrader/indicators/contrib/i_trend_indicator.py +125 -0
- backtrader/indicators/contrib/iamma_indicator.py +39 -0
- backtrader/indicators/contrib/indexed_moving_average.py +33 -0
- backtrader/indicators/contrib/instantaneous_trend_filter_indicator.py +51 -0
- backtrader/indicators/contrib/inverse_reaction_indicator.py +41 -0
- backtrader/indicators/contrib/irsi_sign_indicator.py +95 -0
- backtrader/indicators/contrib/iwpr_sign_indicator.py +59 -0
- backtrader/indicators/contrib/j_brain_trend1_sig_indicator.py +233 -0
- backtrader/indicators/contrib/j_tpo_proxy.py +32 -0
- backtrader/indicators/contrib/jma_slope_indicator.py +73 -0
- backtrader/indicators/contrib/kalman_filter_indicator.py +119 -0
- backtrader/indicators/contrib/kalman_filter_line.py +127 -0
- backtrader/indicators/contrib/kama_indicator.py +150 -0
- backtrader/indicators/contrib/karacatica_indicator.py +99 -0
- backtrader/indicators/contrib/kdj_indicator.py +59 -0
- backtrader/indicators/contrib/kwan_ccc_indicator.py +195 -0
- backtrader/indicators/contrib/kwan_nrp_indicator.py +113 -0
- backtrader/indicators/contrib/kwan_rdp_indicator.py +192 -0
- backtrader/indicators/contrib/laguerre_adx_indicator.py +85 -0
- backtrader/indicators/contrib/laguerre_filter_indicator.py +66 -0
- backtrader/indicators/contrib/laguerre_plus_di_proxy.py +57 -0
- backtrader/indicators/contrib/laguerre_roc_indicator.py +81 -0
- backtrader/indicators/contrib/le_man_signal_indicator.py +63 -0
- backtrader/indicators/contrib/linear_reg_slope_v2_indicator.py +136 -0
- backtrader/indicators/contrib/loco_indicator.py +88 -0
- backtrader/indicators/contrib/lrma_indicator.py +185 -0
- backtrader/indicators/contrib/lsma_angle_indicator.py +106 -0
- backtrader/indicators/contrib/ma_rounding_channel_indicator.py +149 -0
- backtrader/indicators/contrib/macd2_indicator.py +61 -0
- backtrader/indicators/contrib/macd_candle_indicator.py +80 -0
- backtrader/indicators/contrib/malr_indicator.py +77 -0
- backtrader/indicators/contrib/momentum_candle_sign_indicator.py +51 -0
- backtrader/indicators/contrib/moving_average_fn_indicator.py +139 -0
- backtrader/indicators/contrib/mt5_stochastic_close_close.py +57 -0
- backtrader/indicators/contrib/muv_nor_diff_cloud_indicator.py +107 -0
- backtrader/indicators/contrib/non_lag_dot_indicator.py +124 -0
- backtrader/indicators/contrib/nrtr_extr_indicator.py +95 -0
- backtrader/indicators/contrib/nrtr_indicator.py +95 -0
- backtrader/indicators/contrib/p_channel_system.py +40 -0
- backtrader/indicators/contrib/percent_envelope.py +37 -0
- backtrader/indicators/contrib/percentage_crossover_channel.py +47 -0
- backtrader/indicators/contrib/pivot_zig_zag_proxy.py +47 -0
- backtrader/indicators/contrib/price_channel_stop_indicator.py +104 -0
- backtrader/indicators/contrib/price_extreme_channel.py +35 -0
- backtrader/indicators/contrib/qqe_cloud_indicator.py +129 -0
- backtrader/indicators/contrib/ravi_indicator.py +40 -0
- backtrader/indicators/contrib/raw_close_close_stochastic.py +74 -0
- backtrader/indicators/contrib/rd_trend_trigger_indicator.py +51 -0
- backtrader/indicators/contrib/renko_level.py +85 -0
- backtrader/indicators/contrib/renko_line_break.py +91 -0
- backtrader/indicators/contrib/rftl_indicator.py +41 -0
- backtrader/indicators/contrib/rkd_indicator.py +53 -0
- backtrader/indicators/contrib/roc2_vg_indicator.py +68 -0
- backtrader/indicators/contrib/rsi_histogram_indicator.py +43 -0
- backtrader/indicators/contrib/rsi_slowdown.py +57 -0
- backtrader/indicators/contrib/rsioma_v2.py +41 -0
- backtrader/indicators/contrib/rvi_histogram_indicator.py +107 -0
- backtrader/indicators/contrib/safe_adx.py +89 -0
- backtrader/indicators/contrib/shared_strategy_indicators.py +1651 -0
- backtrader/indicators/contrib/sidus_indicator.py +105 -0
- backtrader/indicators/contrib/silver_trend_indicator.py +79 -0
- backtrader/indicators/contrib/sliding_range_color.py +56 -0
- backtrader/indicators/contrib/slow_stoch.py +42 -0
- backtrader/indicators/contrib/smoothed_adx_indicator.py +86 -0
- backtrader/indicators/contrib/smoothed_rsi.py +31 -0
- backtrader/indicators/contrib/spearman_rank_correlation_histogram.py +60 -0
- backtrader/indicators/contrib/stalin_indicator.py +152 -0
- backtrader/indicators/contrib/starter_laguerre_filter.py +62 -0
- backtrader/indicators/contrib/step_manrtr_indicator.py +137 -0
- backtrader/indicators/contrib/stochastic_histogram_indicator.py +143 -0
- backtrader/indicators/contrib/t3_alarm_indicator.py +125 -0
- backtrader/indicators/contrib/t3_average.py +76 -0
- backtrader/indicators/contrib/t3_indicator.py +40 -0
- backtrader/indicators/contrib/the20s_v020_signal.py +93 -0
- backtrader/indicators/contrib/three_candles_indicator.py +70 -0
- backtrader/indicators/contrib/three_line_break_indicator.py +64 -0
- backtrader/indicators/contrib/time_line.py +57 -0
- backtrader/indicators/contrib/trading_channel_index_proxy.py +48 -0
- backtrader/indicators/contrib/trend_arrows_indicator.py +109 -0
- backtrader/indicators/contrib/trend_continuation_indicator.py +127 -0
- backtrader/indicators/contrib/trend_intensity_index_proxy.py +51 -0
- backtrader/indicators/contrib/trend_manager_indicator.py +39 -0
- backtrader/indicators/contrib/tri_x_candle_indicator.py +51 -0
- backtrader/indicators/contrib/trigger_line.py +66 -0
- backtrader/indicators/contrib/triple_ema_rate.py +34 -0
- backtrader/indicators/contrib/trvi_indicator.py +194 -0
- backtrader/indicators/contrib/two_pb_ideal_xosma_indicator.py +127 -0
- backtrader/indicators/contrib/ultra_absolutely_no_lag_lwma_color.py +92 -0
- backtrader/indicators/contrib/ultra_wpr_indicator.py +173 -0
- backtrader/indicators/contrib/up_down_candle_strength.py +68 -0
- backtrader/indicators/contrib/vinin_i_trend_indicator.py +139 -0
- backtrader/indicators/contrib/volume_weighted_ma_indicator.py +78 -0
- backtrader/indicators/contrib/volume_weighted_ma_st_dev_indicator.py +111 -0
- backtrader/indicators/contrib/vwap_close_indicator.py +65 -0
- backtrader/indicators/contrib/vwma_candle.py +57 -0
- backtrader/indicators/contrib/vwma_digit_system.py +70 -0
- backtrader/indicators/contrib/wami.py +43 -0
- backtrader/indicators/contrib/wprsi_signal_indicator.py +105 -0
- backtrader/indicators/contrib/x_de_marker_histogram_vol_direct_indicator.py +145 -0
- backtrader/indicators/contrib/x_fisher_indicator.py +64 -0
- backtrader/indicators/contrib/xcci_histogram_vol_direct_indicator.py +56 -0
- backtrader/indicators/contrib/xcci_histogram_vol_indicator.py +85 -0
- backtrader/indicators/contrib/xma_ichimoku.py +163 -0
- backtrader/indicators/contrib/xma_ishimoku_channel_indicator.py +65 -0
- backtrader/indicators/contrib/xma_ishimoku_line.py +68 -0
- backtrader/indicators/contrib/xma_range_bands_indicator.py +107 -0
- backtrader/indicators/contrib/xmacd_indicator.py +70 -0
- backtrader/indicators/contrib/xrsi_de_marker_histogram.py +67 -0
- backtrader/indicators/contrib/xrsi_histogram_vol_direct_indicator.py +52 -0
- backtrader/indicators/contrib/xrsi_histogram_vol_indicator.py +81 -0
- backtrader/indicators/contrib/xrvi_indicator.py +130 -0
- backtrader/indicators/contrib/zero_lag_macd.py +36 -0
- backtrader/indicators/contrib/zig_zag_recent_pivot_signal.py +90 -0
- backtrader/indicators/contrib/zpf_indicator.py +115 -0
- backtrader/indicators/crossover.py +337 -0
- backtrader/indicators/dema.py +175 -0
- backtrader/indicators/demarker.py +270 -0
- backtrader/indicators/deviation.py +284 -0
- backtrader/indicators/directionalmove.py +1071 -0
- backtrader/indicators/dma.py +112 -0
- backtrader/indicators/dpo.py +96 -0
- backtrader/indicators/dv2.py +56 -0
- backtrader/indicators/ema.py +145 -0
- backtrader/indicators/envelope.py +475 -0
- backtrader/indicators/hadelta.py +198 -0
- backtrader/indicators/heikinashi.py +153 -0
- backtrader/indicators/hma.py +153 -0
- backtrader/indicators/hurst.py +151 -0
- backtrader/indicators/ichimoku.py +267 -0
- backtrader/indicators/kama.py +181 -0
- backtrader/indicators/kst.py +159 -0
- backtrader/indicators/lrsi.py +125 -0
- backtrader/indicators/mabase.py +147 -0
- backtrader/indicators/macd.py +322 -0
- backtrader/indicators/momentum.py +267 -0
- backtrader/indicators/moneyflow.py +237 -0
- backtrader/indicators/mt5atr.py +124 -0
- backtrader/indicators/myind.py +179 -0
- backtrader/indicators/obv.py +94 -0
- backtrader/indicators/ols.py +265 -0
- backtrader/indicators/oscillator.py +161 -0
- backtrader/indicators/percentchange.py +83 -0
- backtrader/indicators/percentrank.py +46 -0
- backtrader/indicators/pivotpoint.py +469 -0
- backtrader/indicators/prettygoodoscillator.py +113 -0
- backtrader/indicators/priceops_ext.py +123 -0
- backtrader/indicators/priceoscillator.py +262 -0
- backtrader/indicators/psar.py +212 -0
- backtrader/indicators/rmi.py +69 -0
- backtrader/indicators/rsi.py +440 -0
- backtrader/indicators/sma.py +141 -0
- backtrader/indicators/smma.py +116 -0
- backtrader/indicators/spread.py +54 -0
- backtrader/indicators/stochastic.py +263 -0
- backtrader/indicators/supertrend.py +436 -0
- backtrader/indicators/trend_ext.py +105 -0
- backtrader/indicators/trix.py +202 -0
- backtrader/indicators/tsi.py +155 -0
- backtrader/indicators/ultimateoscillator.py +158 -0
- backtrader/indicators/vortex.py +62 -0
- backtrader/indicators/williams.py +194 -0
- backtrader/indicators/wma.py +103 -0
- backtrader/indicators/zlema.py +135 -0
- backtrader/indicators/zlind.py +104 -0
- backtrader/linebuffer.py +3155 -0
- backtrader/lineiterator.py +2911 -0
- backtrader/lineroot.py +1106 -0
- backtrader/lineseries.py +2559 -0
- backtrader/live_trading/__init__.py +31 -0
- backtrader/live_trading/interface.py +404 -0
- backtrader/mathsupport.py +94 -0
- backtrader/metabase.py +1804 -0
- backtrader/mixins/__init__.py +21 -0
- backtrader/mixins/singleton.py +118 -0
- backtrader/observer.py +106 -0
- backtrader/observers/__init__.py +45 -0
- backtrader/observers/benchmark.py +126 -0
- backtrader/observers/broker.py +184 -0
- backtrader/observers/buysell.py +144 -0
- backtrader/observers/drawdown.py +161 -0
- backtrader/observers/logreturns.py +113 -0
- backtrader/observers/timereturn.py +86 -0
- backtrader/observers/trade_logger.py +2972 -0
- backtrader/observers/tradelogger.py +6 -0
- backtrader/observers/trades.py +258 -0
- backtrader/order.py +1114 -0
- backtrader/parameters.py +2345 -0
- backtrader/plot/__init__.py +54 -0
- backtrader/plot/finance.py +1022 -0
- backtrader/plot/formatters.py +200 -0
- backtrader/plot/locator.py +353 -0
- backtrader/plot/multicursor.py +495 -0
- backtrader/plot/plot.py +2500 -0
- backtrader/plot/plot_plotly.py +1351 -0
- backtrader/plot/scheme.py +253 -0
- backtrader/plot/utils.py +104 -0
- backtrader/position.py +290 -0
- backtrader/position_modes.py +132 -0
- backtrader/profiles.py +254 -0
- backtrader/reports/__init__.py +39 -0
- backtrader/reports/charts.py +371 -0
- backtrader/reports/performance.py +620 -0
- backtrader/reports/reporter.py +660 -0
- backtrader/resamplerfilter.py +1001 -0
- backtrader/signal.py +118 -0
- backtrader/signals/__init__.py +17 -0
- backtrader/sizer.py +114 -0
- backtrader/sizers/__init__.py +26 -0
- backtrader/sizers/fixedsize.py +161 -0
- backtrader/sizers/percents_sizer.py +119 -0
- backtrader/store.py +221 -0
- backtrader/stores/__init__.py +33 -0
- backtrader/stores/btapistore.py +15506 -0
- backtrader/stores/livestore.py +137 -0
- backtrader/stores/vchartfile.py +96 -0
- backtrader/strategy.py +3655 -0
- backtrader/talib.py +280 -0
- backtrader/test_helpers.py +96 -0
- backtrader/timer.py +358 -0
- backtrader/trade.py +442 -0
- backtrader/tradingcal.py +361 -0
- backtrader/utils/__init__.py +68 -0
- backtrader/utils/autodict.py +251 -0
- backtrader/utils/date.py +71 -0
- backtrader/utils/dateintern.py +509 -0
- backtrader/utils/flushfile.py +94 -0
- backtrader/utils/fractal.py +101 -0
- backtrader/utils/get_metrics.py +101 -0
- backtrader/utils/load_data.py +209 -0
- backtrader/utils/log_message.py +998 -0
- backtrader/utils/ordereddefaultdict.py +75 -0
- backtrader/utils/py3.py +296 -0
- backtrader/version.py +21 -0
- 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
|