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,1071 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Directional Movement Indicator Module - ADX and DI indicators.
|
|
3
|
+
|
|
4
|
+
This module provides the ADX (Average Directional Index) and
|
|
5
|
+
Directional Indicators developed by J. Welles Wilder, Jr. for
|
|
6
|
+
measuring trend strength.
|
|
7
|
+
|
|
8
|
+
Classes:
|
|
9
|
+
UpMove: Upward move calculation.
|
|
10
|
+
DownMove: Downward move calculation.
|
|
11
|
+
_DirectionalIndicator: Base class for DI calculations.
|
|
12
|
+
DirectionalIndicator: DI indicator (alias: DI).
|
|
13
|
+
PlusDirectionalIndicator: +DI indicator (aliases: PlusDI, +DI).
|
|
14
|
+
MinusDirectionalIndicator: -DI indicator (aliases: MinusDI, -DI).
|
|
15
|
+
AverageDirectionalMovementIndex: ADX indicator (alias: ADX).
|
|
16
|
+
AverageDirectionalMovementIndexRating: ADXR indicator (alias: ADXR).
|
|
17
|
+
DirectionalMovementIndex: DMI with ADX and DI (alias: DMI).
|
|
18
|
+
DirectionalMovement: Complete DM system (alias: DM).
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
class MyStrategy(bt.Strategy):
|
|
22
|
+
def __init__(self):
|
|
23
|
+
# Calculate ADX to measure trend strength
|
|
24
|
+
self.adx = bt.indicators.ADX(self.data, period=14)
|
|
25
|
+
|
|
26
|
+
# Or use DI for +DI and -DI
|
|
27
|
+
self.di = bt.indicators.DI(self.data, period=14)
|
|
28
|
+
|
|
29
|
+
def next(self):
|
|
30
|
+
# Buy when trend is strong (ADX > 25) and +DI crosses above -DI
|
|
31
|
+
if self.adx[0] > 25 and self.di.plusDI[0] > self.di.minusDI[0]:
|
|
32
|
+
self.buy()
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from ..lineroot import LineRoot
|
|
36
|
+
from . import ATR, And, If, Indicator, MovAv
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class UpMove(Indicator):
|
|
40
|
+
"""
|
|
41
|
+
Defined by J. Welles Wilder, Jr. in 1978 in his book *"New Concepts in
|
|
42
|
+
Technical Trading Systems"* as part of the Directional Move System to
|
|
43
|
+
calculate Directional Indicators.
|
|
44
|
+
|
|
45
|
+
Positive if the given data has moved higher than the previous day
|
|
46
|
+
|
|
47
|
+
Formula:
|
|
48
|
+
- upmove = data - data(-1)
|
|
49
|
+
|
|
50
|
+
See:
|
|
51
|
+
- https://en.wikipedia.org/wiki/Average_directional_movement_index
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
lines = ("upmove",)
|
|
55
|
+
|
|
56
|
+
def __init__(self):
|
|
57
|
+
"""Initialize the UpMove indicator.
|
|
58
|
+
|
|
59
|
+
Sets minimum period to 2 for difference calculation.
|
|
60
|
+
"""
|
|
61
|
+
super().__init__()
|
|
62
|
+
self.addminperiod(2)
|
|
63
|
+
|
|
64
|
+
def next(self):
|
|
65
|
+
"""Calculate up move for the current bar.
|
|
66
|
+
|
|
67
|
+
Returns data - data(-1), the positive price change.
|
|
68
|
+
"""
|
|
69
|
+
self.lines.upmove[0] = self.data[0] - self.data[-1]
|
|
70
|
+
|
|
71
|
+
def once(self, start, end):
|
|
72
|
+
"""Calculate up moves in runonce mode.
|
|
73
|
+
|
|
74
|
+
Computes data[i] - data[i-1] for each bar.
|
|
75
|
+
"""
|
|
76
|
+
darray = self.data.array
|
|
77
|
+
larray = self.lines.upmove.array
|
|
78
|
+
|
|
79
|
+
while len(larray) < end:
|
|
80
|
+
larray.append(float("nan"))
|
|
81
|
+
|
|
82
|
+
for i in range(1, min(end, len(darray))):
|
|
83
|
+
larray[i] = darray[i] - darray[i - 1]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class DownMove(Indicator):
|
|
87
|
+
"""
|
|
88
|
+
Defined by J. Welles Wilder, Jr. in 1978 in his book *"New Concepts in
|
|
89
|
+
Technical Trading Systems"* as part of the Directional Move System to
|
|
90
|
+
calculate Directional Indicators.
|
|
91
|
+
|
|
92
|
+
Positive if the given data has moved lower than the previous day
|
|
93
|
+
|
|
94
|
+
Formula:
|
|
95
|
+
- downmove = data(-1) - data
|
|
96
|
+
|
|
97
|
+
See:
|
|
98
|
+
- https://en.wikipedia.org/wiki/Average_directional_movement_index
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
lines = ("downmove",)
|
|
102
|
+
|
|
103
|
+
def __init__(self):
|
|
104
|
+
"""Initialize the DownMove indicator.
|
|
105
|
+
|
|
106
|
+
Sets minimum period to 2 for difference calculation.
|
|
107
|
+
"""
|
|
108
|
+
super().__init__()
|
|
109
|
+
self.addminperiod(2)
|
|
110
|
+
|
|
111
|
+
def next(self):
|
|
112
|
+
"""Calculate down move for the current bar.
|
|
113
|
+
|
|
114
|
+
Returns data(-1) - data, the negative price change as positive value.
|
|
115
|
+
"""
|
|
116
|
+
self.lines.downmove[0] = self.data[-1] - self.data[0]
|
|
117
|
+
|
|
118
|
+
def once(self, start, end):
|
|
119
|
+
"""Calculate down moves in runonce mode.
|
|
120
|
+
|
|
121
|
+
Computes data[i-1] - data[i] for each bar.
|
|
122
|
+
"""
|
|
123
|
+
darray = self.data.array
|
|
124
|
+
larray = self.lines.downmove.array
|
|
125
|
+
|
|
126
|
+
while len(larray) < end:
|
|
127
|
+
larray.append(float("nan"))
|
|
128
|
+
|
|
129
|
+
for i in range(1, min(end, len(darray))):
|
|
130
|
+
larray[i] = darray[i - 1] - darray[i]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class _DirectionalIndicator(Indicator):
|
|
134
|
+
"""
|
|
135
|
+
This class serves as the root base class for all "Directional Movement
|
|
136
|
+
System" related indicators, given that the calculations are first common
|
|
137
|
+
and then derived from the common calculations.
|
|
138
|
+
|
|
139
|
+
It can calculate the +DI and -DI values (using kwargs as the hint as to
|
|
140
|
+
what to calculate) but doesn't assign them to lines. This is left for
|
|
141
|
+
sublcases of this class.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
params = (("period", 14), ("movav", MovAv.Smoothed))
|
|
145
|
+
|
|
146
|
+
plotlines = {"plusDI": {"_name": "+DI"}, "minusDI": {"_name": "-DI"}}
|
|
147
|
+
|
|
148
|
+
def _plotlabel(self):
|
|
149
|
+
plabels = [self.p.period]
|
|
150
|
+
plabels += [self.p.movav] * self.p.notdefault("movav")
|
|
151
|
+
return plabels
|
|
152
|
+
|
|
153
|
+
def __init__(self, _plus=True, _minus=True):
|
|
154
|
+
"""Initialize the directional indicator base.
|
|
155
|
+
|
|
156
|
+
Calculates +DI and -DI values based on directional movement.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
_plus: Whether to calculate plus DI.
|
|
160
|
+
_minus: Whether to calculate minus DI.
|
|
161
|
+
"""
|
|
162
|
+
self._di_atr = ATR(self.data, period=self.p.period, movav=self.p.movav)
|
|
163
|
+
|
|
164
|
+
upmove = self.data.high - self.data.high(-1)
|
|
165
|
+
downmove = self.data.low(-1) - self.data.low
|
|
166
|
+
|
|
167
|
+
if _plus:
|
|
168
|
+
plus = And(upmove > downmove, upmove > 0.0)
|
|
169
|
+
plusDM = If(plus, upmove, 0.0)
|
|
170
|
+
self._plusDMav = self.p.movav(plusDM, period=self.p.period)
|
|
171
|
+
|
|
172
|
+
self.DIplus = 100.0 * self._plusDMav / self._di_atr
|
|
173
|
+
|
|
174
|
+
if _minus:
|
|
175
|
+
minus = And(downmove > upmove, downmove > 0.0)
|
|
176
|
+
minusDM = If(minus, downmove, 0.0)
|
|
177
|
+
self._minusDMav = self.p.movav(minusDM, period=self.p.period)
|
|
178
|
+
|
|
179
|
+
self.DIminus = 100.0 * self._minusDMav / self._di_atr
|
|
180
|
+
|
|
181
|
+
super().__init__()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class DirectionalIndicator(_DirectionalIndicator):
|
|
185
|
+
"""
|
|
186
|
+
Defined by J. Welles Wilder, Jr. in 1978 in his book *"New Concepts in
|
|
187
|
+
Technical Trading Systems"*.
|
|
188
|
+
|
|
189
|
+
Intended to measure trend strength
|
|
190
|
+
|
|
191
|
+
This indicator shows +DI, -DI:
|
|
192
|
+
- Use PlusDirectionalIndicator (PlusDI) to get +DI
|
|
193
|
+
- Use MinusDirectionalIndicator (MinusDI) to get -DI
|
|
194
|
+
- Use AverageDirectionalIndex (ADX) to get ADX
|
|
195
|
+
- Use AverageDirectionalIndexRating (ADXR) to get ADX, ADXR
|
|
196
|
+
- Use DirectionalMovementIndex (DMI) to get ADX, +DI, -DI
|
|
197
|
+
- Use DirectionalMovement (DM) to get ADX, ADXR, +DI, -DI
|
|
198
|
+
|
|
199
|
+
Formula:
|
|
200
|
+
- upmove = high - high(-1)
|
|
201
|
+
- downmove = low(-1) - low
|
|
202
|
+
- +dm = upmove if upmove > downmove and upmove > 0 else 0
|
|
203
|
+
- -dm = downmove if downmove > upmove and downmove > 0 else 0
|
|
204
|
+
- +di = 100 * MovingAverage(+dm, period) / atr(period)
|
|
205
|
+
- -di = 100 * MovingAverage(-dm, period) / atr(period)
|
|
206
|
+
|
|
207
|
+
The moving average used is the one originally defined by Wilder,
|
|
208
|
+
the SmoothedMovingAverage
|
|
209
|
+
|
|
210
|
+
See:
|
|
211
|
+
- https://en.wikipedia.org/wiki/Average_directional_movement_index
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
alias = ("DI",)
|
|
215
|
+
lines = (
|
|
216
|
+
"plusDI",
|
|
217
|
+
"minusDI",
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def __init__(self):
|
|
221
|
+
"""Initialize the Directional Indicator.
|
|
222
|
+
|
|
223
|
+
Calculates both +DI and -DI from raw OHLC using Wilder smoothing.
|
|
224
|
+
"""
|
|
225
|
+
super().__init__()
|
|
226
|
+
self.lines.plusDI = self.DIplus
|
|
227
|
+
self.lines.minusDI = self.DIminus
|
|
228
|
+
# State for Wilder smoothing (used by next/prenext)
|
|
229
|
+
self._sm_tr = 0.0
|
|
230
|
+
self._sm_pdm = 0.0
|
|
231
|
+
self._sm_mdm = 0.0
|
|
232
|
+
self._bar_count = 0
|
|
233
|
+
|
|
234
|
+
def prenext(self):
|
|
235
|
+
"""Accumulate during warmup period."""
|
|
236
|
+
self._di_accumulate()
|
|
237
|
+
|
|
238
|
+
def nextstart(self):
|
|
239
|
+
"""Transition from warmup to live calculation."""
|
|
240
|
+
self._di_accumulate()
|
|
241
|
+
|
|
242
|
+
def next(self):
|
|
243
|
+
"""Calculate +DI and -DI for the current bar."""
|
|
244
|
+
self._di_accumulate()
|
|
245
|
+
|
|
246
|
+
def _di_accumulate(self):
|
|
247
|
+
"""Accumulate TR/DM and compute DI using Wilder smoothing."""
|
|
248
|
+
period = self.p.period
|
|
249
|
+
high = self.data.high[0]
|
|
250
|
+
low = self.data.low[0]
|
|
251
|
+
if len(self.data) <= 1:
|
|
252
|
+
return
|
|
253
|
+
try:
|
|
254
|
+
prev_high = self.data.high[-1]
|
|
255
|
+
prev_low = self.data.low[-1]
|
|
256
|
+
prev_close = self.data.close[-1]
|
|
257
|
+
except IndexError:
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
true_high = max(high, prev_close)
|
|
261
|
+
true_low = min(low, prev_close)
|
|
262
|
+
tr = true_high - true_low
|
|
263
|
+
|
|
264
|
+
upmove = high - prev_high
|
|
265
|
+
downmove = prev_low - low
|
|
266
|
+
pdm = upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
267
|
+
mdm = downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
268
|
+
|
|
269
|
+
self._bar_count += 1
|
|
270
|
+
|
|
271
|
+
if self._bar_count <= period:
|
|
272
|
+
# Accumulate sums for seeding
|
|
273
|
+
self._sm_tr += tr
|
|
274
|
+
self._sm_pdm += pdm
|
|
275
|
+
self._sm_mdm += mdm
|
|
276
|
+
else:
|
|
277
|
+
# Wilder smoothing: val = val - val/period + new
|
|
278
|
+
self._sm_tr = self._sm_tr - self._sm_tr / period + tr
|
|
279
|
+
self._sm_pdm = self._sm_pdm - self._sm_pdm / period + pdm
|
|
280
|
+
self._sm_mdm = self._sm_mdm - self._sm_mdm / period + mdm
|
|
281
|
+
|
|
282
|
+
if self._sm_tr > 0:
|
|
283
|
+
self.lines.plusDI[0] = 100.0 * self._sm_pdm / self._sm_tr
|
|
284
|
+
self.lines.minusDI[0] = 100.0 * self._sm_mdm / self._sm_tr
|
|
285
|
+
else:
|
|
286
|
+
self.lines.plusDI[0] = 0.0
|
|
287
|
+
self.lines.minusDI[0] = 0.0
|
|
288
|
+
|
|
289
|
+
def once(self, start, end):
|
|
290
|
+
"""Calculate DI values in runonce mode from raw OHLC."""
|
|
291
|
+
period = self.p.period
|
|
292
|
+
high_arr = self.data.high.array
|
|
293
|
+
low_arr = self.data.low.array
|
|
294
|
+
close_arr = self.data.close.array
|
|
295
|
+
dst_plus = self.lines.plusDI.array
|
|
296
|
+
dst_minus = self.lines.minusDI.array
|
|
297
|
+
|
|
298
|
+
while len(dst_plus) < end:
|
|
299
|
+
dst_plus.append(float("nan"))
|
|
300
|
+
while len(dst_minus) < end:
|
|
301
|
+
dst_minus.append(float("nan"))
|
|
302
|
+
|
|
303
|
+
sm_tr = 0.0
|
|
304
|
+
sm_pdm = 0.0
|
|
305
|
+
sm_mdm = 0.0
|
|
306
|
+
bar_count = 0
|
|
307
|
+
|
|
308
|
+
for i in range(1, min(end, len(high_arr), len(low_arr), len(close_arr))):
|
|
309
|
+
high = high_arr[i]
|
|
310
|
+
low = low_arr[i]
|
|
311
|
+
prev_high = high_arr[i - 1]
|
|
312
|
+
prev_low = low_arr[i - 1]
|
|
313
|
+
prev_close = close_arr[i - 1]
|
|
314
|
+
|
|
315
|
+
true_high = max(high, prev_close)
|
|
316
|
+
true_low = min(low, prev_close)
|
|
317
|
+
tr = true_high - true_low
|
|
318
|
+
|
|
319
|
+
upmove = high - prev_high
|
|
320
|
+
downmove = prev_low - low
|
|
321
|
+
pdm = upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
322
|
+
mdm = downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
323
|
+
|
|
324
|
+
bar_count += 1
|
|
325
|
+
if bar_count <= period:
|
|
326
|
+
sm_tr += tr
|
|
327
|
+
sm_pdm += pdm
|
|
328
|
+
sm_mdm += mdm
|
|
329
|
+
else:
|
|
330
|
+
sm_tr = sm_tr - sm_tr / period + tr
|
|
331
|
+
sm_pdm = sm_pdm - sm_pdm / period + pdm
|
|
332
|
+
sm_mdm = sm_mdm - sm_mdm / period + mdm
|
|
333
|
+
|
|
334
|
+
if sm_tr > 0:
|
|
335
|
+
dst_plus[i] = 100.0 * sm_pdm / sm_tr
|
|
336
|
+
dst_minus[i] = 100.0 * sm_mdm / sm_tr
|
|
337
|
+
else:
|
|
338
|
+
dst_plus[i] = 0.0
|
|
339
|
+
dst_minus[i] = 0.0
|
|
340
|
+
|
|
341
|
+
prenext = LineRoot.prenext # noqa: F811
|
|
342
|
+
nextstart = LineRoot.nextstart # noqa: F811
|
|
343
|
+
next = LineRoot.next # noqa: F811
|
|
344
|
+
preonce = LineRoot.preonce # noqa: F811
|
|
345
|
+
oncestart = LineRoot.oncestart # noqa: F811
|
|
346
|
+
once = LineRoot.once # noqa: F811
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class PlusDirectionalIndicator(_DirectionalIndicator):
|
|
350
|
+
"""
|
|
351
|
+
Defined by J. Welles Wilder, Jr. in 1978 in his book *"New Concepts in
|
|
352
|
+
Technical Trading Systems"*.
|
|
353
|
+
|
|
354
|
+
Intended to measure trend strength
|
|
355
|
+
|
|
356
|
+
This indicator shows +DI:
|
|
357
|
+
- Use MinusDirectionalIndicator (MinusDI) to get -DI
|
|
358
|
+
- Use Directional Indicator (DI) to get +DI, -DI
|
|
359
|
+
- Use AverageDirectionalIndex (ADX) to get ADX
|
|
360
|
+
- Use AverageDirectionalIndexRating (ADXR) to get ADX, ADXR
|
|
361
|
+
- Use DirectionalMovementIndex (DMI) to get ADX, +DI, -DI
|
|
362
|
+
- Use DirectionalMovement (DM) to get ADX, ADXR, +DI, -DI
|
|
363
|
+
|
|
364
|
+
Formula:
|
|
365
|
+
- upmove = high - high(-1)
|
|
366
|
+
- downmove = low(-1) - low
|
|
367
|
+
- +dm = upmove if upmove > downmove and upmove > 0 else 0
|
|
368
|
+
- +di = 100 * MovingAverage(+dm, period) / atr(period)
|
|
369
|
+
|
|
370
|
+
The moving average used is the one originally defined by Wilder,
|
|
371
|
+
the SmoothedMovingAverage
|
|
372
|
+
|
|
373
|
+
See:
|
|
374
|
+
- https://en.wikipedia.org/wiki/Average_directional_movement_index
|
|
375
|
+
"""
|
|
376
|
+
|
|
377
|
+
alias = (("PlusDI", "+DI"),)
|
|
378
|
+
lines = ("plusDI",)
|
|
379
|
+
|
|
380
|
+
plotinfo = {"plotname": "+DirectionalIndicator"}
|
|
381
|
+
|
|
382
|
+
def __init__(self):
|
|
383
|
+
"""Initialize the +DI indicator."""
|
|
384
|
+
super().__init__(_minus=False)
|
|
385
|
+
self.lines.plusDI = self.DIplus
|
|
386
|
+
self._sm_tr = 0.0
|
|
387
|
+
self._sm_pdm = 0.0
|
|
388
|
+
self._bar_count = 0
|
|
389
|
+
|
|
390
|
+
def _pdi_accumulate(self):
|
|
391
|
+
period = self.p.period
|
|
392
|
+
high = self.data.high[0]
|
|
393
|
+
low = self.data.low[0]
|
|
394
|
+
if len(self.data) <= 1:
|
|
395
|
+
return
|
|
396
|
+
try:
|
|
397
|
+
prev_high = self.data.high[-1]
|
|
398
|
+
prev_low = self.data.low[-1]
|
|
399
|
+
prev_close = self.data.close[-1]
|
|
400
|
+
except IndexError:
|
|
401
|
+
return
|
|
402
|
+
tr = max(high, prev_close) - min(low, prev_close)
|
|
403
|
+
upmove = high - prev_high
|
|
404
|
+
downmove = prev_low - low
|
|
405
|
+
pdm = upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
406
|
+
self._bar_count += 1
|
|
407
|
+
if self._bar_count <= period:
|
|
408
|
+
self._sm_tr += tr
|
|
409
|
+
self._sm_pdm += pdm
|
|
410
|
+
else:
|
|
411
|
+
self._sm_tr = self._sm_tr - self._sm_tr / period + tr
|
|
412
|
+
self._sm_pdm = self._sm_pdm - self._sm_pdm / period + pdm
|
|
413
|
+
self.lines.plusDI[0] = (100.0 * self._sm_pdm / self._sm_tr) if self._sm_tr > 0 else 0.0
|
|
414
|
+
|
|
415
|
+
def prenext(self):
|
|
416
|
+
"""Called before minimum period is met.
|
|
417
|
+
|
|
418
|
+
Accumulates +DI values during the warmup period.
|
|
419
|
+
"""
|
|
420
|
+
self._pdi_accumulate()
|
|
421
|
+
|
|
422
|
+
def nextstart(self):
|
|
423
|
+
"""Called once minimum period is first met.
|
|
424
|
+
|
|
425
|
+
Accumulates +DI values at the start of normal operation.
|
|
426
|
+
"""
|
|
427
|
+
self._pdi_accumulate()
|
|
428
|
+
|
|
429
|
+
def next(self):
|
|
430
|
+
"""Called on each bar after minimum period is met.
|
|
431
|
+
|
|
432
|
+
Accumulates +DI values for the current bar.
|
|
433
|
+
"""
|
|
434
|
+
self._pdi_accumulate()
|
|
435
|
+
|
|
436
|
+
def once(self, start, end):
|
|
437
|
+
"""Vectorized calculation of +DI values.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
start: Start index for calculation.
|
|
441
|
+
end: End index for calculation.
|
|
442
|
+
"""
|
|
443
|
+
period = self.p.period
|
|
444
|
+
high_arr = self.data.high.array
|
|
445
|
+
low_arr = self.data.low.array
|
|
446
|
+
close_arr = self.data.close.array
|
|
447
|
+
dst = self.lines.plusDI.array
|
|
448
|
+
while len(dst) < end:
|
|
449
|
+
dst.append(float("nan"))
|
|
450
|
+
sm_tr = 0.0
|
|
451
|
+
sm_pdm = 0.0
|
|
452
|
+
bc = 0
|
|
453
|
+
for i in range(1, min(end, len(high_arr), len(low_arr), len(close_arr))):
|
|
454
|
+
tr = max(high_arr[i], close_arr[i - 1]) - min(low_arr[i], close_arr[i - 1])
|
|
455
|
+
upmove = high_arr[i] - high_arr[i - 1]
|
|
456
|
+
downmove = low_arr[i - 1] - low_arr[i]
|
|
457
|
+
pdm = upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
458
|
+
bc += 1
|
|
459
|
+
if bc <= period:
|
|
460
|
+
sm_tr += tr
|
|
461
|
+
sm_pdm += pdm
|
|
462
|
+
else:
|
|
463
|
+
sm_tr = sm_tr - sm_tr / period + tr
|
|
464
|
+
sm_pdm = sm_pdm - sm_pdm / period + pdm
|
|
465
|
+
dst[i] = (100.0 * sm_pdm / sm_tr) if sm_tr > 0 else 0.0
|
|
466
|
+
|
|
467
|
+
prenext = LineRoot.prenext # noqa: F811
|
|
468
|
+
nextstart = LineRoot.nextstart # noqa: F811
|
|
469
|
+
next = LineRoot.next # noqa: F811
|
|
470
|
+
preonce = LineRoot.preonce # noqa: F811
|
|
471
|
+
oncestart = LineRoot.oncestart # noqa: F811
|
|
472
|
+
once = LineRoot.once # noqa: F811
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class MinusDirectionalIndicator(_DirectionalIndicator):
|
|
476
|
+
"""
|
|
477
|
+
Defined by J. Welles Wilder, Jr. in 1978 in his book *"New Concepts in
|
|
478
|
+
Technical Trading Systems"*.
|
|
479
|
+
|
|
480
|
+
Intended to measure trend strength
|
|
481
|
+
|
|
482
|
+
This indicator shows -DI:
|
|
483
|
+
- Use PlusDirectionalIndicator (PlusDI) to get +DI
|
|
484
|
+
- Use Directional Indicator (DI) to get +DI, -DI
|
|
485
|
+
- Use AverageDirectionalIndex (ADX) to get ADX
|
|
486
|
+
- Use AverageDirectionalIndexRating (ADXR) to get ADX, ADXR
|
|
487
|
+
- Use DirectionalMovementIndex (DMI) to get ADX, +DI, -DI
|
|
488
|
+
- Use DirectionalMovement (DM) to get ADX, ADXR, +DI, -DI
|
|
489
|
+
|
|
490
|
+
Formula:
|
|
491
|
+
- upmove = high - high(-1)
|
|
492
|
+
- downmove = low(-1) - low
|
|
493
|
+
- -dm = downmove if downmove > upmove and downmove > 0 else 0
|
|
494
|
+
- -di = 100 * MovingAverage(-dm, period) / atr(period)
|
|
495
|
+
|
|
496
|
+
The moving average used is the one originally defined by Wilder,
|
|
497
|
+
the SmoothedMovingAverage
|
|
498
|
+
|
|
499
|
+
See:
|
|
500
|
+
- https://en.wikipedia.org/wiki/Average_directional_movement_index
|
|
501
|
+
"""
|
|
502
|
+
|
|
503
|
+
alias = (("MinusDI", "-DI"),)
|
|
504
|
+
lines = ("minusDI",)
|
|
505
|
+
|
|
506
|
+
plotinfo = {"plotname": "-DirectionalIndicator"}
|
|
507
|
+
|
|
508
|
+
def __init__(self):
|
|
509
|
+
"""Initialize the -DI indicator."""
|
|
510
|
+
super().__init__(_plus=False)
|
|
511
|
+
self.lines.minusDI = self.DIminus
|
|
512
|
+
self._sm_tr = 0.0
|
|
513
|
+
self._sm_mdm = 0.0
|
|
514
|
+
self._bar_count = 0
|
|
515
|
+
|
|
516
|
+
def _mdi_accumulate(self):
|
|
517
|
+
period = self.p.period
|
|
518
|
+
high = self.data.high[0]
|
|
519
|
+
low = self.data.low[0]
|
|
520
|
+
if len(self.data) <= 1:
|
|
521
|
+
return
|
|
522
|
+
try:
|
|
523
|
+
prev_high = self.data.high[-1]
|
|
524
|
+
prev_low = self.data.low[-1]
|
|
525
|
+
prev_close = self.data.close[-1]
|
|
526
|
+
except IndexError:
|
|
527
|
+
return
|
|
528
|
+
tr = max(high, prev_close) - min(low, prev_close)
|
|
529
|
+
upmove = high - prev_high
|
|
530
|
+
downmove = prev_low - low
|
|
531
|
+
mdm = downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
532
|
+
self._bar_count += 1
|
|
533
|
+
if self._bar_count <= period:
|
|
534
|
+
self._sm_tr += tr
|
|
535
|
+
self._sm_mdm += mdm
|
|
536
|
+
else:
|
|
537
|
+
self._sm_tr = self._sm_tr - self._sm_tr / period + tr
|
|
538
|
+
self._sm_mdm = self._sm_mdm - self._sm_mdm / period + mdm
|
|
539
|
+
self.lines.minusDI[0] = (100.0 * self._sm_mdm / self._sm_tr) if self._sm_tr > 0 else 0.0
|
|
540
|
+
|
|
541
|
+
def prenext(self):
|
|
542
|
+
"""Called before minimum period is met.
|
|
543
|
+
|
|
544
|
+
Accumulates -DI values during the warmup period.
|
|
545
|
+
"""
|
|
546
|
+
self._mdi_accumulate()
|
|
547
|
+
|
|
548
|
+
def nextstart(self):
|
|
549
|
+
"""Called once minimum period is first met.
|
|
550
|
+
|
|
551
|
+
Accumulates -DI values at the start of normal operation.
|
|
552
|
+
"""
|
|
553
|
+
self._mdi_accumulate()
|
|
554
|
+
|
|
555
|
+
def next(self):
|
|
556
|
+
"""Called on each bar after minimum period is met.
|
|
557
|
+
|
|
558
|
+
Accumulates -DI values for the current bar.
|
|
559
|
+
"""
|
|
560
|
+
self._mdi_accumulate()
|
|
561
|
+
|
|
562
|
+
def once(self, start, end):
|
|
563
|
+
"""Vectorized calculation of -DI values.
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
start: Start index for calculation.
|
|
567
|
+
end: End index for calculation.
|
|
568
|
+
"""
|
|
569
|
+
period = self.p.period
|
|
570
|
+
high_arr = self.data.high.array
|
|
571
|
+
low_arr = self.data.low.array
|
|
572
|
+
close_arr = self.data.close.array
|
|
573
|
+
dst = self.lines.minusDI.array
|
|
574
|
+
while len(dst) < end:
|
|
575
|
+
dst.append(float("nan"))
|
|
576
|
+
sm_tr = 0.0
|
|
577
|
+
sm_mdm = 0.0
|
|
578
|
+
bc = 0
|
|
579
|
+
for i in range(1, min(end, len(high_arr), len(low_arr), len(close_arr))):
|
|
580
|
+
tr = max(high_arr[i], close_arr[i - 1]) - min(low_arr[i], close_arr[i - 1])
|
|
581
|
+
upmove = high_arr[i] - high_arr[i - 1]
|
|
582
|
+
downmove = low_arr[i - 1] - low_arr[i]
|
|
583
|
+
mdm = downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
584
|
+
bc += 1
|
|
585
|
+
if bc <= period:
|
|
586
|
+
sm_tr += tr
|
|
587
|
+
sm_mdm += mdm
|
|
588
|
+
else:
|
|
589
|
+
sm_tr = sm_tr - sm_tr / period + tr
|
|
590
|
+
sm_mdm = sm_mdm - sm_mdm / period + mdm
|
|
591
|
+
dst[i] = (100.0 * sm_mdm / sm_tr) if sm_tr > 0 else 0.0
|
|
592
|
+
|
|
593
|
+
prenext = LineRoot.prenext # noqa: F811
|
|
594
|
+
nextstart = LineRoot.nextstart # noqa: F811
|
|
595
|
+
next = LineRoot.next # noqa: F811
|
|
596
|
+
preonce = LineRoot.preonce # noqa: F811
|
|
597
|
+
oncestart = LineRoot.oncestart # noqa: F811
|
|
598
|
+
once = LineRoot.once # noqa: F811
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
class AverageDirectionalMovementIndex(Indicator):
|
|
602
|
+
"""
|
|
603
|
+
Defined by J. Welles Wilder, Jr. in 1978 in his book *"New Concepts in
|
|
604
|
+
Technical Trading Systems"*.
|
|
605
|
+
|
|
606
|
+
Intended to measure trend strength. Rewritten following MACD pattern
|
|
607
|
+
with explicit next()/once() methods for reliable calculation.
|
|
608
|
+
|
|
609
|
+
Formula:
|
|
610
|
+
- upmove = high - high(-1)
|
|
611
|
+
- downmove = low(-1) - low
|
|
612
|
+
- +dm = upmove if upmove > downmove and upmove > 0 else 0
|
|
613
|
+
- -dm = downmove if downmove > upmove and downmove > 0 else 0
|
|
614
|
+
- +di = 100 * MovingAverage(+dm, period) / atr(period)
|
|
615
|
+
- -di = 100 * MovingAverage(-dm, period) / atr(period)
|
|
616
|
+
- dx = 100 * abs(+di - -di) / (+di + -di)
|
|
617
|
+
- adx = MovingAverage(dx, period)
|
|
618
|
+
"""
|
|
619
|
+
|
|
620
|
+
alias = ("ADX",)
|
|
621
|
+
lines = ("adx",)
|
|
622
|
+
params = (("period", 14), ("movav", MovAv.Smoothed))
|
|
623
|
+
plotlines = {"adx": {"_name": "ADX"}}
|
|
624
|
+
|
|
625
|
+
def __init__(self):
|
|
626
|
+
"""Initialize the ADX indicator.
|
|
627
|
+
|
|
628
|
+
Sets up ATR, DM smoothing, and state for ADX calculation.
|
|
629
|
+
"""
|
|
630
|
+
super().__init__()
|
|
631
|
+
period = self.p.period
|
|
632
|
+
|
|
633
|
+
atr = ATR(self.data, period=period, movav=self.p.movav)
|
|
634
|
+
upmove = self.data.high - self.data.high(-1)
|
|
635
|
+
downmove = self.data.low(-1) - self.data.low
|
|
636
|
+
|
|
637
|
+
plus = And(upmove > downmove, upmove > 0.0)
|
|
638
|
+
plusDM = If(plus, upmove, 0.0)
|
|
639
|
+
plusDMav = self.p.movav(plusDM, period=period)
|
|
640
|
+
self.DIplus = 100.0 * plusDMav / atr
|
|
641
|
+
|
|
642
|
+
minus = And(downmove > upmove, downmove > 0.0)
|
|
643
|
+
minusDM = If(minus, downmove, 0.0)
|
|
644
|
+
minusDMav = self.p.movav(minusDM, period=period)
|
|
645
|
+
self.DIminus = 100.0 * minusDMav / atr
|
|
646
|
+
|
|
647
|
+
dx = abs(self.DIplus - self.DIminus) / (self.DIplus + self.DIminus)
|
|
648
|
+
self.lines.adx = 100.0 * self.p.movav(dx, period=period)
|
|
649
|
+
|
|
650
|
+
# Store sub-indicators for direct array access (like MACD)
|
|
651
|
+
self.atr = ATR(self.data, period=period, movav=self.p.movav)
|
|
652
|
+
self.plusDMav = self.p.movav(period=period)
|
|
653
|
+
self.minusDMav = self.p.movav(period=period)
|
|
654
|
+
|
|
655
|
+
# Calculate minperiod: ATR needs period, then DI smoothing needs period, then ADX smoothing needs period
|
|
656
|
+
# Total: approximately 2*period for DI + period for ADX smoothing
|
|
657
|
+
adx_minperiod = 2 * period
|
|
658
|
+
self._minperiod = max(self._minperiod, adx_minperiod)
|
|
659
|
+
|
|
660
|
+
# Propagate minperiod to lines
|
|
661
|
+
for line in self.lines:
|
|
662
|
+
line.updateminperiod(self._minperiod)
|
|
663
|
+
|
|
664
|
+
# For SMMA calculation
|
|
665
|
+
self.alpha = 1.0 / period
|
|
666
|
+
self.alpha1 = 1.0 - self.alpha
|
|
667
|
+
|
|
668
|
+
# State for smoothed values
|
|
669
|
+
self._plusDMav_val = 0.0
|
|
670
|
+
self._minusDMav_val = 0.0
|
|
671
|
+
self._adx_val = 0.0
|
|
672
|
+
self._adx_bootstrapped = False
|
|
673
|
+
self._last_adx_idx = None
|
|
674
|
+
|
|
675
|
+
def prenext(self):
|
|
676
|
+
"""Track previous high/low during warmup.
|
|
677
|
+
|
|
678
|
+
Stores high and low values for directional move calculation.
|
|
679
|
+
"""
|
|
680
|
+
|
|
681
|
+
def _adx_atr_value(self, atr_array, index):
|
|
682
|
+
import math
|
|
683
|
+
|
|
684
|
+
atr_val = atr_array[index]
|
|
685
|
+
if atr_val == 0 or (isinstance(atr_val, float) and math.isnan(atr_val)):
|
|
686
|
+
return 0.0001
|
|
687
|
+
return atr_val
|
|
688
|
+
|
|
689
|
+
def _adx_dx(self, plus_dmav, minus_dmav, atr_val):
|
|
690
|
+
diplus = 100.0 * plus_dmav / atr_val
|
|
691
|
+
diminus = 100.0 * minus_dmav / atr_val
|
|
692
|
+
disum = diplus + diminus
|
|
693
|
+
return 100.0 * abs(diplus - diminus) / disum if disum != 0 else 0.0
|
|
694
|
+
|
|
695
|
+
def _adx_bootstrap(self, target_idx):
|
|
696
|
+
high_array = self.data.high.array
|
|
697
|
+
low_array = self.data.low.array
|
|
698
|
+
atr_array = self.atr.lines[0].array
|
|
699
|
+
|
|
700
|
+
period = self.p.period
|
|
701
|
+
seed_idx = 2 * period - 1
|
|
702
|
+
data_len = min(len(high_array), len(low_array), len(atr_array))
|
|
703
|
+
if target_idx < seed_idx or data_len <= seed_idx:
|
|
704
|
+
return False
|
|
705
|
+
|
|
706
|
+
dm_plus_sum = 0.0
|
|
707
|
+
dm_minus_sum = 0.0
|
|
708
|
+
for i in range(1, period + 1):
|
|
709
|
+
upmove = high_array[i] - high_array[i - 1]
|
|
710
|
+
downmove = low_array[i - 1] - low_array[i]
|
|
711
|
+
dm_plus_sum += upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
712
|
+
dm_minus_sum += downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
713
|
+
|
|
714
|
+
plus_dmav = dm_plus_sum / period
|
|
715
|
+
minus_dmav = dm_minus_sum / period
|
|
716
|
+
dx_values = [self._adx_dx(plus_dmav, minus_dmav, self._adx_atr_value(atr_array, period))]
|
|
717
|
+
|
|
718
|
+
for i in range(period + 1, seed_idx + 1):
|
|
719
|
+
upmove = high_array[i] - high_array[i - 1]
|
|
720
|
+
downmove = low_array[i - 1] - low_array[i]
|
|
721
|
+
plus_dm = upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
722
|
+
minus_dm = downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
723
|
+
plus_dmav = plus_dmav * self.alpha1 + plus_dm * self.alpha
|
|
724
|
+
minus_dmav = minus_dmav * self.alpha1 + minus_dm * self.alpha
|
|
725
|
+
dx_values.append(self._adx_dx(plus_dmav, minus_dmav, self._adx_atr_value(atr_array, i)))
|
|
726
|
+
|
|
727
|
+
self._plusDMav_val = plus_dmav
|
|
728
|
+
self._minusDMav_val = minus_dmav
|
|
729
|
+
self._adx_val = sum(dx_values[:period]) / period
|
|
730
|
+
self._last_adx_idx = seed_idx
|
|
731
|
+
self._adx_bootstrapped = True
|
|
732
|
+
return True
|
|
733
|
+
|
|
734
|
+
def _adx_advance_to(self, target_idx):
|
|
735
|
+
if not self._adx_bootstrapped and not self._adx_bootstrap(target_idx):
|
|
736
|
+
return False
|
|
737
|
+
|
|
738
|
+
high_array = self.data.high.array
|
|
739
|
+
low_array = self.data.low.array
|
|
740
|
+
atr_array = self.atr.lines[0].array
|
|
741
|
+
data_len = min(len(high_array), len(low_array), len(atr_array))
|
|
742
|
+
if target_idx >= data_len:
|
|
743
|
+
return False
|
|
744
|
+
|
|
745
|
+
for i in range(self._last_adx_idx + 1, target_idx + 1):
|
|
746
|
+
upmove = high_array[i] - high_array[i - 1]
|
|
747
|
+
downmove = low_array[i - 1] - low_array[i]
|
|
748
|
+
plus_dm = upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
749
|
+
minus_dm = downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
750
|
+
self._plusDMav_val = self._plusDMav_val * self.alpha1 + plus_dm * self.alpha
|
|
751
|
+
self._minusDMav_val = self._minusDMav_val * self.alpha1 + minus_dm * self.alpha
|
|
752
|
+
dx = self._adx_dx(
|
|
753
|
+
self._plusDMav_val,
|
|
754
|
+
self._minusDMav_val,
|
|
755
|
+
self._adx_atr_value(atr_array, i),
|
|
756
|
+
)
|
|
757
|
+
self._adx_val = self._adx_val * self.alpha1 + dx * self.alpha
|
|
758
|
+
self._last_adx_idx = i
|
|
759
|
+
|
|
760
|
+
return True
|
|
761
|
+
|
|
762
|
+
def nextstart(self):
|
|
763
|
+
"""Seed ADX calculation on first valid bar.
|
|
764
|
+
|
|
765
|
+
Calculates initial DM, DI, DX, and ADX values.
|
|
766
|
+
"""
|
|
767
|
+
idx = self.lines[0].idx
|
|
768
|
+
if self._adx_advance_to(idx):
|
|
769
|
+
self.lines.adx[0] = self._adx_val
|
|
770
|
+
else:
|
|
771
|
+
self.lines.adx[0] = float("nan")
|
|
772
|
+
|
|
773
|
+
def next(self):
|
|
774
|
+
"""Calculate ADX for the current bar.
|
|
775
|
+
|
|
776
|
+
Calculates DM smoothing, DI, DX, and ADX values.
|
|
777
|
+
"""
|
|
778
|
+
idx = self.lines[0].idx
|
|
779
|
+
if self._adx_advance_to(idx):
|
|
780
|
+
self.lines.adx[0] = self._adx_val
|
|
781
|
+
else:
|
|
782
|
+
self.lines.adx[0] = float("nan")
|
|
783
|
+
|
|
784
|
+
def once(self, start, end):
|
|
785
|
+
"""Calculate ADX in runonce mode using proper Wilder seeding.
|
|
786
|
+
|
|
787
|
+
Implements the standard Wilder ADX algorithm:
|
|
788
|
+
Phase 1: Seed smoothed DM with SMA of first N DM values (bars 1..period)
|
|
789
|
+
Phase 2: Continue SMMA for DM, accumulate DX (bars period..2*period-1)
|
|
790
|
+
Phase 3: Seed ADX with SMA of first N DX values
|
|
791
|
+
Phase 4: Continue SMMA for all components (bars 2*period..end)
|
|
792
|
+
"""
|
|
793
|
+
import math
|
|
794
|
+
|
|
795
|
+
# Get source arrays
|
|
796
|
+
high_array = self.data.high.array
|
|
797
|
+
low_array = self.data.low.array
|
|
798
|
+
atr_array = self.atr.lines[0].array
|
|
799
|
+
adx_array = self.lines.adx.array
|
|
800
|
+
|
|
801
|
+
period = self.p.period
|
|
802
|
+
alpha = self.alpha
|
|
803
|
+
alpha1 = self.alpha1
|
|
804
|
+
|
|
805
|
+
# Ensure arrays are sized
|
|
806
|
+
while len(adx_array) < end:
|
|
807
|
+
adx_array.append(float("nan"))
|
|
808
|
+
|
|
809
|
+
data_len = min(end, len(high_array), len(low_array), len(atr_array))
|
|
810
|
+
|
|
811
|
+
# Pre-fill warmup with NaN
|
|
812
|
+
for i in range(min(2 * period, data_len)):
|
|
813
|
+
adx_array[i] = float("nan")
|
|
814
|
+
|
|
815
|
+
# Need at least 2*period + 1 bars for proper seeding
|
|
816
|
+
if data_len <= 2 * period:
|
|
817
|
+
return
|
|
818
|
+
|
|
819
|
+
# Phase 1: Calculate raw DM values for bars 1..period
|
|
820
|
+
# Seed smoothed DM with SMA (average) of first period values
|
|
821
|
+
dm_plus_sum = 0.0
|
|
822
|
+
dm_minus_sum = 0.0
|
|
823
|
+
|
|
824
|
+
for i in range(1, period + 1):
|
|
825
|
+
upmove = high_array[i] - high_array[i - 1]
|
|
826
|
+
downmove = low_array[i - 1] - low_array[i]
|
|
827
|
+
plusDM = upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
828
|
+
minusDM = downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
829
|
+
dm_plus_sum += plusDM
|
|
830
|
+
dm_minus_sum += minusDM
|
|
831
|
+
|
|
832
|
+
plusDMav = dm_plus_sum / period
|
|
833
|
+
minusDMav = dm_minus_sum / period
|
|
834
|
+
|
|
835
|
+
# Phase 2: Calculate DI and DX for bars period..2*period-1
|
|
836
|
+
# Continue SMMA for DM, accumulate DX values for ADX seeding
|
|
837
|
+
dx_list = []
|
|
838
|
+
|
|
839
|
+
# First DX at bar period (using seeded DM averages)
|
|
840
|
+
atr_val = atr_array[period]
|
|
841
|
+
if atr_val == 0 or (isinstance(atr_val, float) and math.isnan(atr_val)):
|
|
842
|
+
atr_val = 0.0001
|
|
843
|
+
diplus = 100.0 * plusDMav / atr_val
|
|
844
|
+
diminus = 100.0 * minusDMav / atr_val
|
|
845
|
+
disum = diplus + diminus
|
|
846
|
+
dx = 100.0 * abs(diplus - diminus) / disum if disum != 0 else 0.0
|
|
847
|
+
dx_list.append(dx)
|
|
848
|
+
|
|
849
|
+
# Continue for bars period+1 to 2*period-1
|
|
850
|
+
for i in range(period + 1, 2 * period):
|
|
851
|
+
upmove = high_array[i] - high_array[i - 1]
|
|
852
|
+
downmove = low_array[i - 1] - low_array[i]
|
|
853
|
+
plusDM = upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
854
|
+
minusDM = downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
855
|
+
|
|
856
|
+
plusDMav = plusDMav * alpha1 + plusDM * alpha
|
|
857
|
+
minusDMav = minusDMav * alpha1 + minusDM * alpha
|
|
858
|
+
|
|
859
|
+
atr_val = atr_array[i]
|
|
860
|
+
if atr_val == 0 or (isinstance(atr_val, float) and math.isnan(atr_val)):
|
|
861
|
+
atr_val = 0.0001
|
|
862
|
+
|
|
863
|
+
diplus = 100.0 * plusDMav / atr_val
|
|
864
|
+
diminus = 100.0 * minusDMav / atr_val
|
|
865
|
+
disum = diplus + diminus
|
|
866
|
+
dx = 100.0 * abs(diplus - diminus) / disum if disum != 0 else 0.0
|
|
867
|
+
dx_list.append(dx)
|
|
868
|
+
|
|
869
|
+
# Phase 3: Seed ADX with SMA of first period DX values
|
|
870
|
+
adx_val = sum(dx_list[:period]) / period
|
|
871
|
+
adx_array[2 * period - 1] = adx_val
|
|
872
|
+
|
|
873
|
+
# Phase 4: Continue SMMA for everything from 2*period onwards
|
|
874
|
+
for i in range(2 * period, data_len):
|
|
875
|
+
upmove = high_array[i] - high_array[i - 1]
|
|
876
|
+
downmove = low_array[i - 1] - low_array[i]
|
|
877
|
+
plusDM = upmove if (upmove > downmove and upmove > 0) else 0.0
|
|
878
|
+
minusDM = downmove if (downmove > upmove and downmove > 0) else 0.0
|
|
879
|
+
|
|
880
|
+
plusDMav = plusDMav * alpha1 + plusDM * alpha
|
|
881
|
+
minusDMav = minusDMav * alpha1 + minusDM * alpha
|
|
882
|
+
|
|
883
|
+
atr_val = atr_array[i]
|
|
884
|
+
if atr_val == 0 or (isinstance(atr_val, float) and math.isnan(atr_val)):
|
|
885
|
+
atr_val = 0.0001
|
|
886
|
+
|
|
887
|
+
diplus = 100.0 * plusDMav / atr_val
|
|
888
|
+
diminus = 100.0 * minusDMav / atr_val
|
|
889
|
+
disum = diplus + diminus
|
|
890
|
+
dx = 100.0 * abs(diplus - diminus) / disum if disum != 0 else 0.0
|
|
891
|
+
|
|
892
|
+
adx_val = adx_val * alpha1 + dx * alpha
|
|
893
|
+
adx_array[i] = adx_val
|
|
894
|
+
|
|
895
|
+
prenext = LineRoot.prenext # noqa: F811
|
|
896
|
+
nextstart = LineRoot.nextstart # noqa: F811
|
|
897
|
+
next = LineRoot.next # noqa: F811
|
|
898
|
+
preonce = LineRoot.preonce # noqa: F811
|
|
899
|
+
oncestart = LineRoot.oncestart # noqa: F811
|
|
900
|
+
once = LineRoot.once # noqa: F811
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
class AverageDirectionalMovementIndexRating(AverageDirectionalMovementIndex):
|
|
904
|
+
"""
|
|
905
|
+
Defined by J. Welles Wilder, Jr. in 1978 in his book *"New Concepts in
|
|
906
|
+
Technical Trading Systems"*.
|
|
907
|
+
|
|
908
|
+
Intended to measure trend strength.
|
|
909
|
+
|
|
910
|
+
ADXR is the average of ADX with a value period bars ago
|
|
911
|
+
|
|
912
|
+
This indicator shows the ADX and ADXR:
|
|
913
|
+
- Use PlusDirectionalIndicator (PlusDI) to get +DI
|
|
914
|
+
- Use MinusDirectionalIndicator (MinusDI) to get -DI
|
|
915
|
+
- Use Directional Indicator (DI) to get +DI, -DI
|
|
916
|
+
- Use AverageDirectionalIndex (ADX) to get ADX
|
|
917
|
+
- Use DirectionalMovementIndex (DMI) to get ADX, +DI, -DI
|
|
918
|
+
- Use DirectionalMovement (DM) to get ADX, ADXR, +DI, -DI
|
|
919
|
+
|
|
920
|
+
Formula:
|
|
921
|
+
- upmove = high - high(-1)
|
|
922
|
+
- downmove = low(-1) - low
|
|
923
|
+
- +dm = upmove if upmove > downmove and upmove > 0 else 0
|
|
924
|
+
- -dm = downmove if downmove > upmove and downmove > 0 else 0
|
|
925
|
+
- +di = 100 * MovingAverage(+dm, period) / atr(period)
|
|
926
|
+
- -di = 100 * MovingAverage(-dm, period) / atr(period)
|
|
927
|
+
- dx = 100 * abs(+di - -di) / (+di + -di)
|
|
928
|
+
- adx = MovingAverage(dx, period)
|
|
929
|
+
- adxr = (adx + adx(-period)) / 2
|
|
930
|
+
|
|
931
|
+
The moving average used is the one originally defined by Wilder,
|
|
932
|
+
the SmoothedMovingAverage
|
|
933
|
+
|
|
934
|
+
See:
|
|
935
|
+
- https://en.wikipedia.org/wiki/Average_directional_movement_index
|
|
936
|
+
"""
|
|
937
|
+
|
|
938
|
+
alias = ("ADXR",)
|
|
939
|
+
|
|
940
|
+
lines = ("adxr",)
|
|
941
|
+
plotlines = {"adxr": {"_name": "ADXR"}}
|
|
942
|
+
|
|
943
|
+
def __init__(self):
|
|
944
|
+
"""Initialize the ADXR indicator.
|
|
945
|
+
|
|
946
|
+
Extends ADX with rating line.
|
|
947
|
+
"""
|
|
948
|
+
super().__init__()
|
|
949
|
+
self.lines.adxr = (self.l.adx + self.l.adx(-self.p.period)) / 2.0
|
|
950
|
+
|
|
951
|
+
def next(self):
|
|
952
|
+
"""Calculate ADX and ADXR for the current bar.
|
|
953
|
+
|
|
954
|
+
ADXR = (ADX + ADX(-period)) / 2
|
|
955
|
+
"""
|
|
956
|
+
super().next()
|
|
957
|
+
self.lines.adxr[0] = (self.lines.adx[0] + self.lines.adx[-self.p.period]) / 2.0
|
|
958
|
+
|
|
959
|
+
def once(self, start, end):
|
|
960
|
+
"""Calculate ADXR in runonce mode.
|
|
961
|
+
|
|
962
|
+
Computes ADXR as average of current ADX and ADX from period ago.
|
|
963
|
+
"""
|
|
964
|
+
super().once(start, end)
|
|
965
|
+
import math
|
|
966
|
+
|
|
967
|
+
adx_array = self.lines.adx.array
|
|
968
|
+
adxr_array = self.lines.adxr.array
|
|
969
|
+
period = self.p.period
|
|
970
|
+
|
|
971
|
+
while len(adxr_array) < end:
|
|
972
|
+
adxr_array.append(float("nan"))
|
|
973
|
+
|
|
974
|
+
for i in range(start, min(end, len(adx_array))):
|
|
975
|
+
if i >= period:
|
|
976
|
+
adx_curr = adx_array[i] if i < len(adx_array) else 0.0
|
|
977
|
+
adx_prev = (
|
|
978
|
+
adx_array[i - period]
|
|
979
|
+
if i - period >= 0 and i - period < len(adx_array)
|
|
980
|
+
else 0.0
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
if (
|
|
984
|
+
isinstance(adx_curr, float)
|
|
985
|
+
and math.isnan(adx_curr)
|
|
986
|
+
or isinstance(adx_prev, float)
|
|
987
|
+
and math.isnan(adx_prev)
|
|
988
|
+
):
|
|
989
|
+
adxr_array[i] = float("nan")
|
|
990
|
+
else:
|
|
991
|
+
adxr_array[i] = (adx_curr + adx_prev) / 2.0
|
|
992
|
+
else:
|
|
993
|
+
adxr_array[i] = float("nan")
|
|
994
|
+
|
|
995
|
+
prenext = LineRoot.prenext # noqa: F811
|
|
996
|
+
nextstart = LineRoot.nextstart # noqa: F811
|
|
997
|
+
next = LineRoot.next # noqa: F811
|
|
998
|
+
preonce = LineRoot.preonce # noqa: F811
|
|
999
|
+
oncestart = LineRoot.oncestart # noqa: F811
|
|
1000
|
+
once = LineRoot.once # noqa: F811
|
|
1001
|
+
|
|
1002
|
+
|
|
1003
|
+
class DirectionalMovementIndex(AverageDirectionalMovementIndex, DirectionalIndicator):
|
|
1004
|
+
"""
|
|
1005
|
+
Defined by J. Welles Wilder, Jr. in 1978 in his book *"New Concepts in
|
|
1006
|
+
Technical Trading Systems"*.
|
|
1007
|
+
|
|
1008
|
+
Intended to measure trend strength
|
|
1009
|
+
|
|
1010
|
+
This indicator shows the ADX, +DI, -DI:
|
|
1011
|
+
- Use PlusDirectionalIndicator (PlusDI) to get +DI
|
|
1012
|
+
- Use MinusDirectionalIndicator (MinusDI) to get -DI
|
|
1013
|
+
- Use Directional Indicator (DI) to get +DI, -DI
|
|
1014
|
+
- Use AverageDirectionalIndex (ADX) to get ADX
|
|
1015
|
+
- Use AverageDirectionalIndexRating (ADXRating) to get ADX, ADXR
|
|
1016
|
+
- Use DirectionalMovement (DM) to get ADX, ADXR, +DI, -DI
|
|
1017
|
+
|
|
1018
|
+
Formula:
|
|
1019
|
+
- upmove = high - high(-1)
|
|
1020
|
+
- downmove = low(-1) - low
|
|
1021
|
+
- +dm = upmove if upmove > downmove and upmove > 0 else 0
|
|
1022
|
+
- -dm = downmove if downmove > upmove and downmove > 0 else 0
|
|
1023
|
+
- +di = 100 * MovingAverage(+dm, period) / atr(period)
|
|
1024
|
+
- -di = 100 * MovingAverage(-dm, period) / atr(period)
|
|
1025
|
+
- dx = 100 * abs(+di - -di) / (+di + -di)
|
|
1026
|
+
- adx = MovingAverage(dx, period)
|
|
1027
|
+
|
|
1028
|
+
The moving average used is the one originally defined by Wilder,
|
|
1029
|
+
the SmoothedMovingAverage
|
|
1030
|
+
|
|
1031
|
+
See:
|
|
1032
|
+
- https://en.wikipedia.org/wiki/Average_directional_movement_index
|
|
1033
|
+
"""
|
|
1034
|
+
|
|
1035
|
+
alias = ("DMI",)
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
class DirectionalMovement(AverageDirectionalMovementIndexRating, DirectionalIndicator):
|
|
1039
|
+
"""
|
|
1040
|
+
Defined by J. Welles Wilder, Jr. in 1978 in his book *"New Concepts in
|
|
1041
|
+
Technical Trading Systems"*.
|
|
1042
|
+
|
|
1043
|
+
Intended to measure trend strength
|
|
1044
|
+
|
|
1045
|
+
This indicator shows ADX, ADXR, +DI, -DI.
|
|
1046
|
+
|
|
1047
|
+
- Use PlusDirectionalIndicator (PlusDI) to get +DI
|
|
1048
|
+
- Use MinusDirectionalIndicator (MinusDI) to get -DI
|
|
1049
|
+
- Use Directional Indicator (DI) to get +DI, -DI
|
|
1050
|
+
- Use AverageDirectionalIndex (ADX) to get ADX
|
|
1051
|
+
- Use AverageDirectionalIndexRating (ADXR) to get ADX, ADXR
|
|
1052
|
+
- Use DirectionalMovementIndex (DMI) to get ADX, +DI, -DI
|
|
1053
|
+
|
|
1054
|
+
Formula:
|
|
1055
|
+
- upmove = high - high(-1)
|
|
1056
|
+
- downmove = low(-1) - low
|
|
1057
|
+
- +dm = upmove if upmove > downmove and upmove > 0 else 0
|
|
1058
|
+
- -dm = downmove if downmove > upmove and downmove > 0 else 0
|
|
1059
|
+
- +di = 100 * MovingAverage(+dm, period) / atr(period)
|
|
1060
|
+
- -di = 100 * MovingAverage(-dm, period) / atr(period)
|
|
1061
|
+
- dx = 100 * abs(+di - -di) / (+di + -di)
|
|
1062
|
+
- adx = MovingAverage(dx, period)
|
|
1063
|
+
|
|
1064
|
+
The moving average used is the one originally defined by Wilder,
|
|
1065
|
+
the SmoothedMovingAverage
|
|
1066
|
+
|
|
1067
|
+
See:
|
|
1068
|
+
- https://en.wikipedia.org/wiki/Average_directional_movement_index
|
|
1069
|
+
"""
|
|
1070
|
+
|
|
1071
|
+
alias = ("DM",)
|