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,1001 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Resampler and Filter Module - Data resampling and replay functionality.
|
|
3
|
+
|
|
4
|
+
This module provides classes for resampling data to different timeframes
|
|
5
|
+
and replaying data at compressed timeframes. It includes the base
|
|
6
|
+
resampler and replayer classes along with specific implementations for
|
|
7
|
+
different time periods.
|
|
8
|
+
|
|
9
|
+
Key Classes:
|
|
10
|
+
Resampler: Base class for resampling data to different timeframes.
|
|
11
|
+
Replayer: Base class for replaying data with session information.
|
|
12
|
+
DTFaker: Provides fake datetime for live/real-time data feeds.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
Resampling daily data to weekly:
|
|
16
|
+
>>> data = bt.feeds.GenericCSVData(dataname='daily.csv')
|
|
17
|
+
>>> cerebro.adddata(data)
|
|
18
|
+
>>> cerebro.resampledata(data, timeframe=bt.TimeFrame.Weeks)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from datetime import datetime, timedelta, timezone
|
|
22
|
+
|
|
23
|
+
from .dataseries import TimeFrame, _Bar
|
|
24
|
+
from .parameters import ParameterizedBase
|
|
25
|
+
from .utils.date import date2num, num2date
|
|
26
|
+
|
|
27
|
+
# Python 3.11+ has datetime.UTC, earlier versions use timezone.utc
|
|
28
|
+
UTC = timezone.utc
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# This class is only used in the _checkbarover function
|
|
32
|
+
# chkdata = DTFaker(data, forcedata) if fromcheck else data
|
|
33
|
+
class DTFaker:
|
|
34
|
+
"""Provides fake datetime for data sources that need periodic checks.
|
|
35
|
+
|
|
36
|
+
This class is used for real-time data feeds that return None from _load
|
|
37
|
+
to indicate that a check of the resampler and/or notification queue
|
|
38
|
+
is needed. It provides the current time in both UTC and localized formats.
|
|
39
|
+
|
|
40
|
+
Attributes:
|
|
41
|
+
data: The underlying data source.
|
|
42
|
+
_dt: UTC-like time as numeric value.
|
|
43
|
+
_dtime: Localized datetime.
|
|
44
|
+
sessionend: Trading day end time.
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
>>> faker = DTFaker(data)
|
|
48
|
+
>>> print(faker.datetime()) # Current localized time
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# This will only be used for data sources which at some point in time
|
|
52
|
+
# return None from _load to indicate that a check of the resampler and/or
|
|
53
|
+
# notification queue is needed
|
|
54
|
+
# This is meant (at least initially) for real-time feeds, because those are
|
|
55
|
+
# the ones in need of events like the ones described above.
|
|
56
|
+
# These data sources should also be producing ``utc`` time directly because
|
|
57
|
+
# the real-time feed is (more often than not) timestamped and utc provides
|
|
58
|
+
# a universal reference,
|
|
59
|
+
# That's why below the timestamp is chosen in UTC and passed directly to
|
|
60
|
+
# date2num to avoid localization.But it is extracted from data.num2date
|
|
61
|
+
# to ensure the returned datetime object is localized according to the
|
|
62
|
+
# expected output by the user (local timezone or any specified)
|
|
63
|
+
|
|
64
|
+
# Initialize
|
|
65
|
+
def __init__(self, data, forcedata=None):
|
|
66
|
+
"""Initialize the DTFaker with current time.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
data: The underlying data source.
|
|
70
|
+
forcedata: Optional data source to force time from.
|
|
71
|
+
"""
|
|
72
|
+
# Data
|
|
73
|
+
self.data = data
|
|
74
|
+
|
|
75
|
+
# Aliases
|
|
76
|
+
self.datetime = self
|
|
77
|
+
self.p = self
|
|
78
|
+
|
|
79
|
+
# If forcedata is None
|
|
80
|
+
if forcedata is None:
|
|
81
|
+
# Get current utc time and add data time offset
|
|
82
|
+
_dtime = datetime.now(UTC) + data._timeoffset()
|
|
83
|
+
# Convert calculated utc time to number
|
|
84
|
+
self._dt = dt = date2num(_dtime) # utc-like time
|
|
85
|
+
# Convert numeric time to localized time format
|
|
86
|
+
self._dtime = data.num2date(dt) # localized time
|
|
87
|
+
# If forcedata is not None
|
|
88
|
+
else:
|
|
89
|
+
# Get corresponding time from forcedata's datetime column as utc time
|
|
90
|
+
self._dt = forcedata.datetime[0] # utc-like time
|
|
91
|
+
# Get local time directly from forcedata
|
|
92
|
+
self._dtime = forcedata.datetime.datetime() # localized time
|
|
93
|
+
# Trading day end time
|
|
94
|
+
self.sessionend = data.p.sessionend
|
|
95
|
+
|
|
96
|
+
# Length
|
|
97
|
+
def __len__(self):
|
|
98
|
+
return len(self.data)
|
|
99
|
+
|
|
100
|
+
# Return localized date and time when called
|
|
101
|
+
def __call__(self, idx=0):
|
|
102
|
+
"""Return the localized datetime.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
idx: Index (ignored, for compatibility).
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Localized datetime object.
|
|
109
|
+
"""
|
|
110
|
+
return self._dtime # simulates data.datetime.datetime()
|
|
111
|
+
|
|
112
|
+
# datetime returns localized date and time
|
|
113
|
+
def datetime(self, idx=0):
|
|
114
|
+
"""Return the localized datetime.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
idx: Index (ignored, for compatibility).
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Localized datetime object.
|
|
121
|
+
"""
|
|
122
|
+
return self._dtime
|
|
123
|
+
|
|
124
|
+
# Return localized date
|
|
125
|
+
def date(self, idx=0):
|
|
126
|
+
"""Return the localized date.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
idx: Index (ignored, for compatibility).
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Date object.
|
|
133
|
+
"""
|
|
134
|
+
return self._dtime.date()
|
|
135
|
+
|
|
136
|
+
# Return localized time
|
|
137
|
+
def time(self, idx=0):
|
|
138
|
+
"""Return the localized time.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
idx: Index (ignored, for compatibility).
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Time object.
|
|
145
|
+
"""
|
|
146
|
+
return self._dtime.time()
|
|
147
|
+
|
|
148
|
+
# Return data calendar
|
|
149
|
+
@property
|
|
150
|
+
def _calendar(self):
|
|
151
|
+
return self.data._calendar
|
|
152
|
+
|
|
153
|
+
# If idx=0, return utc numeric time, otherwise return -inf
|
|
154
|
+
def __getitem__(self, idx):
|
|
155
|
+
return self._dt if idx == 0 else float("-inf")
|
|
156
|
+
|
|
157
|
+
# Convert number to date and time
|
|
158
|
+
def num2date(self, *args, **kwargs):
|
|
159
|
+
"""Convert numeric time to datetime.
|
|
160
|
+
|
|
161
|
+
Delegates to the underlying data source's num2date method.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Datetime object.
|
|
165
|
+
"""
|
|
166
|
+
return self.data.num2date(*args, **kwargs)
|
|
167
|
+
|
|
168
|
+
# Convert date and time to number
|
|
169
|
+
def date2num(self, *args, **kwargs):
|
|
170
|
+
"""Convert datetime to numeric time.
|
|
171
|
+
|
|
172
|
+
Delegates to the underlying data source's date2num method.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Float representing the datetime.
|
|
176
|
+
"""
|
|
177
|
+
return self.data.date2num(*args, **kwargs)
|
|
178
|
+
|
|
179
|
+
# Get trading day end time
|
|
180
|
+
def _getnexteos(self):
|
|
181
|
+
return self.data._getnexteos()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Base class for resampler
|
|
185
|
+
class _BaseResampler(ParameterizedBase):
|
|
186
|
+
# Parameters
|
|
187
|
+
params = (
|
|
188
|
+
("bar2edge", True),
|
|
189
|
+
("adjbartime", True),
|
|
190
|
+
("rightedge", True),
|
|
191
|
+
("boundoff", 0),
|
|
192
|
+
("timeframe", TimeFrame.Days),
|
|
193
|
+
("compression", 1),
|
|
194
|
+
("takelate", True),
|
|
195
|
+
("sessionend", True),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Initialize
|
|
199
|
+
def __init__(self, data, **kwargs):
|
|
200
|
+
"""Initialize the base resampler.
|
|
201
|
+
|
|
202
|
+
Sets up the resampling configuration based on timeframe and
|
|
203
|
+
compression parameters, and modifies the data source accordingly.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
data: The data source to resample.
|
|
207
|
+
**kwargs: Additional parameters for the resampler.
|
|
208
|
+
"""
|
|
209
|
+
super().__init__(**kwargs)
|
|
210
|
+
# If timeframe is less than day but greater than tick, subdays is True, subdays represents intraday timeframe
|
|
211
|
+
self.subdays = TimeFrame.Ticks < self.p.timeframe < TimeFrame.Days
|
|
212
|
+
# If timeframe is less than week, subweeks is True
|
|
213
|
+
self.subweeks = self.p.timeframe < TimeFrame.Weeks
|
|
214
|
+
# If not subdays, and data timeframe equals parameter timeframe, and parameter compression divided by data compression remainder is 0, componly is True
|
|
215
|
+
self._source_timeframe = data._timeframe
|
|
216
|
+
self._source_compression = data._compression
|
|
217
|
+
self.componly = (
|
|
218
|
+
not self.subdays
|
|
219
|
+
and data._timeframe == self.p.timeframe
|
|
220
|
+
and not (self.p.compression % data._compression)
|
|
221
|
+
)
|
|
222
|
+
# Create an object to save bar data
|
|
223
|
+
self.bar = _Bar(maxdate=True) # bar holder
|
|
224
|
+
# Number of bars produced, used to control compression count
|
|
225
|
+
self.compcount = 0 # count of produced bars to control compression
|
|
226
|
+
# Whether it is the first bar
|
|
227
|
+
self._firstbar = True
|
|
228
|
+
# If bar2edge, adjbartime, subweeks are all True, doadjusttime is True
|
|
229
|
+
self.doadjusttime = self.p.bar2edge and self.p.adjbartime and self.subweeks
|
|
230
|
+
# The end time of this trading day
|
|
231
|
+
self._nexteos = None
|
|
232
|
+
|
|
233
|
+
# Modify data information according to own parameters
|
|
234
|
+
# During initialization, modify data attributes based on parameters
|
|
235
|
+
# Data resampling is 1
|
|
236
|
+
data.resampling = 1
|
|
237
|
+
# replaying equals replaying
|
|
238
|
+
data.replaying = self.replaying
|
|
239
|
+
# Data timeframe equals parameter timeframe
|
|
240
|
+
data._timeframe = self.p.timeframe
|
|
241
|
+
# Data compression equals parameter compression
|
|
242
|
+
data._compression = self.p.compression
|
|
243
|
+
|
|
244
|
+
self.data = data
|
|
245
|
+
|
|
246
|
+
# How to handle late-arriving data, if not subdays return False, if data length > 1 and current time <= previous time return True
|
|
247
|
+
def _latedata(self, data):
|
|
248
|
+
# new data at position 0, still untouched from stream
|
|
249
|
+
if not self.subdays:
|
|
250
|
+
return False
|
|
251
|
+
|
|
252
|
+
# Time already delivered
|
|
253
|
+
return len(data) > 1 and data.datetime[0] <= data.datetime[-1]
|
|
254
|
+
|
|
255
|
+
# Whether to check if bar is over
|
|
256
|
+
def _checkbarover(self, data, fromcheck=False, forcedata=None):
|
|
257
|
+
# Data to check, if fromcheck is True, use DTFaker to generate instance, otherwise use data
|
|
258
|
+
chkdata = DTFaker(data, forcedata) if fromcheck else data
|
|
259
|
+
# Whether finished
|
|
260
|
+
isover = False
|
|
261
|
+
# If not componly and _barover(chkdata) is False, return False
|
|
262
|
+
if not self.componly and not self._barover(chkdata):
|
|
263
|
+
return isover
|
|
264
|
+
# If intraday and bar2edge is True, return True
|
|
265
|
+
if self.subdays and self.p.bar2edge:
|
|
266
|
+
isover = True
|
|
267
|
+
# If fromcheck is False
|
|
268
|
+
elif not fromcheck: # fromcheck doesn't increase compcount
|
|
269
|
+
# compcount+1
|
|
270
|
+
self.compcount += 1
|
|
271
|
+
# If compcount divided by compression equals 0, return True
|
|
272
|
+
if not (self.compcount % self.p.compression):
|
|
273
|
+
# boundary crossed and enough bars for compression ... proceed
|
|
274
|
+
isover = True
|
|
275
|
+
|
|
276
|
+
return isover
|
|
277
|
+
|
|
278
|
+
# Determine if data has finished
|
|
279
|
+
def _barover(self, data):
|
|
280
|
+
# Timeframe
|
|
281
|
+
tframe = self.p.timeframe
|
|
282
|
+
# If timeframe equals tick, return bar.isopen()
|
|
283
|
+
if tframe == TimeFrame.Ticks:
|
|
284
|
+
# Ticks is already the lowest level
|
|
285
|
+
return self.bar.isopen()
|
|
286
|
+
# If timeframe is less than day, call _barover_subdays(data)
|
|
287
|
+
if tframe < TimeFrame.Days:
|
|
288
|
+
return self._barover_subdays(data)
|
|
289
|
+
# If timeframe equals day, call _barover_days(data)
|
|
290
|
+
if tframe == TimeFrame.Days:
|
|
291
|
+
return self._barover_days(data)
|
|
292
|
+
# If timeframe equals week, call _barover_weeks(data)
|
|
293
|
+
if tframe == TimeFrame.Weeks:
|
|
294
|
+
return self._barover_weeks(data)
|
|
295
|
+
# If timeframe equals month, call _barover_months(data)
|
|
296
|
+
if tframe == TimeFrame.Months:
|
|
297
|
+
return self._barover_months(data)
|
|
298
|
+
# If timeframe equals year, call _barover_years(data)
|
|
299
|
+
if tframe == TimeFrame.Years:
|
|
300
|
+
return self._barover_years(data)
|
|
301
|
+
|
|
302
|
+
# Set session end time
|
|
303
|
+
def _eosset(self):
|
|
304
|
+
if self._nexteos is None:
|
|
305
|
+
self._nexteos, self._nextdteos = self.data._getnexteos()
|
|
306
|
+
return
|
|
307
|
+
|
|
308
|
+
# Check session end time
|
|
309
|
+
def _eoscheck(self, data, seteos=True, exact=False):
|
|
310
|
+
# If seteos is True, directly call _eosset to calculate session end time
|
|
311
|
+
if seteos:
|
|
312
|
+
self._eosset()
|
|
313
|
+
# Compare current data time with session end time
|
|
314
|
+
equal = data.datetime[0] == self._nextdteos
|
|
315
|
+
grter = data.datetime[0] > self._nextdteos
|
|
316
|
+
# If exact is True, ret equals equal,
|
|
317
|
+
# Otherwise, if grter is True, if bar.isopen() is True and bar.datetime < next end time, ret equals True
|
|
318
|
+
# Otherwise, ret equals equal
|
|
319
|
+
if exact:
|
|
320
|
+
ret = equal
|
|
321
|
+
else:
|
|
322
|
+
# if the compared data goes over the endofsession
|
|
323
|
+
# make sure the resampled bar is open and has something before that
|
|
324
|
+
# end of the session, It could be a weekend and nothing was delivered
|
|
325
|
+
# until Monday
|
|
326
|
+
if grter:
|
|
327
|
+
ret = self.bar.isopen() and self.bar.datetime <= self._nextdteos
|
|
328
|
+
else:
|
|
329
|
+
ret = equal
|
|
330
|
+
# If ret is True, _lasteos equals _nexteos, _lastdteos equals _nextdteos
|
|
331
|
+
# And set _nexteos and _nextdteos to None and -inf respectively
|
|
332
|
+
if ret:
|
|
333
|
+
self._lasteos = self._nexteos
|
|
334
|
+
self._lastdteos = self._nextdteos
|
|
335
|
+
self._nexteos = None
|
|
336
|
+
self._nextdteos = float("-inf")
|
|
337
|
+
|
|
338
|
+
return ret
|
|
339
|
+
|
|
340
|
+
# Check days
|
|
341
|
+
def _barover_days(self, data):
|
|
342
|
+
return self._eoscheck(data)
|
|
343
|
+
|
|
344
|
+
# Check weeks
|
|
345
|
+
def _barover_weeks(self, data):
|
|
346
|
+
# If data's _calendar is None
|
|
347
|
+
if self.data._calendar is None:
|
|
348
|
+
# Get specific year, week number and day from date
|
|
349
|
+
year, week, _ = data.num2date(self.bar.datetime).date().isocalendar()
|
|
350
|
+
# Get bar's week number
|
|
351
|
+
yearweek = year * 100 + week
|
|
352
|
+
# Get data's year, week number and day, and get data's week number
|
|
353
|
+
baryear, barweek, _ = data.datetime.date().isocalendar()
|
|
354
|
+
bar_yearweek = baryear * 100 + barweek
|
|
355
|
+
# If data's week number is greater than bar's week number, return True, otherwise return False
|
|
356
|
+
return bar_yearweek > yearweek
|
|
357
|
+
# If data's _calendar is not None, call last_weekday
|
|
358
|
+
return data._calendar.last_weekday(data.datetime.date())
|
|
359
|
+
|
|
360
|
+
# Check months
|
|
361
|
+
def _barover_months(self, data):
|
|
362
|
+
dt = data.num2date(self.bar.datetime).date()
|
|
363
|
+
yearmonth = dt.year * 100 + dt.month
|
|
364
|
+
|
|
365
|
+
bardt = data.datetime.datetime()
|
|
366
|
+
bar_yearmonth = bardt.year * 100 + bardt.month
|
|
367
|
+
|
|
368
|
+
return bar_yearmonth > yearmonth
|
|
369
|
+
|
|
370
|
+
# Check years
|
|
371
|
+
def _barover_years(self, data):
|
|
372
|
+
return data.datetime.datetime().year > data.num2date(self.bar.datetime).year
|
|
373
|
+
|
|
374
|
+
# Get time point
|
|
375
|
+
def _gettmpoint(self, tm):
|
|
376
|
+
"""
|
|
377
|
+
Returns the point of time intraday for a given time according to the
|
|
378
|
+
timeframe
|
|
379
|
+
|
|
380
|
+
- Ex 1: 00:05:00 in minutes -> point = 5
|
|
381
|
+
- Ex 2: 00:05:20 in seconds -> point = 5 * 60 + 20 = 320
|
|
382
|
+
"""
|
|
383
|
+
# Minute point
|
|
384
|
+
point = tm.hour * 60 + tm.minute
|
|
385
|
+
# Remaining point
|
|
386
|
+
restpoint = 0
|
|
387
|
+
# If timeframe is less than minutes
|
|
388
|
+
if self.p.timeframe < TimeFrame.Minutes:
|
|
389
|
+
# Second point
|
|
390
|
+
point = point * 60 + tm.second
|
|
391
|
+
# If timeframe is less than seconds
|
|
392
|
+
if self.p.timeframe < TimeFrame.Seconds:
|
|
393
|
+
# Convert point to microseconds
|
|
394
|
+
point = point * 1e6 + tm.microsecond
|
|
395
|
+
# If timeframe is not less than seconds, remaining point is microseconds
|
|
396
|
+
else:
|
|
397
|
+
restpoint = tm.microsecond
|
|
398
|
+
# If timeframe is not less than minutes, remaining point is seconds and microseconds
|
|
399
|
+
else:
|
|
400
|
+
restpoint = tm.second + tm.microsecond
|
|
401
|
+
# Add boundoff to point
|
|
402
|
+
point += self.p.boundoff
|
|
403
|
+
|
|
404
|
+
return point, restpoint
|
|
405
|
+
|
|
406
|
+
# Intraday bar over
|
|
407
|
+
def _barover_subdays(self, data):
|
|
408
|
+
# If _eoscheck(data) returns True, then function returns True
|
|
409
|
+
if self._eoscheck(data):
|
|
410
|
+
return True
|
|
411
|
+
# If data time is less than bar time, return False
|
|
412
|
+
if data.datetime[0] < self.bar.datetime:
|
|
413
|
+
return False
|
|
414
|
+
|
|
415
|
+
# Get time objects for the comparisons - in utc-like format
|
|
416
|
+
# Get bar and data time
|
|
417
|
+
tm = num2date(self.bar.datetime).time()
|
|
418
|
+
bartm = num2date(data.datetime[0]).time()
|
|
419
|
+
# Get self.bar's time point and data's time point respectively
|
|
420
|
+
point, _ = self._gettmpoint(tm)
|
|
421
|
+
barpoint, _ = self._gettmpoint(bartm)
|
|
422
|
+
# Set ret to False
|
|
423
|
+
ret = False
|
|
424
|
+
# If data's time point is less than bar's time point, return False
|
|
425
|
+
# If data's time point is greater than bar's time point, further analyze
|
|
426
|
+
if barpoint > point:
|
|
427
|
+
# The data bar has surpassed the internal bar
|
|
428
|
+
# If bar2edge is False, return True
|
|
429
|
+
if not self.p.bar2edge:
|
|
430
|
+
# Compression done on a simple bar basis (like days)
|
|
431
|
+
ret = True
|
|
432
|
+
# If compression is 1, return True
|
|
433
|
+
elif self.p.compression == 1:
|
|
434
|
+
# no bar compression requested -> internal bar done
|
|
435
|
+
ret = True
|
|
436
|
+
# If bar2edge is True and compression is not 1, calculate remainder of dividing points by compression
|
|
437
|
+
# If data's point remainder is greater than bar's point remainder, return True
|
|
438
|
+
else:
|
|
439
|
+
point_comp = point // self.p.compression
|
|
440
|
+
barpoint_comp = barpoint // self.p.compression
|
|
441
|
+
|
|
442
|
+
# Went over boundary including compression
|
|
443
|
+
if barpoint_comp > point_comp:
|
|
444
|
+
ret = True
|
|
445
|
+
|
|
446
|
+
return ret
|
|
447
|
+
|
|
448
|
+
# Check whether to submit currently stored bar when data hasn't moved forward
|
|
449
|
+
def check(self, data, _forcedata=None):
|
|
450
|
+
"""Called to check if the current stored bar has to be delivered in
|
|
451
|
+
spite of the data not having moved forward. If no ticks from a live
|
|
452
|
+
feed come in, a 5-second resampled bar could be delivered 20 seconds
|
|
453
|
+
later. When this method is called the wall clock (incl data time
|
|
454
|
+
offset) is called to check if the time has gone so far as to have to
|
|
455
|
+
deliver the already stored data
|
|
456
|
+
"""
|
|
457
|
+
if not self.bar.isopen():
|
|
458
|
+
return None
|
|
459
|
+
|
|
460
|
+
return self(data, fromcheck=True, forcedata=_forcedata)
|
|
461
|
+
|
|
462
|
+
# Determine if data is about to form a bar
|
|
463
|
+
def _dataonedge(self, data):
|
|
464
|
+
# If subweek is False, if data._calendar is None, return False and True
|
|
465
|
+
if not self.subweeks:
|
|
466
|
+
if data._calendar is None:
|
|
467
|
+
return False, True # nothing can be done
|
|
468
|
+
# Timeframe
|
|
469
|
+
tframe = self.p.timeframe
|
|
470
|
+
# Set ret to False
|
|
471
|
+
ret = False
|
|
472
|
+
# If timeframe equals week, call last_weekday to check
|
|
473
|
+
# If timeframe equals month, call last_monthday to check
|
|
474
|
+
# If timeframe equals year, call last_yearday to check
|
|
475
|
+
if tframe == TimeFrame.Weeks: # Ticks is already the lowest
|
|
476
|
+
ret = data._calendar.last_weekday(data.datetime.date())
|
|
477
|
+
elif tframe == TimeFrame.Months:
|
|
478
|
+
ret = data._calendar.last_monthday(data.datetime.date())
|
|
479
|
+
elif tframe == TimeFrame.Years:
|
|
480
|
+
ret = data._calendar.last_yearday(data.datetime.date())
|
|
481
|
+
# If ret is True
|
|
482
|
+
if ret:
|
|
483
|
+
# Data must be consumed but compression may not be met yet
|
|
484
|
+
# Prevent barcheckover from being called because it could again
|
|
485
|
+
# increase compcount
|
|
486
|
+
# Set docheckover to False
|
|
487
|
+
docheckover = False
|
|
488
|
+
# compcount+1
|
|
489
|
+
self.compcount += 1
|
|
490
|
+
# If compcount divided by compression remainder equals 0, return True, otherwise return False
|
|
491
|
+
ret = not (self.compcount % self.p.compression)
|
|
492
|
+
# If ret equals False, docheckover equals True
|
|
493
|
+
else:
|
|
494
|
+
docheckover = True
|
|
495
|
+
# Return ret, docheckover
|
|
496
|
+
return ret, docheckover
|
|
497
|
+
# _eoscheck check, return two True
|
|
498
|
+
if self._eoscheck(data, exact=True):
|
|
499
|
+
return True, True
|
|
500
|
+
# If intraday
|
|
501
|
+
if self.subdays:
|
|
502
|
+
# Get data's point and remaining point
|
|
503
|
+
point, prest = self._gettmpoint(data.datetime.time())
|
|
504
|
+
# If remaining point is not 0, return False and True
|
|
505
|
+
if prest:
|
|
506
|
+
return False, True # cannot be on boundary, subunits present
|
|
507
|
+
|
|
508
|
+
# Pass through compression to get boundary and rest over boundary
|
|
509
|
+
# Calculate boundary and remaining boundary
|
|
510
|
+
bound, brest = divmod(point, self.p.compression)
|
|
511
|
+
|
|
512
|
+
# if no extra and decomp bound is point
|
|
513
|
+
# If divmod result remainder is 0, return two True
|
|
514
|
+
return brest == 0 and point == (bound * self.p.compression), True
|
|
515
|
+
|
|
516
|
+
# Code overriden by eoscheck
|
|
517
|
+
# This code will not run
|
|
518
|
+
if False and self.p.sessionend: # noqa: SIM223 # intentionally dead code
|
|
519
|
+
# Days scenario - get datetime to compare in output timezone
|
|
520
|
+
# because p.sessionend is expected in output timezone
|
|
521
|
+
bdtime = data.datetime.datetime()
|
|
522
|
+
bsend = datetime.combine(bdtime.date(), data.p.sessionend)
|
|
523
|
+
return bdtime == bsend
|
|
524
|
+
# If none of above reached return, return False, True
|
|
525
|
+
return False, True # subweeks, not subdays and not sessionend
|
|
526
|
+
|
|
527
|
+
# Calculate adjusted time
|
|
528
|
+
def _calcadjtime(self, greater=False):
|
|
529
|
+
if self._nexteos is None:
|
|
530
|
+
# Session has been exceeded - end of session is the mark
|
|
531
|
+
return self._lastdteos # utc-like
|
|
532
|
+
|
|
533
|
+
dt = self.data.num2date(self.bar.datetime)
|
|
534
|
+
|
|
535
|
+
# Get current time
|
|
536
|
+
tm = dt.time()
|
|
537
|
+
# Get the point of the day in the time frame unit (ex: minute 200)
|
|
538
|
+
point, _ = self._gettmpoint(tm)
|
|
539
|
+
|
|
540
|
+
# Apply compression to update the point position (comp 5 -> 200 // 5)
|
|
541
|
+
# point = (point // self.p.compression)
|
|
542
|
+
point = point // self.p.compression
|
|
543
|
+
|
|
544
|
+
# If rightedge (end of boundary is activated) add it unless recursing
|
|
545
|
+
point += self.p.rightedge
|
|
546
|
+
|
|
547
|
+
# Restore point to the timeframe units by de-applying compression
|
|
548
|
+
point *= self.p.compression
|
|
549
|
+
|
|
550
|
+
# Get hours, minutes, seconds and microseconds
|
|
551
|
+
extradays = 0
|
|
552
|
+
if self.p.timeframe == TimeFrame.Minutes:
|
|
553
|
+
ph, pm = divmod(point, 60)
|
|
554
|
+
ps = 0
|
|
555
|
+
pus = 0
|
|
556
|
+
elif self.p.timeframe == TimeFrame.Seconds:
|
|
557
|
+
ph, pm = divmod(point, 60 * 60)
|
|
558
|
+
pm, ps = divmod(pm, 60)
|
|
559
|
+
pus = 0
|
|
560
|
+
elif self.p.timeframe <= TimeFrame.MicroSeconds:
|
|
561
|
+
ph, pm = divmod(point, 60 * 60 * 1e6)
|
|
562
|
+
pm, psec = divmod(pm, 60 * 1e6)
|
|
563
|
+
ps, pus = divmod(psec, 1e6)
|
|
564
|
+
elif self.p.timeframe == TimeFrame.Days:
|
|
565
|
+
# last resort
|
|
566
|
+
eost = self._nexteos.time()
|
|
567
|
+
ph = eost.hour
|
|
568
|
+
pm = eost.minute
|
|
569
|
+
ps = eost.second
|
|
570
|
+
pus = eost.microsecond
|
|
571
|
+
|
|
572
|
+
if ph > 23: # went over midnight:
|
|
573
|
+
extradays = ph // 24
|
|
574
|
+
ph %= 24
|
|
575
|
+
|
|
576
|
+
# Replace intraday parts with the calculated ones and update it
|
|
577
|
+
dt = dt.replace(hour=int(ph), minute=int(pm), second=int(ps), microsecond=int(pus))
|
|
578
|
+
if extradays:
|
|
579
|
+
dt += timedelta(days=extradays)
|
|
580
|
+
dtnum = self.data.date2num(dt)
|
|
581
|
+
return dtnum
|
|
582
|
+
|
|
583
|
+
# Adjust bar time
|
|
584
|
+
def _adjusttime(self, greater=False, forcedata=None):
|
|
585
|
+
"""
|
|
586
|
+
Adjusts the time of calculated bar (from the underlying data source) by
|
|
587
|
+
using the timeframe to the appropriate boundary, with compression taken
|
|
588
|
+
into account
|
|
589
|
+
|
|
590
|
+
Depending on param ``rightedge`` uses the starting boundary or the
|
|
591
|
+
ending one
|
|
592
|
+
"""
|
|
593
|
+
|
|
594
|
+
dtnum = self._calcadjtime(greater=greater)
|
|
595
|
+
if greater and dtnum <= self.bar.datetime:
|
|
596
|
+
return False
|
|
597
|
+
|
|
598
|
+
self.bar.datetime = dtnum
|
|
599
|
+
return True
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
# Resample small period data to form large period data
|
|
603
|
+
class Resampler(_BaseResampler):
|
|
604
|
+
"""This class resamples data of a given timeframe to a larger timeframe.
|
|
605
|
+
|
|
606
|
+
Params
|
|
607
|
+
|
|
608
|
+
- Bar2edge (default: True)
|
|
609
|
+
|
|
610
|
+
Resamples using time boundaries as the target.For example, with a
|
|
611
|
+
"ticks -> 5 seconds" the resulting 5-seconds bars will be aligned to
|
|
612
|
+
xx:00, xx:05, xx:10 ...
|
|
613
|
+
|
|
614
|
+
# When resampling, use time boundary as target, for example if ticks data wants to resample to 5 seconds, bars will be formed at xx:00, xx:05, xx:10
|
|
615
|
+
|
|
616
|
+
- Adjbartime (default: True)
|
|
617
|
+
|
|
618
|
+
Use the time at the boundary to adjust the time of the delivered
|
|
619
|
+
resampled bar instead of the last seen timestamp. If resampling to "5
|
|
620
|
+
seconds" the time of the bar will be adjusted, for example, to hh:mm:05
|
|
621
|
+
even if the last seen timestamp was hh:mm:04.33
|
|
622
|
+
|
|
623
|
+
:note::
|
|
624
|
+
|
|
625
|
+
Time will only be adjusted if "bar2edge" is True. It wouldn't make
|
|
626
|
+
sense to adjust the time if the bar has not been aligned to a
|
|
627
|
+
boundary
|
|
628
|
+
# Adjust the last bar's final time, when bar2edge is True, use the final boundary as the last bar's time
|
|
629
|
+
|
|
630
|
+
- Rightedge (default: True)
|
|
631
|
+
|
|
632
|
+
Use the right edge of the time boundaries to set the time.
|
|
633
|
+
|
|
634
|
+
If False and compressing to 5 seconds, the time of a resampled bar for
|
|
635
|
+
seconds between hh:mm:00 and hh:mm:04 will be hh:mm:00 (the starting
|
|
636
|
+
boundary
|
|
637
|
+
|
|
638
|
+
If True, the used boundary for the time will be hh:mm:05 (the ending
|
|
639
|
+
boundary)
|
|
640
|
+
# Whether to use the right time boundary, for example if time boundary is hh:mm:00:hh:mm:05, if set to True, will use hh:mm:05
|
|
641
|
+
# Set to False, will use hh:mm:00
|
|
642
|
+
"""
|
|
643
|
+
|
|
644
|
+
# Parameters
|
|
645
|
+
params = (
|
|
646
|
+
("bar2edge", True),
|
|
647
|
+
("adjbartime", True),
|
|
648
|
+
("rightedge", True),
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
replaying = False
|
|
652
|
+
|
|
653
|
+
def _last_bar_complete(self):
|
|
654
|
+
if self.p.timeframe != TimeFrame.Days or self._source_timeframe >= TimeFrame.Days:
|
|
655
|
+
return True
|
|
656
|
+
|
|
657
|
+
if self._nexteos is None:
|
|
658
|
+
self._eosset()
|
|
659
|
+
|
|
660
|
+
return self.bar.datetime + self._source_bar_duration() + 1e-9 >= self._nextdteos
|
|
661
|
+
|
|
662
|
+
def _source_bar_duration(self):
|
|
663
|
+
if self._source_timeframe == TimeFrame.MicroSeconds:
|
|
664
|
+
return self._source_compression / 86400000000.0
|
|
665
|
+
if self._source_timeframe == TimeFrame.Seconds:
|
|
666
|
+
return self._source_compression / 86400.0
|
|
667
|
+
if self._source_timeframe == TimeFrame.Minutes:
|
|
668
|
+
return self._source_compression / 1440.0
|
|
669
|
+
return 0.0
|
|
670
|
+
|
|
671
|
+
# Called when data no longer produces bars, can be called multiple times, has chance to produce extra bars when must deliver bar
|
|
672
|
+
def last(self, data):
|
|
673
|
+
"""Called when the data is no longer producing bars
|
|
674
|
+
|
|
675
|
+
Can be called multiple times. It has the chance to (for example)
|
|
676
|
+
produce extra bars which may still be accumulated and have to be
|
|
677
|
+
delivered
|
|
678
|
+
"""
|
|
679
|
+
if self.bar.isopen():
|
|
680
|
+
if not self._last_bar_complete():
|
|
681
|
+
return False
|
|
682
|
+
|
|
683
|
+
if self.doadjusttime:
|
|
684
|
+
self._adjusttime()
|
|
685
|
+
|
|
686
|
+
data._add2stack(self.bar.lvalues())
|
|
687
|
+
self.bar.bstart(maxdate=True) # close the bar to avoid dups
|
|
688
|
+
return True
|
|
689
|
+
|
|
690
|
+
return False
|
|
691
|
+
|
|
692
|
+
# Used when calling resampler
|
|
693
|
+
def __call__(self, data, fromcheck=False, forcedata=None):
|
|
694
|
+
"""Called for each set of values produced by the data source"""
|
|
695
|
+
consumed = False
|
|
696
|
+
onedge = False
|
|
697
|
+
docheckover = True
|
|
698
|
+
if not fromcheck:
|
|
699
|
+
if self._latedata(data):
|
|
700
|
+
if not self.p.takelate:
|
|
701
|
+
data.backwards()
|
|
702
|
+
return True # get a new bar
|
|
703
|
+
|
|
704
|
+
self.bar.bupdate(data) # update new or existing bar
|
|
705
|
+
# push time beyond reference
|
|
706
|
+
self.bar.datetime = data.datetime[-1] + 0.000001
|
|
707
|
+
data.backwards() # remove used bar
|
|
708
|
+
return True
|
|
709
|
+
|
|
710
|
+
if self.componly: # only if not subdays
|
|
711
|
+
# Get a session ref before rewinding
|
|
712
|
+
_, self._lastdteos = self.data._getnexteos()
|
|
713
|
+
consumed = True
|
|
714
|
+
|
|
715
|
+
else:
|
|
716
|
+
onedge, docheckover = self._dataonedge(data) # for subdays
|
|
717
|
+
consumed = onedge
|
|
718
|
+
|
|
719
|
+
if consumed:
|
|
720
|
+
self.bar.bupdate(data) # update new or existing bar
|
|
721
|
+
data.backwards() # remove used bar
|
|
722
|
+
|
|
723
|
+
# if self.bar.isopen and (onedge or (docheckover and checkbarover))
|
|
724
|
+
cond = self.bar.isopen()
|
|
725
|
+
if cond: # original is and, the 2nd term must also be true
|
|
726
|
+
if not onedge: # onedge true is sufficient
|
|
727
|
+
if docheckover:
|
|
728
|
+
cond = self._checkbarover(data, fromcheck=fromcheck, forcedata=forcedata)
|
|
729
|
+
if cond:
|
|
730
|
+
dodeliver = False
|
|
731
|
+
if forcedata is not None:
|
|
732
|
+
# check our delivery time is not larger than that of forcedata
|
|
733
|
+
tframe = self.p.timeframe
|
|
734
|
+
if tframe == TimeFrame.Ticks: # Ticks is already the lowest
|
|
735
|
+
dodeliver = True
|
|
736
|
+
elif tframe == TimeFrame.Minutes or tframe == TimeFrame.Days:
|
|
737
|
+
dtnum = self._calcadjtime(greater=True)
|
|
738
|
+
dodeliver = dtnum <= forcedata.datetime[0]
|
|
739
|
+
else:
|
|
740
|
+
dodeliver = True
|
|
741
|
+
|
|
742
|
+
if dodeliver:
|
|
743
|
+
if not onedge and self.doadjusttime:
|
|
744
|
+
self._adjusttime(greater=True, forcedata=forcedata)
|
|
745
|
+
|
|
746
|
+
data._add2stack(self.bar.lvalues())
|
|
747
|
+
self.bar.bstart(maxdate=True) # bar delivered -> restart
|
|
748
|
+
|
|
749
|
+
if not fromcheck:
|
|
750
|
+
if not consumed:
|
|
751
|
+
self.bar.bupdate(data) # update new or existing bar
|
|
752
|
+
data.backwards() # remove used bar
|
|
753
|
+
|
|
754
|
+
return True
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
# Replayer class
|
|
758
|
+
class Replayer(_BaseResampler):
|
|
759
|
+
"""This class replays data of a given timeframe to a larger timeframe.
|
|
760
|
+
|
|
761
|
+
It simulates the action of the market by slowly building up (for ex.) a
|
|
762
|
+
daily bar from tick/seconds/minutes data
|
|
763
|
+
|
|
764
|
+
Only when the bar is complete will the "length" of the data be changed
|
|
765
|
+
effectively delivering a closed bar
|
|
766
|
+
|
|
767
|
+
Params
|
|
768
|
+
|
|
769
|
+
- Bar2edge (default: True)
|
|
770
|
+
|
|
771
|
+
Replays using time boundaries as the target of the closed bar.For
|
|
772
|
+
example, with a "ticks -> 5 seconds" the resulting 5-second bars will
|
|
773
|
+
be aligned to xx:00, xx:05, xx:10 ...
|
|
774
|
+
|
|
775
|
+
- Adjbartime (default: False)
|
|
776
|
+
|
|
777
|
+
Use the time at the boundary to adjust the time of the delivered
|
|
778
|
+
resampled bar instead of the last seen timestamp. If resampling to "5
|
|
779
|
+
seconds" the time of the bar will be adjusted, for example, to hh:mm:05
|
|
780
|
+
even if the last seen timestamp was hh:mm:04.33
|
|
781
|
+
|
|
782
|
+
*Note*
|
|
783
|
+
|
|
784
|
+
Time will only be adjusted if "bar2edge" is True. It wouldn't make
|
|
785
|
+
sense to adjust the time if the bar has not been aligned to a
|
|
786
|
+
boundary
|
|
787
|
+
|
|
788
|
+
*Note* if this parameter is True, an extra tick with the *adjusted*
|
|
789
|
+
time will be introduced at the end of the *replayed* bar
|
|
790
|
+
|
|
791
|
+
- Rightedge (default: True)
|
|
792
|
+
|
|
793
|
+
Use the right edge of the time boundaries to set the time.
|
|
794
|
+
|
|
795
|
+
If False and compressing to 5 seconds, the time of a resampled bar for
|
|
796
|
+
seconds between hh:mm:00 and hh:mm:04 will be hh:mm:00 (the starting
|
|
797
|
+
boundary
|
|
798
|
+
|
|
799
|
+
If True, the used boundary for the time will be hh:mm:05 (the ending
|
|
800
|
+
boundary)
|
|
801
|
+
"""
|
|
802
|
+
|
|
803
|
+
params = (
|
|
804
|
+
("bar2edge", True),
|
|
805
|
+
("adjbartime", False),
|
|
806
|
+
("rightedge", True),
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
replaying = True
|
|
810
|
+
|
|
811
|
+
# Run when calling class
|
|
812
|
+
def __call__(self, data, fromcheck=False, forcedata=None):
|
|
813
|
+
"""Process the data for replaying.
|
|
814
|
+
|
|
815
|
+
Manages bar replaying with session information and time alignment.
|
|
816
|
+
|
|
817
|
+
Args:
|
|
818
|
+
data: The data source to replay.
|
|
819
|
+
fromcheck: Whether this is being called from a periodic check.
|
|
820
|
+
forcedata: Optional data source to force timing from.
|
|
821
|
+
|
|
822
|
+
Returns:
|
|
823
|
+
bool: True if a new bar was generated, False otherwise.
|
|
824
|
+
"""
|
|
825
|
+
# Consume
|
|
826
|
+
consumed = False
|
|
827
|
+
# At bar generation time point
|
|
828
|
+
onedge = False
|
|
829
|
+
# Late-arriving data
|
|
830
|
+
takinglate = False
|
|
831
|
+
# Whether to check bar end
|
|
832
|
+
docheckover = True
|
|
833
|
+
# If fromcheck is False
|
|
834
|
+
if not fromcheck:
|
|
835
|
+
# Call _latedata to see how to handle late data, if returns True
|
|
836
|
+
if self._latedata(data):
|
|
837
|
+
# If takelate is False, generate a new bar
|
|
838
|
+
if not self.p.takelate:
|
|
839
|
+
data.backwards(force=True)
|
|
840
|
+
return True # get a new bar
|
|
841
|
+
# Set these two parameters
|
|
842
|
+
consumed = True
|
|
843
|
+
takinglate = True
|
|
844
|
+
# If not intraday
|
|
845
|
+
elif self.componly: # only if not subdays
|
|
846
|
+
consumed = True
|
|
847
|
+
|
|
848
|
+
else:
|
|
849
|
+
# Call _dataonedge to determine if at bar generation time and if bar is over
|
|
850
|
+
onedge, docheckover = self._dataonedge(data) # for subdays
|
|
851
|
+
consumed = onedge
|
|
852
|
+
|
|
853
|
+
data._tick_fill(force=True) # update
|
|
854
|
+
# If consumed is True, update data, if takinglate is True, set a new time for bar
|
|
855
|
+
if consumed:
|
|
856
|
+
self.bar.bupdate(data)
|
|
857
|
+
if takinglate:
|
|
858
|
+
self.bar.datetime = data.datetime[-1] + 0.000001
|
|
859
|
+
|
|
860
|
+
# if onedge or (checkbarover and self._checkbarover)
|
|
861
|
+
cond = onedge
|
|
862
|
+
# If currently not at bar generation time point, if check is needed, need to check if bar is over
|
|
863
|
+
if not cond: # original is or, if true, it would suffice
|
|
864
|
+
if docheckover:
|
|
865
|
+
cond = self._checkbarover(data, fromcheck=fromcheck)
|
|
866
|
+
# If check result returns True
|
|
867
|
+
if cond:
|
|
868
|
+
# If not exactly at bar generation time and need to adjust time
|
|
869
|
+
if not onedge and self.doadjusttime: # insert tick with adjtime
|
|
870
|
+
adjusted = self._adjusttime(greater=True)
|
|
871
|
+
# If adjustment is needed, adjust time and update bar
|
|
872
|
+
if adjusted:
|
|
873
|
+
ago = 0 if (consumed or fromcheck) else -1
|
|
874
|
+
# Update to the point right before the new data
|
|
875
|
+
data._updatebar(self.bar.lvalues(), forward=False, ago=ago)
|
|
876
|
+
# If no check needed
|
|
877
|
+
if not fromcheck:
|
|
878
|
+
# If not in consume mode, use _save2stack to save data
|
|
879
|
+
if not consumed:
|
|
880
|
+
# Reopen bar with real new data and save data to queue
|
|
881
|
+
self.bar.bupdate(data, reopen=True)
|
|
882
|
+
# erase is True, but the tick will not be seen below
|
|
883
|
+
# and therefore no need to mark as 1st
|
|
884
|
+
data._save2stack(erase=True, force=True)
|
|
885
|
+
# If in consume mode, data starts, next bar is first bar
|
|
886
|
+
else:
|
|
887
|
+
self.bar.bstart(maxdate=True)
|
|
888
|
+
self._firstbar = True # next is first
|
|
889
|
+
# If check is needed
|
|
890
|
+
else: # from check
|
|
891
|
+
# fromcheck or consumed have forced delivery, reopen
|
|
892
|
+
self.bar.bstart(maxdate=True)
|
|
893
|
+
self._firstbar = True # next is first
|
|
894
|
+
if adjusted:
|
|
895
|
+
# after adjusting need to redeliver if this was a check
|
|
896
|
+
data._save2stack(erase=True, force=True)
|
|
897
|
+
# If no check needed
|
|
898
|
+
elif not fromcheck:
|
|
899
|
+
if not consumed:
|
|
900
|
+
# Data already "forwarded" and we replay to new bar
|
|
901
|
+
# No need to go backwards.reopen the internal cache
|
|
902
|
+
self.bar.bupdate(data, reopen=True)
|
|
903
|
+
else:
|
|
904
|
+
# compression only, used data to update bar, hence remove
|
|
905
|
+
# from stream, update existing data, reopen bar
|
|
906
|
+
if not self._firstbar: # only discard data if not firstbar
|
|
907
|
+
data.backwards(force=True)
|
|
908
|
+
data._updatebar(self.bar.lvalues(), forward=False, ago=0)
|
|
909
|
+
self.bar.bstart(maxdate=True)
|
|
910
|
+
self._firstbar = True # make sure the next tick moves forward
|
|
911
|
+
# If no check needed
|
|
912
|
+
elif not fromcheck:
|
|
913
|
+
# not over, update, remove new entry, deliver
|
|
914
|
+
if not consumed:
|
|
915
|
+
self.bar.bupdate(data)
|
|
916
|
+
|
|
917
|
+
if not self._firstbar: # only discard data if not firstbar
|
|
918
|
+
data.backwards(force=True)
|
|
919
|
+
|
|
920
|
+
data._updatebar(self.bar.lvalues(), forward=False, ago=0)
|
|
921
|
+
self._firstbar = False
|
|
922
|
+
|
|
923
|
+
return False # the system can process the existing bar
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
class ResamplerTicks(Resampler):
|
|
927
|
+
"""Resampler for tick-level data."""
|
|
928
|
+
|
|
929
|
+
params = (("timeframe", TimeFrame.Ticks),)
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
class ResamplerSeconds(Resampler):
|
|
933
|
+
"""Resampler for seconds-level data."""
|
|
934
|
+
|
|
935
|
+
params = (("timeframe", TimeFrame.Seconds),)
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
class ResamplerMinutes(Resampler):
|
|
939
|
+
"""Resampler for minute-level data."""
|
|
940
|
+
|
|
941
|
+
params = (("timeframe", TimeFrame.Minutes),)
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
class ResamplerDaily(Resampler):
|
|
945
|
+
"""Resampler for daily data."""
|
|
946
|
+
|
|
947
|
+
params = (("timeframe", TimeFrame.Days),)
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
class ResamplerWeekly(Resampler):
|
|
951
|
+
"""Resampler for weekly data."""
|
|
952
|
+
|
|
953
|
+
params = (("timeframe", TimeFrame.Weeks),)
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
class ResamplerMonthly(Resampler):
|
|
957
|
+
"""Resampler for monthly data."""
|
|
958
|
+
|
|
959
|
+
params = (("timeframe", TimeFrame.Months),)
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
class ResamplerYearly(Resampler):
|
|
963
|
+
"""Resampler for yearly data."""
|
|
964
|
+
|
|
965
|
+
params = (("timeframe", TimeFrame.Years),)
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
class ReplayerTicks(Replayer):
|
|
969
|
+
"""Replayer for tick-level data."""
|
|
970
|
+
|
|
971
|
+
params = (("timeframe", TimeFrame.Ticks),)
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
class ReplayerSeconds(Replayer):
|
|
975
|
+
"""Replayer for seconds-level data."""
|
|
976
|
+
|
|
977
|
+
params = (("timeframe", TimeFrame.Seconds),)
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
class ReplayerMinutes(Replayer):
|
|
981
|
+
"""Replayer for minute-level data."""
|
|
982
|
+
|
|
983
|
+
params = (("timeframe", TimeFrame.Minutes),)
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
class ReplayerDaily(Replayer):
|
|
987
|
+
"""Replayer for daily data."""
|
|
988
|
+
|
|
989
|
+
params = (("timeframe", TimeFrame.Days),)
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
class ReplayerWeekly(Replayer):
|
|
993
|
+
"""Replayer for weekly data."""
|
|
994
|
+
|
|
995
|
+
params = (("timeframe", TimeFrame.Weeks),)
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
class ReplayerMonthly(Replayer):
|
|
999
|
+
"""Replayer for monthly data."""
|
|
1000
|
+
|
|
1001
|
+
params = (("timeframe", TimeFrame.Months),)
|