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,115 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Functional-test indicators migrated to contrib.
|
|
3
|
+
|
|
4
|
+
Generated from a single functional strategy module to preserve file-local
|
|
5
|
+
helper functions and constants without cross-test name collisions.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .. import (
|
|
9
|
+
EMA,
|
|
10
|
+
SMA,
|
|
11
|
+
Indicator,
|
|
12
|
+
SmoothedMovingAverage,
|
|
13
|
+
WeightedMovingAverage,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ZPFIndicator",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def resolve_ma_class(name):
|
|
22
|
+
"""Map a moving-average name to its backtrader indicator class.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
name: MA type name (e.g. ``sma``, ``ema``, ``smma`` or MT5-style
|
|
26
|
+
``mode_*`` variants).
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
The matching backtrader moving-average indicator class, defaulting to
|
|
30
|
+
the weighted moving average for unrecognized names.
|
|
31
|
+
"""
|
|
32
|
+
mode = str(name).lower()
|
|
33
|
+
if mode in {"sma", "mode_sma"}:
|
|
34
|
+
return SMA
|
|
35
|
+
if mode in {"ema", "mode_ema"}:
|
|
36
|
+
return EMA
|
|
37
|
+
if mode in {"smma", "mode_smma"}:
|
|
38
|
+
return SmoothedMovingAverage
|
|
39
|
+
return WeightedMovingAverage
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def resolve_price_line(data, mode):
|
|
43
|
+
"""Return the applied-price line for a data feed given a price mode.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
data: The data feed providing OHLC lines.
|
|
47
|
+
mode: Applied-price selector (e.g. ``price_close``, ``price_median``,
|
|
48
|
+
``price_typical`` or their short forms).
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
A line expression for the selected applied price, defaulting to the
|
|
52
|
+
close for unrecognized modes.
|
|
53
|
+
"""
|
|
54
|
+
price_mode = str(mode).lower()
|
|
55
|
+
if price_mode in {"price_open", "open"}:
|
|
56
|
+
return data.open
|
|
57
|
+
if price_mode in {"price_high", "high"}:
|
|
58
|
+
return data.high
|
|
59
|
+
if price_mode in {"price_low", "low"}:
|
|
60
|
+
return data.low
|
|
61
|
+
if price_mode in {"price_median", "median"}:
|
|
62
|
+
return (data.high + data.low) / 2.0
|
|
63
|
+
if price_mode in {"price_typical", "typical"}:
|
|
64
|
+
return (data.high + data.low + data.close) / 3.0
|
|
65
|
+
if price_mode in {"price_weighted", "weighted"}:
|
|
66
|
+
return (data.high + data.low + data.close + data.close) / 4.0
|
|
67
|
+
if price_mode in {"price_simpl", "simpl"}:
|
|
68
|
+
return (data.open + data.close) / 2.0
|
|
69
|
+
if price_mode in {"price_quarter", "quarter"}:
|
|
70
|
+
return (data.high + data.low + data.open + data.close) / 4.0
|
|
71
|
+
if price_mode in {"price_trendfollow0", "trendfollow0"}:
|
|
72
|
+
return (data.high + data.low + data.close + data.close) / 4.0
|
|
73
|
+
if price_mode in {"price_trendfollow1", "trendfollow1"}:
|
|
74
|
+
return (data.high + data.low + data.close + data.open + data.close) / 5.0
|
|
75
|
+
return data.close
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ZPFIndicator(Indicator):
|
|
79
|
+
"""Zero Power Flow oscillator (volume-weighted MA spread).
|
|
80
|
+
|
|
81
|
+
Multiplies a moving average of volume by the gap between a short and a long
|
|
82
|
+
moving average of an applied price, producing a volume-weighted
|
|
83
|
+
trend-strength value (``zpf``) that oscillates around zero, with symmetric
|
|
84
|
+
``line1``/``line2`` envelopes.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
lines = (
|
|
88
|
+
"line1",
|
|
89
|
+
"line2",
|
|
90
|
+
"zpf",
|
|
91
|
+
)
|
|
92
|
+
params = (
|
|
93
|
+
("xma_method", "sma"),
|
|
94
|
+
("xlength", 12),
|
|
95
|
+
("xphase", 15),
|
|
96
|
+
("ipc", "price_close"),
|
|
97
|
+
("volume_type", "tick"),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def __init__(self):
|
|
101
|
+
"""Build the price/volume moving averages and the ZPF lines."""
|
|
102
|
+
ma_cls = resolve_ma_class(self.p.xma_method)
|
|
103
|
+
price_line = resolve_price_line(self.data, self.p.ipc)
|
|
104
|
+
volume_line = (
|
|
105
|
+
self.data.volume
|
|
106
|
+
if str(self.p.volume_type).lower() == "tick"
|
|
107
|
+
else self.data.openinterest
|
|
108
|
+
)
|
|
109
|
+
self.x1ma = ma_cls(price_line, period=self.p.xlength)
|
|
110
|
+
self.x2ma = ma_cls(price_line, period=max(1, 2 * self.p.xlength))
|
|
111
|
+
self.xvol = ma_cls(volume_line, period=self.p.xlength)
|
|
112
|
+
self.lines.zpf = self.xvol * (self.x1ma - self.x2ma) / 2.0
|
|
113
|
+
self.lines.line1 = -self.lines.zpf
|
|
114
|
+
self.lines.line2 = self.lines.zpf
|
|
115
|
+
self.addminperiod(max(2 * self.p.xlength, self.p.xlength) + 2)
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Crossover Indicator Module - Crossover detection indicators.
|
|
3
|
+
|
|
4
|
+
This module provides indicators for detecting when two data series cross
|
|
5
|
+
each other (upward or downward).
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
NonZeroDifference: Tracks difference, memorizing last non-zero value (alias: NZD).
|
|
9
|
+
CrossUp: Detects upward crossover.
|
|
10
|
+
CrossDown: Detects downward crossover.
|
|
11
|
+
CrossOver: Detects both directional crossovers.
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
class MyStrategy(bt.Strategy):
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.sma_fast = bt.indicators.SMA(self.data, period=10)
|
|
17
|
+
self.sma_slow = bt.indicators.SMA(self.data, period=20)
|
|
18
|
+
self.crossover = bt.indicators.CrossOver(self.sma_fast, self.sma_slow)
|
|
19
|
+
|
|
20
|
+
def next(self):
|
|
21
|
+
if self.crossover[0] > 0:
|
|
22
|
+
self.buy()
|
|
23
|
+
elif self.crossover[0] < 0:
|
|
24
|
+
self.sell()
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from . import Indicator
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class NonZeroDifference(Indicator):
|
|
31
|
+
"""
|
|
32
|
+
Keeps track of the difference between two data inputs, memorizing
|
|
33
|
+
the last non-zero value if the current difference is zero
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
_mindatas = 2
|
|
37
|
+
alias = ("NZD",)
|
|
38
|
+
lines = ("nzd",)
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
"""Initialize the NonZeroDifference indicator.
|
|
42
|
+
|
|
43
|
+
Tracks difference between two data sources.
|
|
44
|
+
"""
|
|
45
|
+
super().__init__()
|
|
46
|
+
|
|
47
|
+
def nextstart(self):
|
|
48
|
+
"""Initialize NZD on first valid bar.
|
|
49
|
+
|
|
50
|
+
Sets initial difference value.
|
|
51
|
+
"""
|
|
52
|
+
self.l.nzd[0] = self.data0[0] - self.data1[0]
|
|
53
|
+
|
|
54
|
+
def next(self):
|
|
55
|
+
"""Calculate NZD for the current bar.
|
|
56
|
+
|
|
57
|
+
Memorizes last non-zero difference when current difference is zero.
|
|
58
|
+
"""
|
|
59
|
+
d = self.data0[0] - self.data1[0]
|
|
60
|
+
# Memorize last non-zero value
|
|
61
|
+
new_val = d if d else self.l.nzd[-1]
|
|
62
|
+
self.l.nzd[0] = new_val
|
|
63
|
+
|
|
64
|
+
# Don't override once() - let framework call next() for each bar
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class _CrossBase(Indicator):
|
|
68
|
+
_mindatas = 2
|
|
69
|
+
lines = ("cross",)
|
|
70
|
+
plotinfo = {"plotymargin": 0.05, "plotyhlines": [0.0, 1.0]}
|
|
71
|
+
|
|
72
|
+
def __init__(self):
|
|
73
|
+
"""Initialize the crossover base indicator.
|
|
74
|
+
|
|
75
|
+
Creates NonZeroDifference for crossover detection.
|
|
76
|
+
"""
|
|
77
|
+
super().__init__() # CRITICAL: Call parent init first
|
|
78
|
+
self.nzd = NonZeroDifference(self.data0, self.data1)
|
|
79
|
+
|
|
80
|
+
def next(self):
|
|
81
|
+
"""Detect crossover for the current bar.
|
|
82
|
+
|
|
83
|
+
Returns 1.0 if crossover detected, 0.0 otherwise.
|
|
84
|
+
"""
|
|
85
|
+
# Check for crossover
|
|
86
|
+
if hasattr(self, "_crossup"):
|
|
87
|
+
if self._crossup:
|
|
88
|
+
# Upward cross: previous diff < 0 (strictly), now data0 > data1
|
|
89
|
+
before = self.nzd(-1) < 0.0 # STRICT inequality
|
|
90
|
+
after = self.data0[0] > self.data1[0]
|
|
91
|
+
else:
|
|
92
|
+
# Downward cross: previous diff > 0 (strictly), now data0 < data1
|
|
93
|
+
before = self.nzd(-1) > 0.0 # STRICT inequality
|
|
94
|
+
after = self.data0[0] < self.data1[0]
|
|
95
|
+
|
|
96
|
+
self.lines.cross[0] = 1.0 if (before and after) else 0.0
|
|
97
|
+
else:
|
|
98
|
+
self.lines.cross[0] = 0.0
|
|
99
|
+
|
|
100
|
+
# Don't override once() - let framework call next() for each bar
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class CrossUp(_CrossBase):
|
|
104
|
+
"""Upward cross indicator"""
|
|
105
|
+
|
|
106
|
+
_crossup = True
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class CrossDown(_CrossBase):
|
|
110
|
+
"""Downward cross indicator"""
|
|
111
|
+
|
|
112
|
+
_crossup = False
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class CrossOver(Indicator):
|
|
116
|
+
"""
|
|
117
|
+
Gives signal for data crossover:
|
|
118
|
+
1.0 for upward cross, -1.0 for downward cross, 0.0 otherwise
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
_mindatas = 2
|
|
122
|
+
lines = ("crossover",)
|
|
123
|
+
plotinfo = {"plotymargin": 0.05, "plotyhlines": [-1.0, 1.0]}
|
|
124
|
+
|
|
125
|
+
def __init__(self):
|
|
126
|
+
"""Initialize the CrossOver indicator.
|
|
127
|
+
|
|
128
|
+
Sets up minperiod and tracking variables for crossover detection.
|
|
129
|
+
"""
|
|
130
|
+
super().__init__()
|
|
131
|
+
# CRITICAL FIX: Inherit minperiod from data sources first
|
|
132
|
+
# This is needed because the framework's automatic inheritance isn't working
|
|
133
|
+
if hasattr(self, "datas") and self.datas:
|
|
134
|
+
data_minperiods = [getattr(d, "_minperiod", 1) for d in self.datas]
|
|
135
|
+
self._minperiod = max([self._minperiod] + data_minperiods)
|
|
136
|
+
# CRITICAL FIX: Add minperiod for lookback requirement (nzd(-1) in master)
|
|
137
|
+
# addminperiod(n) adds n-1 to minperiod, so addminperiod(2) adds 1
|
|
138
|
+
self.addminperiod(2)
|
|
139
|
+
# For next() mode: track last non-zero difference
|
|
140
|
+
self._last_nzd = None
|
|
141
|
+
# CRITICAL FIX: Track owner's data length to detect replay mode
|
|
142
|
+
# In replay mode, we should only calculate crossover when the bar is complete
|
|
143
|
+
# The owner (strategy) has the actual data feed whose length changes when bars complete
|
|
144
|
+
self._last_owner_data_len = 0
|
|
145
|
+
self._owner_data = None # Will be set to owner's data feed in next()
|
|
146
|
+
|
|
147
|
+
def prenext(self):
|
|
148
|
+
"""Track difference during warmup period.
|
|
149
|
+
|
|
150
|
+
Updates _last_nzd for use in nextstart/next crossover detection.
|
|
151
|
+
"""
|
|
152
|
+
# Track difference during warmup period so _last_nzd is available in nextstart
|
|
153
|
+
# This is similar to MACD's prenext that calculates MACD values during warmup
|
|
154
|
+
diff = self.data0[0] - self.data1[0]
|
|
155
|
+
# Update _last_nzd (memorize non-zero)
|
|
156
|
+
if self._last_nzd is None:
|
|
157
|
+
self._last_nzd = diff
|
|
158
|
+
else:
|
|
159
|
+
self._last_nzd = diff if diff != 0.0 else self._last_nzd
|
|
160
|
+
|
|
161
|
+
def nextstart(self):
|
|
162
|
+
"""Calculate crossover on first valid bar.
|
|
163
|
+
|
|
164
|
+
Handles replay mode special case and calculates initial crossover.
|
|
165
|
+
"""
|
|
166
|
+
# CRITICAL FIX: In replay mode, the first bar after minperiod doesn't have a valid
|
|
167
|
+
# "previous" bar in the compressed timeframe context. Skip crossover calculation
|
|
168
|
+
# on the first bar ONLY when in replay mode. For normal mode, calculate normally.
|
|
169
|
+
diff = self.data0[0] - self.data1[0]
|
|
170
|
+
|
|
171
|
+
# Check if we're in replay mode by checking owner's datas for replaying attribute
|
|
172
|
+
is_replay = False
|
|
173
|
+
if hasattr(self, "_owner") and hasattr(self._owner, "datas"):
|
|
174
|
+
for data in self._owner.datas:
|
|
175
|
+
if hasattr(data, "replaying") and data.replaying > 0:
|
|
176
|
+
is_replay = True
|
|
177
|
+
break
|
|
178
|
+
|
|
179
|
+
if is_replay:
|
|
180
|
+
# In replay mode, skip crossover on first bar - set to 0 and update _last_nzd
|
|
181
|
+
self.lines.crossover[0] = 0.0
|
|
182
|
+
# Update _last_nzd for next()
|
|
183
|
+
prev_nzd = self._last_nzd if self._last_nzd is not None else diff
|
|
184
|
+
self._last_nzd = diff if diff != 0.0 else prev_nzd
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
# Normal mode: calculate crossover normally
|
|
188
|
+
# Get previous non-zero difference (set during prenext)
|
|
189
|
+
prev_nzd = self._last_nzd if self._last_nzd is not None else diff
|
|
190
|
+
|
|
191
|
+
# Check for crossover
|
|
192
|
+
up_cross = 1.0 if (prev_nzd < 0.0 and self.data0[0] > self.data1[0]) else 0.0
|
|
193
|
+
down_cross = 1.0 if (prev_nzd > 0.0 and self.data0[0] < self.data1[0]) else 0.0
|
|
194
|
+
self.lines.crossover[0] = up_cross - down_cross
|
|
195
|
+
|
|
196
|
+
# Update _last_nzd for next()
|
|
197
|
+
self._last_nzd = diff if diff != 0.0 else prev_nzd
|
|
198
|
+
|
|
199
|
+
def next(self):
|
|
200
|
+
"""Calculate crossover for the current bar.
|
|
201
|
+
|
|
202
|
+
Returns 1.0 for upward cross, -1.0 for downward cross, 0.0 otherwise.
|
|
203
|
+
Handles replay mode correctly by deferring calculation when bars are updating.
|
|
204
|
+
"""
|
|
205
|
+
# Current difference
|
|
206
|
+
diff = self.data0[0] - self.data1[0]
|
|
207
|
+
|
|
208
|
+
# CRITICAL FIX: In replay mode with runonce, the same bar is updated multiple times.
|
|
209
|
+
# The key insight is that we should only calculate crossover when we're at a NEW bar
|
|
210
|
+
# (idx has advanced), not when we're updating the same bar multiple times.
|
|
211
|
+
# We detect this by checking if idx < len - 1, which means we haven't advanced yet.
|
|
212
|
+
# IMPORTANT: Only apply this logic in replay mode, not in exactbars mode!
|
|
213
|
+
|
|
214
|
+
# Check if we're in replay mode
|
|
215
|
+
is_replay = False
|
|
216
|
+
if hasattr(self, "_owner") and hasattr(self._owner, "datas"):
|
|
217
|
+
for data in self._owner.datas:
|
|
218
|
+
if hasattr(data, "replaying") and data.replaying > 0:
|
|
219
|
+
is_replay = True
|
|
220
|
+
break
|
|
221
|
+
|
|
222
|
+
# Only defer crossover calculation in replay mode
|
|
223
|
+
if is_replay and hasattr(self.lines[0], "idx") and hasattr(self.lines[0], "__len__"):
|
|
224
|
+
current_idx = self.lines[0].idx
|
|
225
|
+
current_len = len(self.lines[0])
|
|
226
|
+
# If idx < len - 1, we're still filling the current bar, not at a new bar yet
|
|
227
|
+
# Defer crossover calculation by updating _last_nzd but setting crossover to 0
|
|
228
|
+
if current_idx < current_len - 1:
|
|
229
|
+
# Still updating current bar - defer crossover calculation
|
|
230
|
+
if self._last_nzd is None or diff != 0.0:
|
|
231
|
+
self._last_nzd = diff
|
|
232
|
+
self.lines.crossover[0] = 0.0
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
# At this point, we're at a new bar (idx == len - 1), calculate crossover
|
|
236
|
+
# using the previous bar's difference (stored in _last_nzd or from data[-1])
|
|
237
|
+
try:
|
|
238
|
+
prev_diff = self.data0[-1] - self.data1[-1]
|
|
239
|
+
# Find last non-zero difference by looking at line values
|
|
240
|
+
if prev_diff == 0.0:
|
|
241
|
+
prev_nzd = self._last_nzd if self._last_nzd is not None else diff
|
|
242
|
+
else:
|
|
243
|
+
prev_nzd = prev_diff
|
|
244
|
+
except (IndexError, TypeError):
|
|
245
|
+
# Fall back to cached value
|
|
246
|
+
prev_nzd = self._last_nzd if self._last_nzd is not None else diff
|
|
247
|
+
|
|
248
|
+
# Update _last_nzd for next bar
|
|
249
|
+
if self._last_nzd is None or diff != 0.0:
|
|
250
|
+
self._last_nzd = diff
|
|
251
|
+
|
|
252
|
+
# Check for crossover using STRICT inequalities
|
|
253
|
+
# Upward: prev < 0 and now data0 > data1
|
|
254
|
+
up_cross = 1.0 if (prev_nzd < 0.0 and self.data0[0] > self.data1[0]) else 0.0
|
|
255
|
+
|
|
256
|
+
# Downward: prev > 0 and now data0 < data1
|
|
257
|
+
down_cross = 1.0 if (prev_nzd > 0.0 and self.data0[0] < self.data1[0]) else 0.0
|
|
258
|
+
|
|
259
|
+
# Combine
|
|
260
|
+
self.lines.crossover[0] = up_cross - down_cross
|
|
261
|
+
|
|
262
|
+
def once(self, start, end):
|
|
263
|
+
"""Calculate crossover in runonce mode.
|
|
264
|
+
|
|
265
|
+
Vectorized implementation that processes all bars at once.
|
|
266
|
+
"""
|
|
267
|
+
# Vectorized once() implementation matching next() behavior
|
|
268
|
+
d0array = self.data0.array
|
|
269
|
+
d1array = self.data1.array
|
|
270
|
+
crossarray = self.line.array
|
|
271
|
+
|
|
272
|
+
# Handle case where data is shorter than minperiod
|
|
273
|
+
if start >= end:
|
|
274
|
+
# No bars to process - initialize all to 0
|
|
275
|
+
while len(crossarray) < len(d0array):
|
|
276
|
+
crossarray.append(0.0)
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
# Ensure array is large enough
|
|
280
|
+
while len(crossarray) < end:
|
|
281
|
+
crossarray.append(0.0)
|
|
282
|
+
|
|
283
|
+
# Defensive bound: never read past either input array. This protects
|
|
284
|
+
# against orphan sub-indicators (e.g., bt.indicators.SMA constructed
|
|
285
|
+
# at module level with no data) whose array remains empty when used
|
|
286
|
+
# as a CrossOver data source.
|
|
287
|
+
effective_end = min(end, len(d0array), len(d1array))
|
|
288
|
+
if effective_end <= start:
|
|
289
|
+
return
|
|
290
|
+
|
|
291
|
+
# Initialize prev_nzd from prenext period (bar before start)
|
|
292
|
+
# This matches next() which uses _last_nzd set during prenext
|
|
293
|
+
if start > 0 and start - 1 < len(d0array) and start - 1 < len(d1array):
|
|
294
|
+
prev_nzd = d0array[start - 1] - d1array[start - 1]
|
|
295
|
+
# Scan backwards to find last non-zero difference (like prenext does)
|
|
296
|
+
for j in range(start - 1, -1, -1):
|
|
297
|
+
diff_j = d0array[j] - d1array[j]
|
|
298
|
+
if diff_j != 0.0:
|
|
299
|
+
prev_nzd = diff_j
|
|
300
|
+
break
|
|
301
|
+
else:
|
|
302
|
+
prev_nzd = 0.0
|
|
303
|
+
|
|
304
|
+
# CRITICAL FIX: For replay mode, skip crossover on the very first bar.
|
|
305
|
+
# The first bar after minperiod doesn't have a valid "previous" bar in the
|
|
306
|
+
# compressed timeframe context. Defer crossover to the second bar.
|
|
307
|
+
# This prevents false positive crossovers at the start of replay data.
|
|
308
|
+
# ONLY apply this fix when in replay mode.
|
|
309
|
+
is_replay = False
|
|
310
|
+
if hasattr(self, "_owner") and hasattr(self._owner, "datas"):
|
|
311
|
+
for data in self._owner.datas:
|
|
312
|
+
if hasattr(data, "replaying") and data.replaying > 0:
|
|
313
|
+
is_replay = True
|
|
314
|
+
break
|
|
315
|
+
|
|
316
|
+
first_bar = start if is_replay else -1 # -1 means never skip
|
|
317
|
+
|
|
318
|
+
# Process ALL bars from start
|
|
319
|
+
for i in range(start, effective_end):
|
|
320
|
+
d0_val = d0array[i]
|
|
321
|
+
d1_val = d1array[i]
|
|
322
|
+
diff = d0_val - d1_val
|
|
323
|
+
|
|
324
|
+
# Skip crossover calculation on first bar ONLY in replay mode
|
|
325
|
+
if i == first_bar:
|
|
326
|
+
crossarray[i] = 0.0
|
|
327
|
+
# Still update prev_nzd for next iteration
|
|
328
|
+
prev_nzd = diff if diff != 0.0 else prev_nzd
|
|
329
|
+
continue
|
|
330
|
+
|
|
331
|
+
# Check crossover using prev_nzd (from previous bar)
|
|
332
|
+
up_cross = 1.0 if (prev_nzd < 0.0 and d0_val > d1_val) else 0.0
|
|
333
|
+
down_cross = 1.0 if (prev_nzd > 0.0 and d0_val < d1_val) else 0.0
|
|
334
|
+
crossarray[i] = up_cross - down_cross
|
|
335
|
+
|
|
336
|
+
# Update prev_nzd for next iteration (memorize non-zero)
|
|
337
|
+
prev_nzd = diff if diff != 0.0 else prev_nzd
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""DEMA/TEMA Indicator Module - Double/Triple Exponential Moving Average.
|
|
3
|
+
|
|
4
|
+
This module provides DEMA and TEMA indicators introduced by Patrick G. Mulloy
|
|
5
|
+
in 1994 to reduce the lag associated with traditional moving averages.
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
DoubleExponentialMovingAverage: DEMA indicator (alias: DEMA).
|
|
9
|
+
TripleExponentialMovingAverage: TEMA indicator (alias: TEMA).
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
class MyStrategy(bt.Strategy):
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.dema = bt.indicators.DEMA(self.data.close, period=20)
|
|
15
|
+
|
|
16
|
+
def next(self):
|
|
17
|
+
if self.data.close[0] > self.dema[0]:
|
|
18
|
+
self.buy()
|
|
19
|
+
elif self.data.close[0] < self.dema[0]:
|
|
20
|
+
self.sell()
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import math
|
|
24
|
+
|
|
25
|
+
from . import MovingAverageBase
|
|
26
|
+
from .ema import EMA
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DoubleExponentialMovingAverage(MovingAverageBase):
|
|
30
|
+
"""
|
|
31
|
+
DEMA was first time introduced in 1994, in the article "Smoothing Data with
|
|
32
|
+
Faster-Moving Averages" by Patrick G. Mulloy in "Technical Analysis of
|
|
33
|
+
Stocks & Commodities" magazine.
|
|
34
|
+
|
|
35
|
+
It attempts to reduce the inherent lag associated with Moving Averages
|
|
36
|
+
|
|
37
|
+
Formula:
|
|
38
|
+
- dema = (2.0 - ema(data, period) - ema(ema(data, period), period)
|
|
39
|
+
|
|
40
|
+
See:
|
|
41
|
+
(None)
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
alias = (
|
|
45
|
+
"DEMA",
|
|
46
|
+
"MovingAverageDoubleExponential",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
lines = ("dema",)
|
|
50
|
+
params = (("_movav", EMA),)
|
|
51
|
+
|
|
52
|
+
def __init__(self):
|
|
53
|
+
"""Initialize the DEMA indicator.
|
|
54
|
+
|
|
55
|
+
Creates two EMAs for the DEMA calculation.
|
|
56
|
+
"""
|
|
57
|
+
super().__init__()
|
|
58
|
+
self.ema1 = self.p._movav(self.data, period=self.p.period)
|
|
59
|
+
self.ema2 = self.p._movav(self.ema1, period=self.p.period)
|
|
60
|
+
# minperiod = 2 * period - 1 for DEMA
|
|
61
|
+
self._minperiod = max(self._minperiod, 2 * self.p.period - 1)
|
|
62
|
+
|
|
63
|
+
def next(self):
|
|
64
|
+
"""Calculate DEMA for the current bar.
|
|
65
|
+
|
|
66
|
+
Formula: DEMA = 2 * EMA1 - EMA(EMA1)
|
|
67
|
+
"""
|
|
68
|
+
self.lines.dema[0] = 2.0 * self.ema1[0] - self.ema2[0]
|
|
69
|
+
|
|
70
|
+
def once(self, start, end):
|
|
71
|
+
"""Calculate DEMA in runonce mode."""
|
|
72
|
+
ema1_array = self.ema1.lines[0].array
|
|
73
|
+
ema2_array = self.ema2.lines[0].array
|
|
74
|
+
larray = self.lines.dema.array
|
|
75
|
+
|
|
76
|
+
while len(larray) < end:
|
|
77
|
+
larray.append(float("nan"))
|
|
78
|
+
|
|
79
|
+
minperiod = 2 * self.p.period - 1
|
|
80
|
+
for i in range(min(minperiod - 1, len(ema1_array))):
|
|
81
|
+
if i < len(larray):
|
|
82
|
+
larray[i] = float("nan")
|
|
83
|
+
|
|
84
|
+
for i in range(minperiod - 1, min(end, len(ema1_array), len(ema2_array))):
|
|
85
|
+
ema1_val = ema1_array[i] if i < len(ema1_array) else 0.0
|
|
86
|
+
ema2_val = ema2_array[i] if i < len(ema2_array) else 0.0
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
isinstance(ema1_val, float)
|
|
90
|
+
and math.isnan(ema1_val)
|
|
91
|
+
or isinstance(ema2_val, float)
|
|
92
|
+
and math.isnan(ema2_val)
|
|
93
|
+
):
|
|
94
|
+
larray[i] = float("nan")
|
|
95
|
+
else:
|
|
96
|
+
larray[i] = 2.0 * ema1_val - ema2_val
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# Triple Exponential Moving Average
|
|
100
|
+
class TripleExponentialMovingAverage(MovingAverageBase):
|
|
101
|
+
"""
|
|
102
|
+
TEMA was first time introduced in 1994, in the article "Smoothing Data with
|
|
103
|
+
Faster-Moving Averages" by Patrick G. Mulloy in "Technical Analysis of
|
|
104
|
+
Stocks & Commodities" magazine.
|
|
105
|
+
|
|
106
|
+
It attempts to reduce the inherent lag associated with Moving Averages
|
|
107
|
+
|
|
108
|
+
Formula:
|
|
109
|
+
- ema1 = ema(data, period)
|
|
110
|
+
- ema2 = ema(ema1, period)
|
|
111
|
+
- ema3 = ema(ema2, period)
|
|
112
|
+
- tema = 3 * ema1 - 3 * ema2 + ema3
|
|
113
|
+
|
|
114
|
+
See:
|
|
115
|
+
(None)
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
alias = (
|
|
119
|
+
"TEMA",
|
|
120
|
+
"MovingAverageTripleExponential",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
lines = ("tema",)
|
|
124
|
+
params = (("_movav", EMA),)
|
|
125
|
+
|
|
126
|
+
def __init__(self):
|
|
127
|
+
"""Initialize the TEMA indicator.
|
|
128
|
+
|
|
129
|
+
Creates three EMAs for the TEMA calculation.
|
|
130
|
+
"""
|
|
131
|
+
super().__init__()
|
|
132
|
+
self.ema1 = self.p._movav(self.data, period=self.p.period)
|
|
133
|
+
self.ema2 = self.p._movav(self.ema1, period=self.p.period)
|
|
134
|
+
self.ema3 = self.p._movav(self.ema2, period=self.p.period)
|
|
135
|
+
# minperiod = 3 * period - 2 for TEMA
|
|
136
|
+
self._minperiod = max(self._minperiod, 3 * self.p.period - 2)
|
|
137
|
+
|
|
138
|
+
def next(self):
|
|
139
|
+
"""Calculate TEMA for the current bar.
|
|
140
|
+
|
|
141
|
+
Formula: TEMA = 3 * EMA1 - 3 * EMA2 + EMA3
|
|
142
|
+
"""
|
|
143
|
+
self.lines.tema[0] = 3.0 * self.ema1[0] - 3.0 * self.ema2[0] + self.ema3[0]
|
|
144
|
+
|
|
145
|
+
def once(self, start, end):
|
|
146
|
+
"""Calculate TEMA in runonce mode."""
|
|
147
|
+
ema1_array = self.ema1.lines[0].array
|
|
148
|
+
ema2_array = self.ema2.lines[0].array
|
|
149
|
+
ema3_array = self.ema3.lines[0].array
|
|
150
|
+
larray = self.lines.tema.array
|
|
151
|
+
|
|
152
|
+
while len(larray) < end:
|
|
153
|
+
larray.append(float("nan"))
|
|
154
|
+
|
|
155
|
+
minperiod = 3 * self.p.period - 2
|
|
156
|
+
for i in range(min(minperiod - 1, len(ema1_array))):
|
|
157
|
+
if i < len(larray):
|
|
158
|
+
larray[i] = float("nan")
|
|
159
|
+
|
|
160
|
+
for i in range(minperiod - 1, min(end, len(ema1_array), len(ema2_array), len(ema3_array))):
|
|
161
|
+
ema1_val = ema1_array[i] if i < len(ema1_array) else 0.0
|
|
162
|
+
ema2_val = ema2_array[i] if i < len(ema2_array) else 0.0
|
|
163
|
+
ema3_val = ema3_array[i] if i < len(ema3_array) else 0.0
|
|
164
|
+
|
|
165
|
+
if (
|
|
166
|
+
isinstance(ema1_val, float)
|
|
167
|
+
and math.isnan(ema1_val)
|
|
168
|
+
or isinstance(ema2_val, float)
|
|
169
|
+
and math.isnan(ema2_val)
|
|
170
|
+
or isinstance(ema3_val, float)
|
|
171
|
+
and math.isnan(ema3_val)
|
|
172
|
+
):
|
|
173
|
+
larray[i] = float("nan")
|
|
174
|
+
else:
|
|
175
|
+
larray[i] = 3.0 * ema1_val - 3.0 * ema2_val + ema3_val
|