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
backtrader/functions.py
ADDED
|
@@ -0,0 +1,960 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Functions Module - Common operations on line objects.
|
|
3
|
+
|
|
4
|
+
This module provides utility functions and classes for performing
|
|
5
|
+
operations on line objects. It includes arithmetic operations with
|
|
6
|
+
zero-division protection, logical operations, comparison operations,
|
|
7
|
+
and mathematical functions.
|
|
8
|
+
|
|
9
|
+
Classes:
|
|
10
|
+
Logic: Base class for logical operations on lines.
|
|
11
|
+
DivByZero: Division with zero-division protection.
|
|
12
|
+
DivZeroByZero: Division with zero/zero indetermination protection.
|
|
13
|
+
And/Or/Not/If/Max/Min/MinN/MaxN: Logical and comparison operations.
|
|
14
|
+
Sum/Average/StdDev/TSMean: Statistical operations.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
Using indicator functions:
|
|
18
|
+
>>> from backtrader.functions import And, Or
|
|
19
|
+
>>> condition = And(indicator1 > indicator2, indicator3 > 0)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import functools
|
|
23
|
+
import math
|
|
24
|
+
|
|
25
|
+
from .linebuffer import LineActions
|
|
26
|
+
from .utils.log_message import get_logger, throttled_warning
|
|
27
|
+
from .utils.py3 import cmp, range
|
|
28
|
+
|
|
29
|
+
logger = get_logger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _sanitize_cmp_value(value):
|
|
33
|
+
if value is None:
|
|
34
|
+
return 0.0
|
|
35
|
+
|
|
36
|
+
if isinstance(value, float) and not math.isfinite(value):
|
|
37
|
+
return 0.0
|
|
38
|
+
|
|
39
|
+
return value
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _sanitize_div_value(value):
|
|
43
|
+
if value is None:
|
|
44
|
+
return 0.0
|
|
45
|
+
|
|
46
|
+
if isinstance(value, float) and not math.isfinite(value):
|
|
47
|
+
return 0.0
|
|
48
|
+
|
|
49
|
+
return value
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _sanitize_numeric_values(values):
|
|
53
|
+
return [_sanitize_div_value(value) for value in values]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _value_at(array, index, default=0.0):
|
|
57
|
+
try:
|
|
58
|
+
return array[index]
|
|
59
|
+
except (IndexError, TypeError):
|
|
60
|
+
try:
|
|
61
|
+
return array[-1]
|
|
62
|
+
except (IndexError, TypeError):
|
|
63
|
+
return default
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _maxlogic(values):
|
|
67
|
+
return max(_sanitize_numeric_values(values))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _minlogic(values):
|
|
71
|
+
return min(_sanitize_numeric_values(values))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _sumlogic(values):
|
|
75
|
+
return math.fsum(_sanitize_numeric_values(values))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# Generate a List equivalent which uses "is" for contains
|
|
79
|
+
# Create a new List class, overriding __contains__ method, if any element in list has hash value equal to other's hash value, return True
|
|
80
|
+
class List(list):
|
|
81
|
+
"""List subclass that uses hash equality for contains checks.
|
|
82
|
+
|
|
83
|
+
This class overrides __contains__ to check if any element has
|
|
84
|
+
the same hash value as the target, rather than using identity comparison.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def __contains__(self, other):
|
|
88
|
+
return any(x is other for x in self)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# Create a class to serialize elements within it
|
|
92
|
+
class Logic(LineActions):
|
|
93
|
+
"""Base class for logical operations on line objects.
|
|
94
|
+
|
|
95
|
+
Handles argument conversion to arrays and manages minperiod
|
|
96
|
+
propagation from operands.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(self, *args):
|
|
100
|
+
"""Initialize the Logic operation.
|
|
101
|
+
|
|
102
|
+
Converts all arguments to arrays and propagates minperiod
|
|
103
|
+
from operands to ensure proper synchronization.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
*args: Line objects or values to operate on.
|
|
107
|
+
"""
|
|
108
|
+
super().__init__()
|
|
109
|
+
self.args = [self.arrayize(arg) for arg in args]
|
|
110
|
+
|
|
111
|
+
# CRITICAL FIX: Collect minperiods from args and update own minperiod
|
|
112
|
+
# This ensures functions like And, Or, etc. inherit the max minperiod from their operands
|
|
113
|
+
_minperiods = []
|
|
114
|
+
for arg in self.args:
|
|
115
|
+
mp = getattr(arg, "_minperiod", 1)
|
|
116
|
+
_minperiods.append(mp)
|
|
117
|
+
|
|
118
|
+
if _minperiods:
|
|
119
|
+
max_minperiod = max(_minperiods)
|
|
120
|
+
self.updateminperiod(max_minperiod)
|
|
121
|
+
|
|
122
|
+
def _next(self):
|
|
123
|
+
clock = getattr(self, "_clock", None)
|
|
124
|
+
if clock is not None and clock.__class__.__name__ != "MinimalClock":
|
|
125
|
+
try:
|
|
126
|
+
if len(clock) <= len(self):
|
|
127
|
+
return
|
|
128
|
+
except Exception: # nosec B110
|
|
129
|
+
# Clock without a comparable length; proceed to advance below.
|
|
130
|
+
throttled_warning(
|
|
131
|
+
logger, "logic_clock_advance", "functions:130 suppressed Exception"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
target_len = len(self) + 1
|
|
135
|
+
for arg in getattr(self, "args", ()):
|
|
136
|
+
if isinstance(arg, LineActions) and hasattr(arg, "_next"):
|
|
137
|
+
try:
|
|
138
|
+
if len(arg) < target_len:
|
|
139
|
+
arg._next()
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.debug("Logic operand _next() failed: %s", e)
|
|
142
|
+
|
|
143
|
+
self.advance()
|
|
144
|
+
self.next()
|
|
145
|
+
for binding in self.bindings:
|
|
146
|
+
binding[0] = self[0]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# Avoid division by zero when dividing two lines, if denominator is 0, division result is 0
|
|
150
|
+
class DivByZero(Logic):
|
|
151
|
+
"""This operation is a Lines object and fills it values by executing a
|
|
152
|
+
division on the numerator / denominator arguments and avoiding a division
|
|
153
|
+
by zero exception by checking the denominator
|
|
154
|
+
|
|
155
|
+
Params:
|
|
156
|
+
- a: numerator (numeric or iterable object ... mostly a Lines object)
|
|
157
|
+
- b: denominator (numeric or iterable object ... mostly a Lines object)
|
|
158
|
+
- zero (def: 0.0): value to apply if division by zero is raised
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
def __init__(self, a, b, zero=0.0):
|
|
163
|
+
"""Initialize the DivByZero operation.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
a: Numerator line or value.
|
|
167
|
+
b: Denominator line or value.
|
|
168
|
+
zero: Value to return when division by zero occurs.
|
|
169
|
+
"""
|
|
170
|
+
super().__init__(a, b)
|
|
171
|
+
self.a = self.args[0]
|
|
172
|
+
self.b = self.args[1]
|
|
173
|
+
self.zero = zero
|
|
174
|
+
|
|
175
|
+
def next(self):
|
|
176
|
+
"""Calculate the next value with zero-division protection."""
|
|
177
|
+
a = _sanitize_div_value(self.a[0])
|
|
178
|
+
b = _sanitize_div_value(self.b[0])
|
|
179
|
+
self[0] = a / b if b else self.zero
|
|
180
|
+
|
|
181
|
+
def once(self, start, end):
|
|
182
|
+
"""Calculate all values at once with zero-division protection.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
start: Starting index for calculation.
|
|
186
|
+
end: Ending index for calculation.
|
|
187
|
+
"""
|
|
188
|
+
# cache python dictionary lookups
|
|
189
|
+
dst = self.array
|
|
190
|
+
srca = self.a.array
|
|
191
|
+
srcb = self.b.array
|
|
192
|
+
zero = self.zero
|
|
193
|
+
|
|
194
|
+
# Ensure destination array is properly sized
|
|
195
|
+
while len(dst) < end:
|
|
196
|
+
dst.append(0.0)
|
|
197
|
+
|
|
198
|
+
for i in range(start, end):
|
|
199
|
+
a = _sanitize_div_value(_value_at(srca, i))
|
|
200
|
+
b = _sanitize_div_value(_value_at(srcb, i))
|
|
201
|
+
dst[i] = a / b if b else zero
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# Division operation for two lines considering both numerator and denominator may be 0
|
|
205
|
+
class DivZeroByZero(Logic):
|
|
206
|
+
"""This operation is a Lines object and fills it values by executing a
|
|
207
|
+
division on the numerator / denominator arguments and avoiding a division
|
|
208
|
+
by zero exception or an indetermination by checking the
|
|
209
|
+
denominator/numerator pair
|
|
210
|
+
|
|
211
|
+
Params:
|
|
212
|
+
- a: numerator (numeric or iterable object ... mostly a Lines object)
|
|
213
|
+
- b: denominator (numeric or iterable object ... mostly a Lines object)
|
|
214
|
+
- single (def: +inf): value to apply if division is x / 0
|
|
215
|
+
- dual (def: 0.0): value to apply if division is 0 / 0
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
def __init__(self, a, b, single=float("inf"), dual=0.0):
|
|
219
|
+
"""Initialize the DivZeroByZero operation.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
a: Numerator line or value.
|
|
223
|
+
b: Denominator line or value.
|
|
224
|
+
single: Value to return when numerator is non-zero and denominator is zero.
|
|
225
|
+
dual: Value to return when both numerator and denominator are zero.
|
|
226
|
+
"""
|
|
227
|
+
super().__init__(a, b)
|
|
228
|
+
self.a = self.args[0]
|
|
229
|
+
self.b = self.args[1]
|
|
230
|
+
self.single = single
|
|
231
|
+
self.dual = dual
|
|
232
|
+
|
|
233
|
+
def next(self):
|
|
234
|
+
"""Calculate the next value with zero/zero indetermination protection."""
|
|
235
|
+
b = _sanitize_div_value(self.b[0])
|
|
236
|
+
a = _sanitize_div_value(self.a[0])
|
|
237
|
+
if b == 0.0:
|
|
238
|
+
self[0] = self.dual if a == 0.0 else self.single
|
|
239
|
+
else:
|
|
240
|
+
self[0] = a / b
|
|
241
|
+
|
|
242
|
+
def once(self, start, end):
|
|
243
|
+
"""Calculate all values at once with zero/zero indetermination protection.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
start: Starting index for calculation.
|
|
247
|
+
end: Ending index for calculation.
|
|
248
|
+
"""
|
|
249
|
+
# cache python dictionary lookups
|
|
250
|
+
dst = self.array
|
|
251
|
+
srca = self.a.array
|
|
252
|
+
srcb = self.b.array
|
|
253
|
+
single = self.single
|
|
254
|
+
dual = self.dual
|
|
255
|
+
|
|
256
|
+
# Ensure destination array is properly sized
|
|
257
|
+
while len(dst) < end:
|
|
258
|
+
dst.append(0.0)
|
|
259
|
+
|
|
260
|
+
for i in range(start, end):
|
|
261
|
+
b = _sanitize_div_value(_value_at(srcb, i))
|
|
262
|
+
a = _sanitize_div_value(_value_at(srca, i))
|
|
263
|
+
if b == 0.0:
|
|
264
|
+
dst[i] = dual if a == 0.0 else single
|
|
265
|
+
else:
|
|
266
|
+
dst[i] = a / b
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# Compare a and b, a and b are likely lines
|
|
270
|
+
class Cmp(Logic):
|
|
271
|
+
"""Comparison operation that returns comparison results.
|
|
272
|
+
|
|
273
|
+
Compares two line objects and returns standard comparison values:
|
|
274
|
+
-1 if a < b, 0 if a == b, 1 if a > b.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
def __init__(self, a, b):
|
|
278
|
+
"""Initialize the comparison operation.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
a: First line or value to compare.
|
|
282
|
+
b: Second line or value to compare.
|
|
283
|
+
"""
|
|
284
|
+
super().__init__(a, b)
|
|
285
|
+
self.a = self.args[0]
|
|
286
|
+
self.b = self.args[1]
|
|
287
|
+
|
|
288
|
+
def next(self):
|
|
289
|
+
"""Calculate the next comparison value."""
|
|
290
|
+
self[0] = cmp(_sanitize_cmp_value(self.a[0]), _sanitize_cmp_value(self.b[0]))
|
|
291
|
+
|
|
292
|
+
def once(self, start, end):
|
|
293
|
+
"""Calculate all comparison values at once.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
start: Starting index for calculation.
|
|
297
|
+
end: Ending index for calculation.
|
|
298
|
+
"""
|
|
299
|
+
# cache python dictionary lookups
|
|
300
|
+
dst = self.array
|
|
301
|
+
srca = self.a.array
|
|
302
|
+
srcb = self.b.array
|
|
303
|
+
|
|
304
|
+
# Ensure destination array is properly sized
|
|
305
|
+
while len(dst) < end:
|
|
306
|
+
dst.append(0.0)
|
|
307
|
+
|
|
308
|
+
for i in range(start, end):
|
|
309
|
+
dst[i] = cmp(
|
|
310
|
+
_sanitize_cmp_value(_value_at(srca, i)),
|
|
311
|
+
_sanitize_cmp_value(_value_at(srcb, i)),
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
# Compare two lines, a and b, return corresponding r1 value when a<b, return r2 value when a=b, return r3 value when a>b
|
|
316
|
+
class CmpEx(Logic):
|
|
317
|
+
"""Extended comparison operation with three possible return values.
|
|
318
|
+
|
|
319
|
+
Compares two line objects and returns one of three values based on
|
|
320
|
+
the comparison result:
|
|
321
|
+
- r1 if a < b
|
|
322
|
+
- r2 if a == b
|
|
323
|
+
- r3 if a > b
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
def __init__(self, a, b, r1, r2, r3):
|
|
327
|
+
"""Initialize the extended comparison operation.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
a: First line or value to compare.
|
|
331
|
+
b: Second line or value to compare.
|
|
332
|
+
r1: Value to return when a < b.
|
|
333
|
+
r2: Value to return when a == b.
|
|
334
|
+
r3: Value to return when a > b.
|
|
335
|
+
"""
|
|
336
|
+
super().__init__(a, b, r1, r2, r3)
|
|
337
|
+
self.a = self.args[0]
|
|
338
|
+
self.b = self.args[1]
|
|
339
|
+
self.r1 = self.args[2]
|
|
340
|
+
self.r2 = self.args[3]
|
|
341
|
+
self.r3 = self.args[4]
|
|
342
|
+
|
|
343
|
+
def next(self):
|
|
344
|
+
"""Calculate the next extended comparison value."""
|
|
345
|
+
# self[0] = cmp(self.a[0], self.b[0])
|
|
346
|
+
a0 = _sanitize_cmp_value(self.a[0])
|
|
347
|
+
b0 = _sanitize_cmp_value(self.b[0])
|
|
348
|
+
|
|
349
|
+
if a0 < b0:
|
|
350
|
+
self[0] = _sanitize_div_value(self.r1[0])
|
|
351
|
+
elif a0 > b0:
|
|
352
|
+
self[0] = _sanitize_div_value(self.r3[0])
|
|
353
|
+
else:
|
|
354
|
+
self[0] = _sanitize_div_value(self.r2[0])
|
|
355
|
+
|
|
356
|
+
def once(self, start, end):
|
|
357
|
+
"""Calculate all extended comparison values at once.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
start: Starting index for calculation.
|
|
361
|
+
end: Ending index for calculation.
|
|
362
|
+
"""
|
|
363
|
+
# cache python dictionary lookups
|
|
364
|
+
dst = self.array
|
|
365
|
+
srca = self.a.array
|
|
366
|
+
srcb = self.b.array
|
|
367
|
+
r1 = self.r1.array
|
|
368
|
+
r2 = self.r2.array
|
|
369
|
+
r3 = self.r3.array
|
|
370
|
+
|
|
371
|
+
# Ensure destination array is properly sized
|
|
372
|
+
while len(dst) < end:
|
|
373
|
+
dst.append(0.0)
|
|
374
|
+
|
|
375
|
+
for i in range(start, end):
|
|
376
|
+
ai = _sanitize_cmp_value(_value_at(srca, i))
|
|
377
|
+
bi = _sanitize_cmp_value(_value_at(srcb, i))
|
|
378
|
+
|
|
379
|
+
if ai < bi:
|
|
380
|
+
dst[i] = _sanitize_div_value(_value_at(r1, i))
|
|
381
|
+
elif ai > bi:
|
|
382
|
+
dst[i] = _sanitize_div_value(_value_at(r3, i))
|
|
383
|
+
else:
|
|
384
|
+
dst[i] = _sanitize_div_value(_value_at(r2, i))
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
# If statement, return corresponding a value when cond is satisfied, return b value when not satisfied
|
|
388
|
+
class If(Logic):
|
|
389
|
+
"""Conditional selection operation.
|
|
390
|
+
|
|
391
|
+
Returns a value from a or b based on a condition:
|
|
392
|
+
- Returns a if condition is True
|
|
393
|
+
- Returns b if condition is False
|
|
394
|
+
"""
|
|
395
|
+
|
|
396
|
+
def __init__(self, cond, a, b):
|
|
397
|
+
"""Initialize the conditional operation.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
cond: Condition line - must evaluate to boolean.
|
|
401
|
+
a: Value to return when condition is True.
|
|
402
|
+
b: Value to return when condition is False.
|
|
403
|
+
"""
|
|
404
|
+
super().__init__(cond, a, b)
|
|
405
|
+
self.cond = self.args[0]
|
|
406
|
+
self.a = self.args[1]
|
|
407
|
+
self.b = self.args[2]
|
|
408
|
+
|
|
409
|
+
def next(self):
|
|
410
|
+
"""Calculate the next conditional value."""
|
|
411
|
+
cond_val = _sanitize_div_value(self.cond[0])
|
|
412
|
+
value = self.a[0] if cond_val else self.b[0]
|
|
413
|
+
self[0] = _sanitize_div_value(value)
|
|
414
|
+
|
|
415
|
+
def _has_self_reference(self):
|
|
416
|
+
"""Check if this If operation has a self-referencing pattern.
|
|
417
|
+
|
|
418
|
+
Detects patterns like: self.lines.direction = bt.If(..., direction(-1))
|
|
419
|
+
where the output line appears as an input via _LineDelay.
|
|
420
|
+
"""
|
|
421
|
+
if not self.bindings:
|
|
422
|
+
return False
|
|
423
|
+
|
|
424
|
+
# Get the bound line(s)
|
|
425
|
+
bound_lines = {id(b) for b in self.bindings}
|
|
426
|
+
|
|
427
|
+
# Check if any operand references a bound line (via _LineDelay)
|
|
428
|
+
def _check_ref(obj, depth=0):
|
|
429
|
+
if depth > 10:
|
|
430
|
+
return False
|
|
431
|
+
if hasattr(obj, "a"):
|
|
432
|
+
# _LineDelay: check if obj.a is one of our bound lines
|
|
433
|
+
if id(getattr(obj, "a", None)) in bound_lines:
|
|
434
|
+
return True
|
|
435
|
+
# Check if obj.a's array is the same as a bound line's array
|
|
436
|
+
obj_a = getattr(obj, "a", None)
|
|
437
|
+
if obj_a is not None:
|
|
438
|
+
for binding in self.bindings:
|
|
439
|
+
if hasattr(obj_a, "array") and hasattr(binding, "array"):
|
|
440
|
+
if obj_a.array is binding.array:
|
|
441
|
+
return True
|
|
442
|
+
if _check_ref(obj_a, depth + 1):
|
|
443
|
+
return True
|
|
444
|
+
if hasattr(obj, "b"):
|
|
445
|
+
obj_b = getattr(obj, "b", None)
|
|
446
|
+
if obj_b is not None:
|
|
447
|
+
if id(obj_b) in bound_lines:
|
|
448
|
+
return True
|
|
449
|
+
if hasattr(obj_b, "array"):
|
|
450
|
+
for binding in self.bindings:
|
|
451
|
+
if hasattr(binding, "array") and obj_b.array is binding.array:
|
|
452
|
+
return True
|
|
453
|
+
if _check_ref(obj_b, depth + 1):
|
|
454
|
+
return True
|
|
455
|
+
if hasattr(obj, "args"):
|
|
456
|
+
for arg in getattr(obj, "args", []):
|
|
457
|
+
if _check_ref(arg, depth + 1):
|
|
458
|
+
return True
|
|
459
|
+
if hasattr(obj, "cond"):
|
|
460
|
+
if _check_ref(getattr(obj, "cond", None), depth + 1):
|
|
461
|
+
return True
|
|
462
|
+
return False
|
|
463
|
+
|
|
464
|
+
return _check_ref(self.a) or _check_ref(self.b) or _check_ref(self.cond)
|
|
465
|
+
|
|
466
|
+
def once(self, start, end):
|
|
467
|
+
"""Calculate all conditional values at once.
|
|
468
|
+
|
|
469
|
+
Supports self-referencing patterns like:
|
|
470
|
+
self.lines.direction = bt.If(cond, 1, bt.If(cond2, -1, self.lines.direction(-1)))
|
|
471
|
+
|
|
472
|
+
For self-referencing patterns, processes bar-by-bar using next() semantics
|
|
473
|
+
to ensure previously computed values are available for the next bar.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
start: Starting index for calculation.
|
|
477
|
+
end: Ending index for calculation.
|
|
478
|
+
"""
|
|
479
|
+
dst = self.array
|
|
480
|
+
|
|
481
|
+
# Ensure destination array is properly sized
|
|
482
|
+
while len(dst) < end:
|
|
483
|
+
dst.append(0.0)
|
|
484
|
+
|
|
485
|
+
# Also ensure bound line arrays are sized
|
|
486
|
+
for binding in self.bindings:
|
|
487
|
+
while len(binding.array) < end:
|
|
488
|
+
binding.array.append(0.0)
|
|
489
|
+
|
|
490
|
+
# For self-referencing patterns, use bar-by-bar processing
|
|
491
|
+
# This ensures _LineDelay can read previously computed values
|
|
492
|
+
if self.bindings and self._has_self_reference():
|
|
493
|
+
self._once_sequential(start, end)
|
|
494
|
+
return
|
|
495
|
+
|
|
496
|
+
# Standard If expressions can contain nested LinesOperation/_LineDelay
|
|
497
|
+
# operands which are not always scheduled separately by LineIterator.
|
|
498
|
+
# Compute them explicitly before reading their arrays below.
|
|
499
|
+
for operand in (self.cond, self.a, self.b):
|
|
500
|
+
if hasattr(operand, "once") and len(getattr(operand, "array", [])) < end:
|
|
501
|
+
try:
|
|
502
|
+
operand.once(0, end)
|
|
503
|
+
except Exception as e:
|
|
504
|
+
logger.debug("If operand once() failed: %s", e)
|
|
505
|
+
|
|
506
|
+
# Standard batch processing for non-self-referencing patterns
|
|
507
|
+
self._once_batch(start, end)
|
|
508
|
+
|
|
509
|
+
def _once_sequential(self, start, end):
|
|
510
|
+
"""Process bar-by-bar for self-referencing patterns.
|
|
511
|
+
|
|
512
|
+
Ensures all operand arrays are computed first, then processes
|
|
513
|
+
sequentially with immediate binding propagation so _LineDelay
|
|
514
|
+
can read previously written values.
|
|
515
|
+
"""
|
|
516
|
+
dst = self.array
|
|
517
|
+
has_bindings = bool(self.bindings)
|
|
518
|
+
|
|
519
|
+
# Ensure operand arrays are computed first
|
|
520
|
+
# The condition (LinesOperation) needs its once() called
|
|
521
|
+
if hasattr(self.cond, "once") and len(getattr(self.cond, "array", [])) < end:
|
|
522
|
+
try:
|
|
523
|
+
self.cond.once(start, end)
|
|
524
|
+
except Exception: # nosec B110
|
|
525
|
+
# Operand already (partially) computed or not once()-able; continue.
|
|
526
|
+
logger.warning("functions:524 suppressed Exception")
|
|
527
|
+
|
|
528
|
+
# The 'a' operand (could be constant or LinesOperation)
|
|
529
|
+
if hasattr(self.a, "once") and len(getattr(self.a, "array", [])) < end:
|
|
530
|
+
try:
|
|
531
|
+
self.a.once(start, end)
|
|
532
|
+
except Exception: # nosec B110
|
|
533
|
+
# Operand already (partially) computed or not once()-able; continue.
|
|
534
|
+
logger.warning("functions:532 suppressed Exception")
|
|
535
|
+
|
|
536
|
+
# The 'b' operand - for self-referencing, this is typically another bt.If
|
|
537
|
+
# We need to compute it BUT it contains the self-reference, so we handle it specially
|
|
538
|
+
if hasattr(self.b, "once") and len(getattr(self.b, "array", [])) < end:
|
|
539
|
+
# Check if b itself has self-reference (nested bt.If with direction(-1))
|
|
540
|
+
# If so, we need to compute b bar-by-bar too
|
|
541
|
+
if hasattr(self.b, "_has_self_reference") and self.b._has_self_reference():
|
|
542
|
+
# Don't call b.once() - we'll compute b[i] dynamically
|
|
543
|
+
pass
|
|
544
|
+
else:
|
|
545
|
+
try:
|
|
546
|
+
self.b.once(start, end)
|
|
547
|
+
except Exception: # nosec B110
|
|
548
|
+
# Operand already (partially) computed or not once()-able; continue.
|
|
549
|
+
logger.warning("functions:547 suppressed Exception")
|
|
550
|
+
|
|
551
|
+
# Get arrays for direct access where possible
|
|
552
|
+
cond_array = getattr(self.cond, "array", [])
|
|
553
|
+
cond_has_array = len(cond_array) >= end
|
|
554
|
+
a_array = getattr(self.a, "array", [])
|
|
555
|
+
a_has_array = len(a_array) >= end
|
|
556
|
+
# Check if a is a constant (_LineDelay wrapping PseudoArray)
|
|
557
|
+
a_is_constant = False
|
|
558
|
+
a_constant_val = None
|
|
559
|
+
if not a_has_array:
|
|
560
|
+
try:
|
|
561
|
+
a_constant_val = self.a[0]
|
|
562
|
+
a_is_constant = True
|
|
563
|
+
except Exception: # nosec B110
|
|
564
|
+
# 'a' is neither array-backed nor a constant scalar; leave defaults.
|
|
565
|
+
logger.warning("functions:563 suppressed Exception")
|
|
566
|
+
|
|
567
|
+
b_array = getattr(self.b, "array", [])
|
|
568
|
+
b_has_array = len(b_array) >= end
|
|
569
|
+
|
|
570
|
+
for i in range(start, end):
|
|
571
|
+
# Get condition value
|
|
572
|
+
if cond_has_array:
|
|
573
|
+
cond_val = cond_array[i]
|
|
574
|
+
else:
|
|
575
|
+
try:
|
|
576
|
+
cond_val = (
|
|
577
|
+
self.cond.array[i] if i < len(getattr(self.cond, "array", [])) else 0.0
|
|
578
|
+
)
|
|
579
|
+
except (IndexError, TypeError):
|
|
580
|
+
cond_val = 0.0
|
|
581
|
+
|
|
582
|
+
cond_bool = (cond_val != 0.0) and (
|
|
583
|
+
not (isinstance(cond_val, float) and math.isnan(cond_val))
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
if cond_bool:
|
|
587
|
+
# Get a value
|
|
588
|
+
if a_is_constant:
|
|
589
|
+
val = a_constant_val
|
|
590
|
+
elif a_has_array:
|
|
591
|
+
val = a_array[i]
|
|
592
|
+
else:
|
|
593
|
+
try:
|
|
594
|
+
val = self.a.array[i] if i < len(getattr(self.a, "array", [])) else 0.0
|
|
595
|
+
except (IndexError, TypeError):
|
|
596
|
+
val = 0.0
|
|
597
|
+
else:
|
|
598
|
+
# Get b value - for self-referencing, b is the inner bt.If
|
|
599
|
+
# which reads from _LineDelay(direction, -1)
|
|
600
|
+
if b_has_array:
|
|
601
|
+
val = b_array[i]
|
|
602
|
+
else:
|
|
603
|
+
# b's array isn't fully computed - compute dynamically
|
|
604
|
+
# For nested bt.If with self-reference, we need to evaluate it
|
|
605
|
+
try:
|
|
606
|
+
val = self._eval_operand_at(self.b, i)
|
|
607
|
+
except Exception:
|
|
608
|
+
throttled_warning(
|
|
609
|
+
logger, "if_sequential_operand", "functions:605 fallback on Exception"
|
|
610
|
+
)
|
|
611
|
+
val = 0.0
|
|
612
|
+
|
|
613
|
+
val = _sanitize_div_value(val)
|
|
614
|
+
|
|
615
|
+
dst[i] = val
|
|
616
|
+
|
|
617
|
+
# Propagate to bindings immediately for self-referencing
|
|
618
|
+
if has_bindings:
|
|
619
|
+
for binding in self.bindings:
|
|
620
|
+
binding.array[i] = val
|
|
621
|
+
|
|
622
|
+
def _eval_operand_at(self, operand, i):
|
|
623
|
+
"""Evaluate an operand at absolute index i.
|
|
624
|
+
|
|
625
|
+
For nested bt.If with self-reference, recursively evaluates
|
|
626
|
+
the condition and branches at the given index.
|
|
627
|
+
"""
|
|
628
|
+
if isinstance(operand, If):
|
|
629
|
+
# Recursively evaluate the nested If
|
|
630
|
+
# Get condition
|
|
631
|
+
cond_arr = getattr(operand.cond, "array", [])
|
|
632
|
+
if i < len(cond_arr):
|
|
633
|
+
cond_val = cond_arr[i]
|
|
634
|
+
else:
|
|
635
|
+
cond_val = 0.0
|
|
636
|
+
|
|
637
|
+
cond_bool = (cond_val != 0.0) and (
|
|
638
|
+
not (isinstance(cond_val, float) and math.isnan(cond_val))
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
if cond_bool:
|
|
642
|
+
return self._eval_operand_at(operand.a, i)
|
|
643
|
+
return self._eval_operand_at(operand.b, i)
|
|
644
|
+
|
|
645
|
+
# For _LineDelay, read from its source array at offset
|
|
646
|
+
if hasattr(operand, "ago") and hasattr(operand, "a"):
|
|
647
|
+
src_array = getattr(operand.a, "array", [])
|
|
648
|
+
src_idx = i + operand.ago
|
|
649
|
+
if 0 <= src_idx < len(src_array):
|
|
650
|
+
return src_array[src_idx]
|
|
651
|
+
return 0.0
|
|
652
|
+
|
|
653
|
+
# For arrays, direct access
|
|
654
|
+
arr = getattr(operand, "array", [])
|
|
655
|
+
if i < len(arr):
|
|
656
|
+
return arr[i]
|
|
657
|
+
|
|
658
|
+
# Constant
|
|
659
|
+
try:
|
|
660
|
+
return operand[0]
|
|
661
|
+
except Exception:
|
|
662
|
+
throttled_warning(logger, "if_operand_scalar", "functions:656 fallback on Exception")
|
|
663
|
+
return 0.0
|
|
664
|
+
|
|
665
|
+
def _once_batch(self, start, end):
|
|
666
|
+
"""Standard batch processing for non-self-referencing patterns."""
|
|
667
|
+
dst = self.array
|
|
668
|
+
|
|
669
|
+
# Detect constants
|
|
670
|
+
a_is_constant = False
|
|
671
|
+
a_constant_val = None
|
|
672
|
+
try:
|
|
673
|
+
srca = self.a.array
|
|
674
|
+
a_has_array = len(srca) > 0
|
|
675
|
+
if not a_has_array:
|
|
676
|
+
try:
|
|
677
|
+
a_constant_val = self.a[0]
|
|
678
|
+
a_is_constant = True
|
|
679
|
+
except Exception: # nosec B110
|
|
680
|
+
# 'a' has an empty array and no scalar value; not a constant.
|
|
681
|
+
logger.warning("functions:675 suppressed Exception")
|
|
682
|
+
except (AttributeError, TypeError):
|
|
683
|
+
srca = []
|
|
684
|
+
a_has_array = False
|
|
685
|
+
try:
|
|
686
|
+
a_constant_val = self.a[0]
|
|
687
|
+
a_is_constant = True
|
|
688
|
+
except Exception: # nosec B110
|
|
689
|
+
# 'a' is neither array-backed nor a scalar constant.
|
|
690
|
+
logger.warning("functions:684 suppressed Exception")
|
|
691
|
+
|
|
692
|
+
b_is_constant = False
|
|
693
|
+
b_constant_val = None
|
|
694
|
+
try:
|
|
695
|
+
srcb = self.b.array
|
|
696
|
+
b_has_array = len(srcb) > 0
|
|
697
|
+
if not b_has_array:
|
|
698
|
+
try:
|
|
699
|
+
b_constant_val = self.b[0]
|
|
700
|
+
b_is_constant = True
|
|
701
|
+
except Exception: # nosec B110
|
|
702
|
+
# 'b' has an empty array and no scalar value; not a constant.
|
|
703
|
+
logger.warning("functions:697 suppressed Exception")
|
|
704
|
+
except (AttributeError, TypeError):
|
|
705
|
+
srcb = []
|
|
706
|
+
b_has_array = False
|
|
707
|
+
try:
|
|
708
|
+
b_constant_val = self.b[0]
|
|
709
|
+
b_is_constant = True
|
|
710
|
+
except Exception: # nosec B110
|
|
711
|
+
# 'b' is neither array-backed nor a scalar constant.
|
|
712
|
+
logger.warning("functions:706 suppressed Exception")
|
|
713
|
+
|
|
714
|
+
try:
|
|
715
|
+
cond = self.cond.array
|
|
716
|
+
cond_has_array = len(cond) > 0
|
|
717
|
+
except (AttributeError, TypeError):
|
|
718
|
+
cond = []
|
|
719
|
+
cond_has_array = False
|
|
720
|
+
|
|
721
|
+
a_use_dynamic = not a_is_constant and not a_has_array and hasattr(self.a, "__getitem__")
|
|
722
|
+
b_use_dynamic = not b_is_constant and not b_has_array and hasattr(self.b, "__getitem__")
|
|
723
|
+
has_bindings = bool(self.bindings)
|
|
724
|
+
|
|
725
|
+
for i in range(start, end):
|
|
726
|
+
if cond_has_array:
|
|
727
|
+
try:
|
|
728
|
+
cond_val = cond[i] if i < len(cond) else (cond[-1] if cond else 0.0)
|
|
729
|
+
except (IndexError, TypeError):
|
|
730
|
+
cond_val = 0.0
|
|
731
|
+
else:
|
|
732
|
+
try:
|
|
733
|
+
cond_val = self.cond[i] if hasattr(self.cond, "__getitem__") else 0.0
|
|
734
|
+
except Exception:
|
|
735
|
+
throttled_warning(
|
|
736
|
+
logger, "if_batch_condition", "functions:728 fallback on Exception"
|
|
737
|
+
)
|
|
738
|
+
cond_val = 0.0
|
|
739
|
+
|
|
740
|
+
cond_bool = (cond_val != 0.0) and (
|
|
741
|
+
not (isinstance(cond_val, float) and math.isnan(cond_val))
|
|
742
|
+
)
|
|
743
|
+
|
|
744
|
+
if a_is_constant:
|
|
745
|
+
a_val = a_constant_val
|
|
746
|
+
elif a_has_array:
|
|
747
|
+
try:
|
|
748
|
+
a_val = srca[i] if i < len(srca) else (srca[-1] if srca else 0.0)
|
|
749
|
+
except (IndexError, TypeError):
|
|
750
|
+
a_val = 0.0
|
|
751
|
+
elif a_use_dynamic:
|
|
752
|
+
try:
|
|
753
|
+
a_val = self.a[i]
|
|
754
|
+
except Exception:
|
|
755
|
+
throttled_warning(
|
|
756
|
+
logger, "if_batch_true_operand", "functions:745 fallback on Exception"
|
|
757
|
+
)
|
|
758
|
+
a_val = 0.0
|
|
759
|
+
else:
|
|
760
|
+
a_val = 0.0
|
|
761
|
+
|
|
762
|
+
if b_is_constant:
|
|
763
|
+
b_val = b_constant_val
|
|
764
|
+
elif b_has_array:
|
|
765
|
+
try:
|
|
766
|
+
b_val = srcb[i] if i < len(srcb) else (srcb[-1] if srcb else 0.0)
|
|
767
|
+
except (IndexError, TypeError):
|
|
768
|
+
b_val = 0.0
|
|
769
|
+
elif b_use_dynamic:
|
|
770
|
+
try:
|
|
771
|
+
b_val = self.b[i]
|
|
772
|
+
except Exception:
|
|
773
|
+
throttled_warning(
|
|
774
|
+
logger, "if_batch_false_operand", "functions:760 fallback on Exception"
|
|
775
|
+
)
|
|
776
|
+
b_val = 0.0
|
|
777
|
+
else:
|
|
778
|
+
b_val = 0.0
|
|
779
|
+
|
|
780
|
+
a_val = _sanitize_div_value(a_val)
|
|
781
|
+
b_val = _sanitize_div_value(b_val)
|
|
782
|
+
|
|
783
|
+
val = a_val if cond_bool else b_val
|
|
784
|
+
dst[i] = val
|
|
785
|
+
|
|
786
|
+
# Propagate to bindings for consistency
|
|
787
|
+
if has_bindings:
|
|
788
|
+
for binding in self.bindings:
|
|
789
|
+
binding.array[i] = val
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
# Apply one logic to multiple elements
|
|
793
|
+
class MultiLogic(Logic):
|
|
794
|
+
"""Base class for operations that apply a function to multiple arguments.
|
|
795
|
+
|
|
796
|
+
The flogic attribute should be set to a callable that takes
|
|
797
|
+
an iterable of values and returns a single result.
|
|
798
|
+
"""
|
|
799
|
+
|
|
800
|
+
def next(self):
|
|
801
|
+
"""Apply the logic function to current values from all arguments."""
|
|
802
|
+
self[0] = self.flogic([arg[0] for arg in self.args])
|
|
803
|
+
|
|
804
|
+
def once(self, start, end):
|
|
805
|
+
"""Apply the logic function to all values across the specified range.
|
|
806
|
+
|
|
807
|
+
Args:
|
|
808
|
+
start: Starting index for calculation.
|
|
809
|
+
end: Ending index for calculation.
|
|
810
|
+
"""
|
|
811
|
+
# cache python dictionary lookups
|
|
812
|
+
dst = self.array
|
|
813
|
+
|
|
814
|
+
# Ensure destination array is properly sized
|
|
815
|
+
while len(dst) < end:
|
|
816
|
+
dst.append(0.0)
|
|
817
|
+
|
|
818
|
+
for arg in self.args:
|
|
819
|
+
if isinstance(arg, LineActions) and hasattr(arg, "once"):
|
|
820
|
+
try:
|
|
821
|
+
if len(getattr(arg, "array", [])) < end:
|
|
822
|
+
arg.once(0, end)
|
|
823
|
+
except Exception as e:
|
|
824
|
+
logger.debug("MultiLogic operand once() failed: %s", e)
|
|
825
|
+
|
|
826
|
+
arrays = [arg.array for arg in self.args]
|
|
827
|
+
flogic = self.flogic
|
|
828
|
+
|
|
829
|
+
for i in range(start, end):
|
|
830
|
+
dst[i] = flogic([_value_at(arr, i) for arr in arrays])
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
# Mainly uses functools.partial to generate partial function, functools.reduce, iterates function on a sequence
|
|
834
|
+
class MultiLogicReduce(MultiLogic):
|
|
835
|
+
"""MultiLogic that uses functools.reduce for cumulative operations.
|
|
836
|
+
|
|
837
|
+
This class applies a reduction function cumulatively to all arguments,
|
|
838
|
+
combining them into a single result.
|
|
839
|
+
"""
|
|
840
|
+
|
|
841
|
+
def __init__(self, *args, **kwargs):
|
|
842
|
+
"""Initialize the reduction operation.
|
|
843
|
+
|
|
844
|
+
Args:
|
|
845
|
+
*args: Line objects or values to reduce.
|
|
846
|
+
**kwargs: Optional keyword arguments including 'initializer'.
|
|
847
|
+
"""
|
|
848
|
+
super().__init__(*args)
|
|
849
|
+
if "initializer" not in kwargs:
|
|
850
|
+
self.flogic = functools.partial(functools.reduce, self.flogic)
|
|
851
|
+
else:
|
|
852
|
+
self.flogic = functools.partial(
|
|
853
|
+
functools.reduce, self.flogic, initializer=kwargs["initializer"]
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
# Inheritance class, process flogic
|
|
858
|
+
class Reduce(MultiLogicReduce):
|
|
859
|
+
"""Generic reduction operation with a custom function.
|
|
860
|
+
|
|
861
|
+
Allows any reduction function to be applied to the arguments.
|
|
862
|
+
"""
|
|
863
|
+
|
|
864
|
+
def __init__(self, flogic, *args, **kwargs):
|
|
865
|
+
"""Initialize the custom reduction operation.
|
|
866
|
+
|
|
867
|
+
Args:
|
|
868
|
+
flogic: Function to use for reduction.
|
|
869
|
+
*args: Line objects or values to reduce.
|
|
870
|
+
**kwargs: Optional keyword arguments.
|
|
871
|
+
"""
|
|
872
|
+
self.flogic = flogic
|
|
873
|
+
super().__init__(*args, **kwargs)
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
# The _xxxlogic functions are defined at module scope to make them
|
|
877
|
+
# pickable and therefore compatible with multiprocessing
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
# Determine if both x and y are True
|
|
881
|
+
def _andlogic(x, y):
|
|
882
|
+
"""Logical AND operation for reduction."""
|
|
883
|
+
return bool(x and y)
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
# Determine if all elements are True
|
|
887
|
+
class And(MultiLogicReduce):
|
|
888
|
+
"""Logical AND operation across all arguments.
|
|
889
|
+
|
|
890
|
+
Returns True only if all input values are truthy.
|
|
891
|
+
"""
|
|
892
|
+
|
|
893
|
+
flogic = staticmethod(_andlogic)
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
# Determine if either x or y is true
|
|
897
|
+
def _orlogic(x, y):
|
|
898
|
+
"""Logical OR operation for reduction."""
|
|
899
|
+
return bool(x or y)
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
# Determine if any element in the sequence is true
|
|
903
|
+
class Or(MultiLogicReduce):
|
|
904
|
+
"""Logical OR operation across all arguments.
|
|
905
|
+
|
|
906
|
+
Returns True if any input value is truthy.
|
|
907
|
+
"""
|
|
908
|
+
|
|
909
|
+
flogic = staticmethod(_orlogic)
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
# Find maximum value
|
|
913
|
+
class Max(MultiLogic):
|
|
914
|
+
"""Maximum operation across all arguments.
|
|
915
|
+
|
|
916
|
+
Returns the maximum value from all input lines.
|
|
917
|
+
"""
|
|
918
|
+
|
|
919
|
+
flogic = staticmethod(_maxlogic)
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
# Find minimum value
|
|
923
|
+
class Min(MultiLogic):
|
|
924
|
+
"""Minimum operation across all arguments.
|
|
925
|
+
|
|
926
|
+
Returns the minimum value from all input lines.
|
|
927
|
+
"""
|
|
928
|
+
|
|
929
|
+
flogic = staticmethod(_minlogic)
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
# Calculate sum
|
|
933
|
+
class Sum(MultiLogic):
|
|
934
|
+
"""Sum operation across all arguments.
|
|
935
|
+
|
|
936
|
+
Returns the sum of all input values using math.fsum
|
|
937
|
+
for better floating point precision.
|
|
938
|
+
"""
|
|
939
|
+
|
|
940
|
+
flogic = staticmethod(_sumlogic)
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
# Check if any exists
|
|
944
|
+
class Any(MultiLogic):
|
|
945
|
+
"""Any operation across all arguments.
|
|
946
|
+
|
|
947
|
+
Returns True if any input value is truthy.
|
|
948
|
+
"""
|
|
949
|
+
|
|
950
|
+
flogic = any
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
# Check if all
|
|
954
|
+
class All(MultiLogic):
|
|
955
|
+
"""All operation across all arguments.
|
|
956
|
+
|
|
957
|
+
Returns True only if all input values are truthy.
|
|
958
|
+
"""
|
|
959
|
+
|
|
960
|
+
flogic = all
|