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,1218 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""btrun - Command-line runner for Backtrader backtesting framework.
|
|
3
|
+
|
|
4
|
+
This module provides a command-line interface for running backtrader strategies
|
|
5
|
+
from the command line. It supports loading data feeds, strategies, indicators,
|
|
6
|
+
observers, analyzers, and signals from both built-in modules and external files.
|
|
7
|
+
|
|
8
|
+
The main entry point is the `btrun()` function which parses command-line arguments
|
|
9
|
+
and executes a complete backtest with the specified configuration.
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
Running from command line:
|
|
13
|
+
$ python -m backtrader.btrun.btrun --data data.csv --strategy MyStrategy
|
|
14
|
+
|
|
15
|
+
Running programmatically:
|
|
16
|
+
>>> from backtrader.btrun import btrun
|
|
17
|
+
>>> btrun('--data data.csv --strategy MyStrategy')
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import ast
|
|
22
|
+
import datetime
|
|
23
|
+
import inspect
|
|
24
|
+
import random
|
|
25
|
+
import string
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
from .. import analyzers as analyzers_module
|
|
29
|
+
from .. import indicators as indicators_module
|
|
30
|
+
from .. import observers as observers_module
|
|
31
|
+
from .. import signal as signal_module
|
|
32
|
+
from .. import signals as signals_module
|
|
33
|
+
from .. import strategies as strategies_module
|
|
34
|
+
from ..analyzer import Analyzer
|
|
35
|
+
from ..cerebro import Cerebro
|
|
36
|
+
from ..dataseries import TimeFrame
|
|
37
|
+
from ..feeds.btcsv import BacktraderCSVData
|
|
38
|
+
from ..feeds.mt4csv import MT4CSVData
|
|
39
|
+
from ..feeds.sierrachart import SierraChartCSVData
|
|
40
|
+
from ..feeds.vchartcsv import VChartCSVData
|
|
41
|
+
from ..feeds.vchartfile import VChartFile
|
|
42
|
+
from ..feeds.yahoo import YahooFinanceCSVData, YahooFinanceData
|
|
43
|
+
from ..feeds.yahoounreversed import YahooFinanceCSVData as YahooFinanceCSVDataUnreversed
|
|
44
|
+
from ..indicator import Indicator
|
|
45
|
+
from ..observer import Observer
|
|
46
|
+
from ..strategy import Strategy
|
|
47
|
+
from ..utils.log_message import get_logger
|
|
48
|
+
from ..writer import WriterFile
|
|
49
|
+
|
|
50
|
+
logger = get_logger(__name__)
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
from ..feeds.btapifeed import BtApiFeed
|
|
54
|
+
except ImportError:
|
|
55
|
+
BtApiFeed = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
DATAFORMATS = {
|
|
59
|
+
"btcsv": BacktraderCSVData,
|
|
60
|
+
"vchartcsv": VChartCSVData,
|
|
61
|
+
"vcfile": VChartFile,
|
|
62
|
+
"sierracsv": SierraChartCSVData,
|
|
63
|
+
"mt4csv": MT4CSVData,
|
|
64
|
+
"yahoocsv": YahooFinanceCSVData,
|
|
65
|
+
"yahoocsv_unreversed": YahooFinanceCSVDataUnreversed,
|
|
66
|
+
"yahoo": YahooFinanceData,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if BtApiFeed is not None:
|
|
70
|
+
DATAFORMATS["btapi"] = BtApiFeed
|
|
71
|
+
|
|
72
|
+
TIMEFRAMES = {
|
|
73
|
+
"microseconds": TimeFrame.MicroSeconds,
|
|
74
|
+
"seconds": TimeFrame.Seconds,
|
|
75
|
+
"minutes": TimeFrame.Minutes,
|
|
76
|
+
"days": TimeFrame.Days,
|
|
77
|
+
"weeks": TimeFrame.Weeks,
|
|
78
|
+
"months": TimeFrame.Months,
|
|
79
|
+
"years": TimeFrame.Years,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _safe_parse_kwargs(kwtext: str) -> dict:
|
|
84
|
+
"""Safely parse kwargs string without using eval().
|
|
85
|
+
|
|
86
|
+
Parses a string like "a=1,b=2,c='hello'" into a dictionary.
|
|
87
|
+
Uses ast.literal_eval for safe evaluation of values.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
kwtext: String containing key=value pairs separated by commas.
|
|
91
|
+
Values can be integers, floats, booleans, strings, or None.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
dict: Parsed keyword arguments.
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
>>> _safe_parse_kwargs("a=1,b=2.5,c=True,d='hello'")
|
|
98
|
+
{'a': 1, 'b': 2.5, 'c': True, 'd': 'hello'}
|
|
99
|
+
"""
|
|
100
|
+
if not kwtext or not kwtext.strip():
|
|
101
|
+
return {}
|
|
102
|
+
|
|
103
|
+
result = {}
|
|
104
|
+
|
|
105
|
+
# Try ast.literal_eval first for the entire dict expression
|
|
106
|
+
try:
|
|
107
|
+
# Wrap in dict() and try literal_eval
|
|
108
|
+
parsed = ast.literal_eval(f"dict({kwtext})")
|
|
109
|
+
if isinstance(parsed, dict):
|
|
110
|
+
return parsed
|
|
111
|
+
except (ValueError, SyntaxError, TypeError):
|
|
112
|
+
# Not a clean dict literal; fall back to manual key=value parsing below.
|
|
113
|
+
logger.debug("btrun:112 ignored ValueError,SyntaxError,TypeError")
|
|
114
|
+
|
|
115
|
+
# Fall back to manual parsing
|
|
116
|
+
# Handle nested parentheses and quoted strings properly
|
|
117
|
+
items = _split_kwargs(kwtext)
|
|
118
|
+
|
|
119
|
+
for item in items:
|
|
120
|
+
item = item.strip()
|
|
121
|
+
if not item:
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
if "=" not in item:
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
key, value = item.split("=", 1)
|
|
128
|
+
key = key.strip()
|
|
129
|
+
value = value.strip()
|
|
130
|
+
|
|
131
|
+
result[key] = _convert_value(value)
|
|
132
|
+
|
|
133
|
+
return result
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _split_kwargs(kwtext: str) -> list:
|
|
137
|
+
"""Split kwargs string by commas, respecting quotes and parentheses.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
kwtext: String containing key=value pairs.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
list: List of individual key=value strings.
|
|
144
|
+
"""
|
|
145
|
+
items = []
|
|
146
|
+
current: list = []
|
|
147
|
+
depth = 0
|
|
148
|
+
in_string = False
|
|
149
|
+
string_char = None
|
|
150
|
+
|
|
151
|
+
for char in kwtext:
|
|
152
|
+
if char in ('"', "'") and (not current or current[-1] != "\\"):
|
|
153
|
+
if not in_string:
|
|
154
|
+
in_string = True
|
|
155
|
+
string_char = char
|
|
156
|
+
elif char == string_char:
|
|
157
|
+
in_string = False
|
|
158
|
+
string_char = None
|
|
159
|
+
current.append(char)
|
|
160
|
+
elif char in ("(", "[", "{") and not in_string:
|
|
161
|
+
depth += 1
|
|
162
|
+
current.append(char)
|
|
163
|
+
elif char in (")", "]", "}") and not in_string:
|
|
164
|
+
depth -= 1
|
|
165
|
+
current.append(char)
|
|
166
|
+
elif char == "," and depth == 0 and not in_string:
|
|
167
|
+
items.append("".join(current))
|
|
168
|
+
current = []
|
|
169
|
+
else:
|
|
170
|
+
current.append(char)
|
|
171
|
+
|
|
172
|
+
if current:
|
|
173
|
+
items.append("".join(current))
|
|
174
|
+
|
|
175
|
+
return items
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _convert_value(value: str):
|
|
179
|
+
"""Convert a string value to its appropriate Python type.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
value: String representation of a value.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
The value converted to int, float, bool, None, or string.
|
|
186
|
+
"""
|
|
187
|
+
# Try ast.literal_eval first (handles most cases safely)
|
|
188
|
+
try:
|
|
189
|
+
return ast.literal_eval(value)
|
|
190
|
+
except (ValueError, SyntaxError):
|
|
191
|
+
# Not a Python literal; try the explicit conversions below.
|
|
192
|
+
logger.debug("btrun:191 ignored ValueError,SyntaxError")
|
|
193
|
+
|
|
194
|
+
# Handle boolean values (case-insensitive)
|
|
195
|
+
if value.lower() == "true":
|
|
196
|
+
return True
|
|
197
|
+
if value.lower() == "false":
|
|
198
|
+
return False
|
|
199
|
+
if value.lower() == "none":
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
# Handle integers
|
|
203
|
+
try:
|
|
204
|
+
return int(value)
|
|
205
|
+
except ValueError:
|
|
206
|
+
# Not an integer; try float next.
|
|
207
|
+
logger.debug("btrun:206 ignored ValueError")
|
|
208
|
+
|
|
209
|
+
# Handle floats
|
|
210
|
+
try:
|
|
211
|
+
return float(value)
|
|
212
|
+
except ValueError:
|
|
213
|
+
# Not a float; treat as a (possibly quoted) string below.
|
|
214
|
+
logger.debug("btrun:213 ignored ValueError")
|
|
215
|
+
|
|
216
|
+
# Remove surrounding quotes if present
|
|
217
|
+
if len(value) >= 2:
|
|
218
|
+
if (value[0] == '"' and value[-1] == '"') or (value[0] == "'" and value[-1] == "'"):
|
|
219
|
+
return value[1:-1]
|
|
220
|
+
|
|
221
|
+
# Return as string
|
|
222
|
+
return value
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def btrun(pargs=""):
|
|
226
|
+
"""Run a backtest with the specified configuration.
|
|
227
|
+
|
|
228
|
+
This is the main entry point for command-line backtesting. It parses arguments,
|
|
229
|
+
sets up the Cerebro engine with data feeds, strategies, indicators, observers,
|
|
230
|
+
and analyzers, then executes the backtest and optionally displays results.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
pargs (str or list, optional): Command-line arguments as a string or list.
|
|
234
|
+
If empty, uses sys.argv. Defaults to "".
|
|
235
|
+
|
|
236
|
+
The function performs the following steps:
|
|
237
|
+
1. Parse command-line arguments
|
|
238
|
+
2. Create Cerebro instance with specified parameters
|
|
239
|
+
3. Add data feeds (with optional resampling/replaying)
|
|
240
|
+
4. Add signals, strategies, indicators, observers, and analyzers
|
|
241
|
+
5. Configure broker settings (cash, commission, slippage)
|
|
242
|
+
6. Add writers for output
|
|
243
|
+
7. Run the backtest
|
|
244
|
+
8. Optionally print analyzer results and plot results
|
|
245
|
+
"""
|
|
246
|
+
args = parse_args(pargs)
|
|
247
|
+
|
|
248
|
+
if args.flush:
|
|
249
|
+
pass
|
|
250
|
+
|
|
251
|
+
stdstats = not args.nostdstats
|
|
252
|
+
|
|
253
|
+
cer_kwargs_str = args.cerebro
|
|
254
|
+
cer_kwargs = _safe_parse_kwargs(cer_kwargs_str)
|
|
255
|
+
if "stdstats" not in cer_kwargs:
|
|
256
|
+
cer_kwargs.update(stdstats=stdstats)
|
|
257
|
+
|
|
258
|
+
cerebro = Cerebro(**cer_kwargs)
|
|
259
|
+
|
|
260
|
+
_add_datas(args, cerebro)
|
|
261
|
+
|
|
262
|
+
# get and add signals
|
|
263
|
+
signals = getobjects(args.signals, Indicator, signals_module, issignal=True)
|
|
264
|
+
for sig, kwargs, sigtype in signals:
|
|
265
|
+
stype = getattr(signal_module, "SIGNAL_" + sigtype.upper())
|
|
266
|
+
cerebro.add_signal(stype, sig, **kwargs)
|
|
267
|
+
|
|
268
|
+
# get and add strategies
|
|
269
|
+
strategies = getobjects(args.strategies, Strategy, strategies_module)
|
|
270
|
+
for strat, kwargs in strategies:
|
|
271
|
+
cerebro.addstrategy(strat, **kwargs)
|
|
272
|
+
|
|
273
|
+
inds = getobjects(args.indicators, Indicator, indicators_module)
|
|
274
|
+
for ind, kwargs in inds:
|
|
275
|
+
cerebro.addindicator(ind, **kwargs)
|
|
276
|
+
|
|
277
|
+
obs = getobjects(args.observers, Observer, observers_module)
|
|
278
|
+
for ob, kwargs in obs:
|
|
279
|
+
cerebro.addobserver(ob, **kwargs)
|
|
280
|
+
|
|
281
|
+
ans = getobjects(args.analyzers, Analyzer, analyzers_module)
|
|
282
|
+
for an, kwargs in ans:
|
|
283
|
+
cerebro.addanalyzer(an, **kwargs)
|
|
284
|
+
|
|
285
|
+
setbroker(args, cerebro)
|
|
286
|
+
|
|
287
|
+
for wrkwargs_str in args.writers or []:
|
|
288
|
+
wrkwargs = _safe_parse_kwargs(wrkwargs_str)
|
|
289
|
+
cerebro.addwriter(WriterFile, **wrkwargs)
|
|
290
|
+
|
|
291
|
+
ans = getfunctions(args.hooks, Cerebro)
|
|
292
|
+
for hook, kwargs in ans:
|
|
293
|
+
hook(cerebro, **kwargs)
|
|
294
|
+
runsts = cerebro.run()
|
|
295
|
+
runst = runsts[0] # single strategy and no optimization
|
|
296
|
+
|
|
297
|
+
if args.pranalyzer or args.ppranalyzer:
|
|
298
|
+
_print_analyzers(args, runst)
|
|
299
|
+
|
|
300
|
+
if args.plot:
|
|
301
|
+
pkwargs = {"style": "bar"}
|
|
302
|
+
if args.plot is not True:
|
|
303
|
+
# evaluates to True but is not "True" - args were passed
|
|
304
|
+
ekwargs = _safe_parse_kwargs(args.plot)
|
|
305
|
+
pkwargs.update(ekwargs)
|
|
306
|
+
|
|
307
|
+
# cerebro.plot(numfigs=args.plotfigs, style=args.plotstyle)
|
|
308
|
+
cerebro.plot(**pkwargs)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _add_datas(args, cerebro):
|
|
312
|
+
"""Add data feeds to cerebro, honoring --resample / --replay options."""
|
|
313
|
+
tf = cp = None
|
|
314
|
+
if args.resample is not None or args.replay is not None:
|
|
315
|
+
if args.resample is not None:
|
|
316
|
+
tfcp = args.resample.split(":")
|
|
317
|
+
elif args.replay is not None:
|
|
318
|
+
tfcp = args.replay.split(":")
|
|
319
|
+
|
|
320
|
+
# compression may be skipped and it will default to 1
|
|
321
|
+
if len(tfcp) == 1 or tfcp[1] == "":
|
|
322
|
+
tf, cp = tfcp[0], 1
|
|
323
|
+
else:
|
|
324
|
+
tf, cp = tfcp
|
|
325
|
+
|
|
326
|
+
cp = int(cp) # convert any value to int
|
|
327
|
+
tf = TIMEFRAMES.get(tf)
|
|
328
|
+
|
|
329
|
+
for data in getdatas(args):
|
|
330
|
+
if args.resample is not None:
|
|
331
|
+
cerebro.resampledata(data, timeframe=tf, compression=cp)
|
|
332
|
+
elif args.replay is not None:
|
|
333
|
+
cerebro.replaydata(data, timeframe=tf, compression=cp)
|
|
334
|
+
else:
|
|
335
|
+
cerebro.adddata(data)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _print_analyzers(args, runst):
|
|
339
|
+
"""Print analyzer results for the finished strategy (pranalyzer/ppranalyzer)."""
|
|
340
|
+
if not runst.analyzers:
|
|
341
|
+
return
|
|
342
|
+
print("====================")
|
|
343
|
+
print("== Analyzers")
|
|
344
|
+
print("====================")
|
|
345
|
+
for name, analyzer in runst.analyzers.getitems():
|
|
346
|
+
if args.pranalyzer:
|
|
347
|
+
analyzer.print()
|
|
348
|
+
elif args.ppranalyzer:
|
|
349
|
+
print("##########")
|
|
350
|
+
print(name)
|
|
351
|
+
print("##########")
|
|
352
|
+
analyzer.pprint()
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def setbroker(args, cerebro):
|
|
356
|
+
"""Configure broker settings from parsed command-line arguments.
|
|
357
|
+
|
|
358
|
+
Sets broker cash, commission scheme parameters, and slippage settings
|
|
359
|
+
on the cerebro instance's broker.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
args (argparse.Namespace): Parsed command-line arguments containing
|
|
363
|
+
broker configuration options like cash, commission, margin, mult,
|
|
364
|
+
interest, slippage, etc.
|
|
365
|
+
cerebro (Cerebro): Cerebro instance whose broker will be configured.
|
|
366
|
+
|
|
367
|
+
The following broker settings are configured:
|
|
368
|
+
- Cash: Initial capital via args.cash
|
|
369
|
+
- Commission: Trading commission via args.commission, args.margin, args.mult
|
|
370
|
+
- Interest: Credit interest rate via args.interest, args.interest_long
|
|
371
|
+
- Slippage: Price slippage model via args.slip_perc or args.slip_fixed
|
|
372
|
+
"""
|
|
373
|
+
broker = cerebro.getbroker()
|
|
374
|
+
|
|
375
|
+
if args.cash is not None:
|
|
376
|
+
broker.setcash(args.cash)
|
|
377
|
+
|
|
378
|
+
commkwargs = {}
|
|
379
|
+
if args.commission is not None:
|
|
380
|
+
commkwargs["commission"] = args.commission
|
|
381
|
+
if args.margin is not None:
|
|
382
|
+
commkwargs["margin"] = args.margin
|
|
383
|
+
if args.mult is not None:
|
|
384
|
+
commkwargs["mult"] = args.mult
|
|
385
|
+
if args.interest is not None:
|
|
386
|
+
commkwargs["interest"] = args.interest
|
|
387
|
+
if args.interest_long is not None:
|
|
388
|
+
commkwargs["interest_long"] = args.interest_long
|
|
389
|
+
|
|
390
|
+
if commkwargs:
|
|
391
|
+
broker.setcommission(**commkwargs)
|
|
392
|
+
|
|
393
|
+
if args.slip_perc is not None:
|
|
394
|
+
cerebro.broker.set_slippage_perc(
|
|
395
|
+
args.slip_perc,
|
|
396
|
+
slip_open=args.slip_open,
|
|
397
|
+
slip_match=not args.no_slip_match,
|
|
398
|
+
slip_out=args.slip_out,
|
|
399
|
+
)
|
|
400
|
+
elif args.slip_fixed is not None:
|
|
401
|
+
cerebro.broker.set_slippage_fixed(
|
|
402
|
+
args.slip_fixed,
|
|
403
|
+
slip_open=args.slip_open,
|
|
404
|
+
slip_match=not args.no_slip_match,
|
|
405
|
+
slip_out=args.slip_out,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def getdatas(args):
|
|
410
|
+
"""Create data feed instances from command-line arguments.
|
|
411
|
+
|
|
412
|
+
Parses the data format, date range, timeframe, and compression settings
|
|
413
|
+
from the arguments, then creates data feed instances for each specified
|
|
414
|
+
data file.
|
|
415
|
+
|
|
416
|
+
Args:
|
|
417
|
+
args (argparse.Namespace): Parsed command-line arguments containing
|
|
418
|
+
data configuration options including:
|
|
419
|
+
- data: List of data file paths
|
|
420
|
+
- format: Data format type (e.g., 'btcsv', 'yahoo')
|
|
421
|
+
- fromdate: Start date filter
|
|
422
|
+
- todate: End date filter
|
|
423
|
+
- timeframe: Timeframe for the data
|
|
424
|
+
- compression: Compression factor
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
list: List of data feed instances ready to be added to Cerebro.
|
|
428
|
+
"""
|
|
429
|
+
# Get the data feed class from the global dictionary
|
|
430
|
+
dfcls = DATAFORMATS[args.format]
|
|
431
|
+
|
|
432
|
+
# Prepare some args
|
|
433
|
+
dfkwargs = {}
|
|
434
|
+
if args.format == "yahoo_unreversed":
|
|
435
|
+
dfkwargs["reverse"] = True
|
|
436
|
+
|
|
437
|
+
fmtstr = "%Y-%m-%d"
|
|
438
|
+
if args.fromdate:
|
|
439
|
+
dtsplit = args.fromdate.split("T")
|
|
440
|
+
if len(dtsplit) > 1:
|
|
441
|
+
fmtstr += "T%H:%M:%S"
|
|
442
|
+
|
|
443
|
+
fromdate = datetime.datetime.strptime(args.fromdate, fmtstr)
|
|
444
|
+
dfkwargs["fromdate"] = fromdate
|
|
445
|
+
|
|
446
|
+
fmtstr = "%Y-%m-%d"
|
|
447
|
+
if args.todate:
|
|
448
|
+
dtsplit = args.todate.split("T")
|
|
449
|
+
if len(dtsplit) > 1:
|
|
450
|
+
fmtstr += "T%H:%M:%S"
|
|
451
|
+
todate = datetime.datetime.strptime(args.todate, fmtstr)
|
|
452
|
+
dfkwargs["todate"] = todate
|
|
453
|
+
|
|
454
|
+
if args.timeframe is not None:
|
|
455
|
+
dfkwargs["timeframe"] = TIMEFRAMES[args.timeframe]
|
|
456
|
+
|
|
457
|
+
if args.compression is not None:
|
|
458
|
+
dfkwargs["compression"] = args.compression
|
|
459
|
+
|
|
460
|
+
datas = []
|
|
461
|
+
for dname in args.data:
|
|
462
|
+
dfkwargs["dataname"] = dname
|
|
463
|
+
data = dfcls(**dfkwargs)
|
|
464
|
+
datas.append(data)
|
|
465
|
+
|
|
466
|
+
return datas
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def getmodclasses(mod, clstype, clsname=None):
|
|
470
|
+
"""Get classes from a module that match a specific type.
|
|
471
|
+
|
|
472
|
+
Searches through a module to find all classes that are subclasses of
|
|
473
|
+
a given type. Optionally filters by class name.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
mod (module): Python module to search for classes.
|
|
477
|
+
clstype (type): Base class type to filter by (e.g., Strategy, Indicator).
|
|
478
|
+
clsname (str, optional): Specific class name to find. If None,
|
|
479
|
+
returns all matching classes. Defaults to None.
|
|
480
|
+
|
|
481
|
+
Returns:
|
|
482
|
+
list: List of class objects that match the criteria. If clsname is
|
|
483
|
+
specified, returns a list with at most one element.
|
|
484
|
+
"""
|
|
485
|
+
clsmembers = inspect.getmembers(mod, inspect.isclass)
|
|
486
|
+
|
|
487
|
+
clslist = []
|
|
488
|
+
for name, cls in clsmembers:
|
|
489
|
+
if not issubclass(cls, clstype):
|
|
490
|
+
continue
|
|
491
|
+
|
|
492
|
+
if clsname:
|
|
493
|
+
if clsname == name:
|
|
494
|
+
clslist.append(cls)
|
|
495
|
+
break
|
|
496
|
+
else:
|
|
497
|
+
clslist.append(cls)
|
|
498
|
+
|
|
499
|
+
return clslist
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def getmodfunctions(mod, funcname=None):
|
|
503
|
+
"""Get functions from a module, optionally filtering by name.
|
|
504
|
+
|
|
505
|
+
Searches through a module to find all functions and methods. Optionally
|
|
506
|
+
filters by function name.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
mod (module): Python module to search for functions.
|
|
510
|
+
funcname (str, optional): Specific function name to find. If None,
|
|
511
|
+
returns all functions and methods. Defaults to None.
|
|
512
|
+
|
|
513
|
+
Returns:
|
|
514
|
+
list: List of function/method objects that match the criteria. If
|
|
515
|
+
funcname is specified, returns a list with at most one element.
|
|
516
|
+
"""
|
|
517
|
+
members = inspect.getmembers(mod, inspect.isfunction) + inspect.getmembers(
|
|
518
|
+
mod, inspect.ismethod
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
funclist = []
|
|
522
|
+
for name, member in members:
|
|
523
|
+
if funcname:
|
|
524
|
+
if name == funcname:
|
|
525
|
+
funclist.append(member)
|
|
526
|
+
break
|
|
527
|
+
else:
|
|
528
|
+
funclist.append(member)
|
|
529
|
+
|
|
530
|
+
return funclist
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def loadmodule(modpath, modname=""):
|
|
534
|
+
"""Load a Python module from a file path.
|
|
535
|
+
|
|
536
|
+
Dynamically loads a Python module from a file path. Supports both Python 2
|
|
537
|
+
and Python 3 using different loading mechanisms. If no module name is provided,
|
|
538
|
+
generates a random 10-character alphanumeric name.
|
|
539
|
+
|
|
540
|
+
Args:
|
|
541
|
+
modpath (str): Path to the Python module file. If it doesn't end with
|
|
542
|
+
'.py', the extension will be automatically appended.
|
|
543
|
+
modname (str, optional): Name to assign to the loaded module. If None or
|
|
544
|
+
empty, a random name is generated. Defaults to "".
|
|
545
|
+
|
|
546
|
+
Returns:
|
|
547
|
+
tuple: A tuple containing:
|
|
548
|
+
- mod (module or None): The loaded module object, or None if loading failed.
|
|
549
|
+
- e (Exception or None): Exception object if loading failed, None otherwise.
|
|
550
|
+
"""
|
|
551
|
+
# generate a random name for the module
|
|
552
|
+
|
|
553
|
+
if not modpath.endswith(".py"):
|
|
554
|
+
modpath += ".py"
|
|
555
|
+
|
|
556
|
+
if not modname:
|
|
557
|
+
chars = string.ascii_uppercase + string.digits
|
|
558
|
+
# Module name only needs to be unique, not cryptographically secure.
|
|
559
|
+
# This is an internal import alias, never a security boundary.
|
|
560
|
+
modname = "".join(random.choice(chars) for _ in range(10)) # nosec B311
|
|
561
|
+
|
|
562
|
+
try:
|
|
563
|
+
mod = _load_module_from_path(modpath, modname)
|
|
564
|
+
except Exception as e:
|
|
565
|
+
logger.warning("btrun:564 fallback on Exception")
|
|
566
|
+
return None, e
|
|
567
|
+
|
|
568
|
+
return mod, None
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _load_module_from_path(modpath, modname):
|
|
572
|
+
"""Load a Python module from a file path using importlib.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
modpath (str): Path to the Python module file.
|
|
576
|
+
modname (str): Name to assign to the loaded module.
|
|
577
|
+
|
|
578
|
+
Returns:
|
|
579
|
+
module: The loaded module object.
|
|
580
|
+
|
|
581
|
+
Raises:
|
|
582
|
+
Exception: If module loading fails.
|
|
583
|
+
"""
|
|
584
|
+
import importlib.util
|
|
585
|
+
|
|
586
|
+
spec = importlib.util.spec_from_file_location(modname, modpath)
|
|
587
|
+
mod = importlib.util.module_from_spec(spec)
|
|
588
|
+
spec.loader.exec_module(mod)
|
|
589
|
+
return mod
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def getobjects(iterable, clsbase, modbase, issignal=False):
|
|
593
|
+
"""Load and instantiate objects from module specifications.
|
|
594
|
+
|
|
595
|
+
Parses a list of object specifications in the format 'module:name:kwargs',
|
|
596
|
+
loads the corresponding modules, finds the requested classes, and returns
|
|
597
|
+
them with their associated kwargs. Used for strategies, indicators,
|
|
598
|
+
observers, analyzers, and signals.
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
iterable (list): List of object specification strings. Each string can be:
|
|
602
|
+
- 'module:name:kwargs' - Load specific class from module with kwargs
|
|
603
|
+
- 'module:name' - Load specific class from module
|
|
604
|
+
- 'module' - Load first matching class from module
|
|
605
|
+
- ':name' - Load class from built-in module (modbase)
|
|
606
|
+
- For signals: 'signaltype+module:name:kwargs'
|
|
607
|
+
clsbase (type): Base class type to filter by (e.g., Strategy, Indicator).
|
|
608
|
+
modbase (module): Default module to use when module path is omitted.
|
|
609
|
+
issignal (bool, optional): Whether processing signals. If True, parses
|
|
610
|
+
signal type prefix (e.g., 'longshort+'). Defaults to False.
|
|
611
|
+
|
|
612
|
+
Returns:
|
|
613
|
+
list: List of tuples containing:
|
|
614
|
+
- For signals: (class, kwargs_dict, signal_type)
|
|
615
|
+
- For others: (class, kwargs_dict)
|
|
616
|
+
|
|
617
|
+
The function will call sys.exit(1) if module loading or class finding fails.
|
|
618
|
+
"""
|
|
619
|
+
retobjects: list = []
|
|
620
|
+
|
|
621
|
+
for item in iterable or []:
|
|
622
|
+
if issignal:
|
|
623
|
+
sigtokens = item.split("+", 1)
|
|
624
|
+
if len(sigtokens) == 1: # no + seen
|
|
625
|
+
sigtype = "longshort"
|
|
626
|
+
else:
|
|
627
|
+
sigtype, item = sigtokens
|
|
628
|
+
|
|
629
|
+
tokens = item.split(":", 1)
|
|
630
|
+
|
|
631
|
+
if len(tokens) == 1:
|
|
632
|
+
modpath = tokens[0]
|
|
633
|
+
name = ""
|
|
634
|
+
kwargs: dict = {}
|
|
635
|
+
else:
|
|
636
|
+
modpath, name = tokens
|
|
637
|
+
kwtokens = name.split(":", 1)
|
|
638
|
+
if len(kwtokens) == 1:
|
|
639
|
+
# no '(' found
|
|
640
|
+
kwargs = {}
|
|
641
|
+
else:
|
|
642
|
+
name = kwtokens[0]
|
|
643
|
+
kwargs = _safe_parse_kwargs(kwtokens[1])
|
|
644
|
+
|
|
645
|
+
if modpath:
|
|
646
|
+
mod, e = loadmodule(modpath)
|
|
647
|
+
|
|
648
|
+
if not mod:
|
|
649
|
+
print("")
|
|
650
|
+
print("Failed to load module %s:" % modpath, e)
|
|
651
|
+
sys.exit(1)
|
|
652
|
+
else:
|
|
653
|
+
mod = modbase
|
|
654
|
+
|
|
655
|
+
loaded = getmodclasses(mod=mod, clstype=clsbase, clsname=name)
|
|
656
|
+
|
|
657
|
+
if not loaded:
|
|
658
|
+
print(f"No class {str(name)} / module {modpath}")
|
|
659
|
+
sys.exit(1)
|
|
660
|
+
|
|
661
|
+
if issignal:
|
|
662
|
+
retobjects.append((loaded[0], kwargs, sigtype))
|
|
663
|
+
else:
|
|
664
|
+
retobjects.append((loaded[0], kwargs))
|
|
665
|
+
|
|
666
|
+
return retobjects
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
def getfunctions(iterable, modbase):
|
|
670
|
+
"""Load and retrieve function objects from module specifications.
|
|
671
|
+
|
|
672
|
+
Parses a list of function specifications in the format 'module:name:kwargs',
|
|
673
|
+
loads the corresponding modules, finds the requested functions, and returns
|
|
674
|
+
them with their associated kwargs. Used for cerebro hook functions.
|
|
675
|
+
|
|
676
|
+
Args:
|
|
677
|
+
iterable (list): List of function specification strings. Each string can be:
|
|
678
|
+
- 'module:name:kwargs' - Load specific function from module with kwargs
|
|
679
|
+
- 'module:name' - Load specific function from module
|
|
680
|
+
- 'module' - Load first function from module
|
|
681
|
+
- ':name' - Load built-in cerebro method (e.g., ':addtz')
|
|
682
|
+
modbase (module): Default module to use when module path is omitted
|
|
683
|
+
(typically Cerebro for built-in methods).
|
|
684
|
+
|
|
685
|
+
Returns:
|
|
686
|
+
list: List of tuples containing:
|
|
687
|
+
- (function, kwargs_dict): Function object and its keyword arguments
|
|
688
|
+
|
|
689
|
+
The function will call sys.exit(1) if module loading or function finding fails.
|
|
690
|
+
"""
|
|
691
|
+
retfunctions = []
|
|
692
|
+
|
|
693
|
+
for item in iterable or []:
|
|
694
|
+
tokens = item.split(":", 1)
|
|
695
|
+
|
|
696
|
+
if len(tokens) == 1:
|
|
697
|
+
modpath = tokens[0]
|
|
698
|
+
name = ""
|
|
699
|
+
kwargs: dict = {}
|
|
700
|
+
else:
|
|
701
|
+
modpath, name = tokens
|
|
702
|
+
kwtokens = name.split(":", 1)
|
|
703
|
+
if len(kwtokens) == 1:
|
|
704
|
+
# no '(' found
|
|
705
|
+
kwargs = {}
|
|
706
|
+
else:
|
|
707
|
+
name = kwtokens[0]
|
|
708
|
+
kwargs = _safe_parse_kwargs(kwtokens[1])
|
|
709
|
+
|
|
710
|
+
if modpath:
|
|
711
|
+
mod, e = loadmodule(modpath)
|
|
712
|
+
|
|
713
|
+
if not mod:
|
|
714
|
+
print("")
|
|
715
|
+
print("Failed to load module %s:" % modpath, e)
|
|
716
|
+
sys.exit(1)
|
|
717
|
+
else:
|
|
718
|
+
mod = modbase
|
|
719
|
+
|
|
720
|
+
loaded = getmodfunctions(mod=mod, funcname=name)
|
|
721
|
+
|
|
722
|
+
if not loaded:
|
|
723
|
+
print(f"No function {str(name)} / module {modpath}")
|
|
724
|
+
sys.exit(1)
|
|
725
|
+
|
|
726
|
+
retfunctions.append((loaded[0], kwargs))
|
|
727
|
+
|
|
728
|
+
return retfunctions
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
def parse_args(pargs=""):
|
|
732
|
+
"""Parse command-line arguments for the btrun script.
|
|
733
|
+
|
|
734
|
+
Creates an argument parser and defines all command-line options for the
|
|
735
|
+
backtrader runner, including data feeds, strategies, indicators, observers,
|
|
736
|
+
analyzers, signals, broker settings, and output options.
|
|
737
|
+
|
|
738
|
+
Args:
|
|
739
|
+
pargs (str or list, optional): Command-line arguments to parse. If empty
|
|
740
|
+
string, parses from sys.argv. Can be a string or list of strings.
|
|
741
|
+
Defaults to "".
|
|
742
|
+
|
|
743
|
+
Returns:
|
|
744
|
+
argparse.Namespace: Parsed command-line arguments object containing all
|
|
745
|
+
the configuration options for the backtest.
|
|
746
|
+
|
|
747
|
+
The parser defines the following argument groups:
|
|
748
|
+
- Data options: --data, --format, --fromdate, --todate, --timeframe,
|
|
749
|
+
--compression, --resample, --replay
|
|
750
|
+
- Cerebro options: --cerebro, --nostdstats
|
|
751
|
+
- Strategy options: --strategy
|
|
752
|
+
- Signals: --signal
|
|
753
|
+
- Observers and statistics: --observer
|
|
754
|
+
- Analyzers: --analyzer, --pranalyzer, --ppranalyzer
|
|
755
|
+
- Indicators: --indicator
|
|
756
|
+
- Writers: --writer
|
|
757
|
+
- Cash and Commission: --cash, --commission, --margin, --mult,
|
|
758
|
+
--interest, --interest_long
|
|
759
|
+
- Slippage: --slip_perc, --slip_fixed, --slip_open, --no-slip_match,
|
|
760
|
+
--slip_out
|
|
761
|
+
- Output: --flush, --plot
|
|
762
|
+
- Hooks: --hook
|
|
763
|
+
"""
|
|
764
|
+
parser = argparse.ArgumentParser(
|
|
765
|
+
description="Backtrader Run Script",
|
|
766
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
group = parser.add_argument_group(title="Data options")
|
|
770
|
+
# Data options
|
|
771
|
+
group.add_argument(
|
|
772
|
+
"--data", "-d", action="append", required=True, help="Data files to be added to the system"
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
group = parser.add_argument_group(title="Cerebro options")
|
|
776
|
+
group.add_argument(
|
|
777
|
+
"--cerebro",
|
|
778
|
+
"-cer",
|
|
779
|
+
metavar="kwargs",
|
|
780
|
+
required=False,
|
|
781
|
+
const="",
|
|
782
|
+
default="",
|
|
783
|
+
nargs="?",
|
|
784
|
+
help=(
|
|
785
|
+
"The argument can be specified with the following form:\n"
|
|
786
|
+
"\n"
|
|
787
|
+
" - kwargs\n"
|
|
788
|
+
"\n"
|
|
789
|
+
' Example: "preload=True" which set its to True\n'
|
|
790
|
+
"\n"
|
|
791
|
+
"The passed kwargs will be passed directly to the cerebro\n"
|
|
792
|
+
"instance created for the execution\n"
|
|
793
|
+
"\n"
|
|
794
|
+
"The available kwargs to cerebro are:\n"
|
|
795
|
+
" - preload (default: True)\n"
|
|
796
|
+
" - runonce (default: True)\n"
|
|
797
|
+
" - maxcpus (default: None)\n"
|
|
798
|
+
" - stdstats (default: True)\n"
|
|
799
|
+
" - live (default: False)\n"
|
|
800
|
+
" - exactbars (default: False)\n"
|
|
801
|
+
" - preload (default: True)\n"
|
|
802
|
+
" - writer (default False)\n"
|
|
803
|
+
" - oldbuysell (default False)\n"
|
|
804
|
+
" - tradehistory (default False)\n"
|
|
805
|
+
),
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
group.add_argument(
|
|
809
|
+
"--nostdstats", action="store_true", help="Disable the standard statistics observers"
|
|
810
|
+
)
|
|
811
|
+
|
|
812
|
+
datakeys = list(DATAFORMATS)
|
|
813
|
+
group.add_argument(
|
|
814
|
+
"--format",
|
|
815
|
+
"--csvformat",
|
|
816
|
+
"-c",
|
|
817
|
+
required=False,
|
|
818
|
+
default="btcsv",
|
|
819
|
+
choices=datakeys,
|
|
820
|
+
help="CSV Format",
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
group.add_argument(
|
|
824
|
+
"--fromdate",
|
|
825
|
+
"-f",
|
|
826
|
+
required=False,
|
|
827
|
+
default=None,
|
|
828
|
+
help="Starting date in YYYY-MM-DD[THH:MM:SS] format",
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
group.add_argument(
|
|
832
|
+
"--todate",
|
|
833
|
+
"-t",
|
|
834
|
+
required=False,
|
|
835
|
+
default=None,
|
|
836
|
+
help="Ending date in YYYY-MM-DD[THH:MM:SS] format",
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
group.add_argument(
|
|
840
|
+
"--timeframe",
|
|
841
|
+
"-tf",
|
|
842
|
+
required=False,
|
|
843
|
+
default="days",
|
|
844
|
+
choices=TIMEFRAMES.keys(),
|
|
845
|
+
help="Ending date in YYYY-MM-DD[THH:MM:SS] format",
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
group.add_argument(
|
|
849
|
+
"--compression",
|
|
850
|
+
"-cp",
|
|
851
|
+
required=False,
|
|
852
|
+
default=1,
|
|
853
|
+
type=int,
|
|
854
|
+
help="Ending date in YYYY-MM-DD[THH:MM:SS] format",
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
group = parser.add_mutually_exclusive_group(required=False)
|
|
858
|
+
|
|
859
|
+
group.add_argument(
|
|
860
|
+
"--resample",
|
|
861
|
+
"-rs",
|
|
862
|
+
required=False,
|
|
863
|
+
default=None,
|
|
864
|
+
help="resample with timeframe:compression values",
|
|
865
|
+
)
|
|
866
|
+
|
|
867
|
+
group.add_argument(
|
|
868
|
+
"--replay",
|
|
869
|
+
"-rp",
|
|
870
|
+
required=False,
|
|
871
|
+
default=None,
|
|
872
|
+
help="replay with timeframe:compression values",
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
group.add_argument(
|
|
876
|
+
"--hook",
|
|
877
|
+
dest="hooks",
|
|
878
|
+
action="append",
|
|
879
|
+
required=False,
|
|
880
|
+
metavar="module:hookfunction:kwargs",
|
|
881
|
+
help=(
|
|
882
|
+
"This option can be specified multiple times.\n"
|
|
883
|
+
"\n"
|
|
884
|
+
"The argument can be specified with the following form:\n"
|
|
885
|
+
"\n"
|
|
886
|
+
" - module:hookfunction:kwargs\n"
|
|
887
|
+
"\n"
|
|
888
|
+
" Example: mymod:myhook:a=1,b=2\n"
|
|
889
|
+
"\n"
|
|
890
|
+
"kwargs is optional\n"
|
|
891
|
+
"\n"
|
|
892
|
+
"If module is omitted then hookfunction will be sought\n"
|
|
893
|
+
"as the built-in cerebro method. Example:\n"
|
|
894
|
+
"\n"
|
|
895
|
+
" - :addtz:tz=America/St_Johns\n"
|
|
896
|
+
"\n"
|
|
897
|
+
"If name is omitted, then the 1st function found in the\n"
|
|
898
|
+
"mod will be used. Such as in:\n"
|
|
899
|
+
"\n"
|
|
900
|
+
" - module or module::kwargs\n"
|
|
901
|
+
"\n"
|
|
902
|
+
"The function specified will be called, with cerebro\n"
|
|
903
|
+
"instance passed as the first argument together with\n"
|
|
904
|
+
"kwargs, if any were specified. This allows to customize\n"
|
|
905
|
+
"cerebro, beyond options provided by this script\n\n"
|
|
906
|
+
),
|
|
907
|
+
)
|
|
908
|
+
|
|
909
|
+
# Module where to read the strategy from
|
|
910
|
+
group = parser.add_argument_group(title="Strategy options")
|
|
911
|
+
group.add_argument(
|
|
912
|
+
"--strategy",
|
|
913
|
+
"-st",
|
|
914
|
+
dest="strategies",
|
|
915
|
+
action="append",
|
|
916
|
+
required=False,
|
|
917
|
+
metavar="module:name:kwargs",
|
|
918
|
+
help=(
|
|
919
|
+
"This option can be specified multiple times.\n"
|
|
920
|
+
"\n"
|
|
921
|
+
"The argument can be specified with the following form:\n"
|
|
922
|
+
"\n"
|
|
923
|
+
" - module:classname:kwargs\n"
|
|
924
|
+
"\n"
|
|
925
|
+
" Example: mymod:myclass:a=1,b=2\n"
|
|
926
|
+
"\n"
|
|
927
|
+
"kwargs is optional\n"
|
|
928
|
+
"\n"
|
|
929
|
+
"If module is omitted then class name will be sought in\n"
|
|
930
|
+
"the built-in strategies module. Such as in:\n"
|
|
931
|
+
"\n"
|
|
932
|
+
" - :name:kwargs or :name\n"
|
|
933
|
+
"\n"
|
|
934
|
+
"If name is omitted, then the 1st strategy found in the mod\n"
|
|
935
|
+
"will be used. Such as in:\n"
|
|
936
|
+
"\n"
|
|
937
|
+
" - module or module::kwargs"
|
|
938
|
+
),
|
|
939
|
+
)
|
|
940
|
+
|
|
941
|
+
# Module where to read the strategy from
|
|
942
|
+
group = parser.add_argument_group(title="Signals")
|
|
943
|
+
group.add_argument(
|
|
944
|
+
"--signal",
|
|
945
|
+
"-sig",
|
|
946
|
+
dest="signals",
|
|
947
|
+
action="append",
|
|
948
|
+
required=False,
|
|
949
|
+
metavar="module:signaltype:name:kwargs",
|
|
950
|
+
help=(
|
|
951
|
+
"This option can be specified multiple times.\n"
|
|
952
|
+
"\n"
|
|
953
|
+
"The argument can be specified with the following form:\n"
|
|
954
|
+
"\n"
|
|
955
|
+
" - signaltype:module:signaltype:classname:kwargs\n"
|
|
956
|
+
"\n"
|
|
957
|
+
" Example: longshort+mymod:myclass:a=1,b=2\n"
|
|
958
|
+
"\n"
|
|
959
|
+
"signaltype may be ommited: longshort will be used\n"
|
|
960
|
+
"\n"
|
|
961
|
+
" Example: mymod:myclass:a=1,b=2\n"
|
|
962
|
+
"\n"
|
|
963
|
+
"kwargs is optional\n"
|
|
964
|
+
"\n"
|
|
965
|
+
"signaltype will be uppercased to match the defintions\n"
|
|
966
|
+
"fromt the backtrader.signal module\n"
|
|
967
|
+
"\n"
|
|
968
|
+
"If module is omitted then class name will be sought in\n"
|
|
969
|
+
"the built-in signals module. Such as in:\n"
|
|
970
|
+
"\n"
|
|
971
|
+
" - LONGSHORT::name:kwargs or :name\n"
|
|
972
|
+
"\n"
|
|
973
|
+
"If name is omitted, then the 1st signal found in the mod\n"
|
|
974
|
+
"will be used. Such as in:\n"
|
|
975
|
+
"\n"
|
|
976
|
+
" - module or module:::kwargs"
|
|
977
|
+
),
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
# Observers
|
|
981
|
+
group = parser.add_argument_group(title="Observers and statistics")
|
|
982
|
+
group.add_argument(
|
|
983
|
+
"--observer",
|
|
984
|
+
"-ob",
|
|
985
|
+
dest="observers",
|
|
986
|
+
action="append",
|
|
987
|
+
required=False,
|
|
988
|
+
metavar="module:name:kwargs",
|
|
989
|
+
help=(
|
|
990
|
+
"This option can be specified multiple times.\n"
|
|
991
|
+
"\n"
|
|
992
|
+
"The argument can be specified with the following form:\n"
|
|
993
|
+
"\n"
|
|
994
|
+
" - module:classname:kwargs\n"
|
|
995
|
+
"\n"
|
|
996
|
+
" Example: mymod:myclass:a=1,b=2\n"
|
|
997
|
+
"\n"
|
|
998
|
+
"kwargs is optional\n"
|
|
999
|
+
"\n"
|
|
1000
|
+
"If module is omitted then class name will be sought in\n"
|
|
1001
|
+
"the built-in observers module. Such as in:\n"
|
|
1002
|
+
"\n"
|
|
1003
|
+
" - :name:kwargs or :name\n"
|
|
1004
|
+
"\n"
|
|
1005
|
+
"If name is omitted, then the 1st observer found in the\n"
|
|
1006
|
+
"will be used. Such as in:\n"
|
|
1007
|
+
"\n"
|
|
1008
|
+
" - module or module::kwargs"
|
|
1009
|
+
),
|
|
1010
|
+
)
|
|
1011
|
+
# Analyzers
|
|
1012
|
+
group = parser.add_argument_group(title="Analyzers")
|
|
1013
|
+
group.add_argument(
|
|
1014
|
+
"--analyzer",
|
|
1015
|
+
"-an",
|
|
1016
|
+
dest="analyzers",
|
|
1017
|
+
action="append",
|
|
1018
|
+
required=False,
|
|
1019
|
+
metavar="module:name:kwargs",
|
|
1020
|
+
help=(
|
|
1021
|
+
"This option can be specified multiple times.\n"
|
|
1022
|
+
"\n"
|
|
1023
|
+
"The argument can be specified with the following form:\n"
|
|
1024
|
+
"\n"
|
|
1025
|
+
" - module:classname:kwargs\n"
|
|
1026
|
+
"\n"
|
|
1027
|
+
" Example: mymod:myclass:a=1,b=2\n"
|
|
1028
|
+
"\n"
|
|
1029
|
+
"kwargs is optional\n"
|
|
1030
|
+
"\n"
|
|
1031
|
+
"If module is omitted then class name will be sought in\n"
|
|
1032
|
+
"the built-in analyzers module. Such as in:\n"
|
|
1033
|
+
"\n"
|
|
1034
|
+
" - :name:kwargs or :name\n"
|
|
1035
|
+
"\n"
|
|
1036
|
+
"If name is omitted, then the 1st analyzer found in the\n"
|
|
1037
|
+
"will be used. Such as in:\n"
|
|
1038
|
+
"\n"
|
|
1039
|
+
" - module or module::kwargs"
|
|
1040
|
+
),
|
|
1041
|
+
)
|
|
1042
|
+
|
|
1043
|
+
# Analyzer - Print
|
|
1044
|
+
group = parser.add_mutually_exclusive_group(required=False)
|
|
1045
|
+
group.add_argument(
|
|
1046
|
+
"--pranalyzer",
|
|
1047
|
+
"-pralyzer",
|
|
1048
|
+
required=False,
|
|
1049
|
+
action="store_true",
|
|
1050
|
+
help="Automatically print analyzers",
|
|
1051
|
+
)
|
|
1052
|
+
|
|
1053
|
+
group.add_argument(
|
|
1054
|
+
"--ppranalyzer",
|
|
1055
|
+
"-ppralyzer",
|
|
1056
|
+
required=False,
|
|
1057
|
+
action="store_true",
|
|
1058
|
+
help="Automatically PRETTY print analyzers",
|
|
1059
|
+
)
|
|
1060
|
+
|
|
1061
|
+
# Indicators
|
|
1062
|
+
group = parser.add_argument_group(title="Indicators")
|
|
1063
|
+
group.add_argument(
|
|
1064
|
+
"--indicator",
|
|
1065
|
+
"-ind",
|
|
1066
|
+
dest="indicators",
|
|
1067
|
+
metavar="module:name:kwargs",
|
|
1068
|
+
action="append",
|
|
1069
|
+
required=False,
|
|
1070
|
+
help=(
|
|
1071
|
+
"This option can be specified multiple times.\n"
|
|
1072
|
+
"\n"
|
|
1073
|
+
"The argument can be specified with the following form:\n"
|
|
1074
|
+
"\n"
|
|
1075
|
+
" - module:classname:kwargs\n"
|
|
1076
|
+
"\n"
|
|
1077
|
+
" Example: mymod:myclass:a=1,b=2\n"
|
|
1078
|
+
"\n"
|
|
1079
|
+
"kwargs is optional\n"
|
|
1080
|
+
"\n"
|
|
1081
|
+
"If module is omitted then class name will be sought in\n"
|
|
1082
|
+
"the built-in analyzers module. Such as in:\n"
|
|
1083
|
+
"\n"
|
|
1084
|
+
" - :name:kwargs or :name\n"
|
|
1085
|
+
"\n"
|
|
1086
|
+
"If name is omitted, then the 1st analyzer found in the\n"
|
|
1087
|
+
"will be used. Such as in:\n"
|
|
1088
|
+
"\n"
|
|
1089
|
+
" - module or module::kwargs"
|
|
1090
|
+
),
|
|
1091
|
+
)
|
|
1092
|
+
|
|
1093
|
+
# Writer
|
|
1094
|
+
group = parser.add_argument_group(title="Writers")
|
|
1095
|
+
group.add_argument(
|
|
1096
|
+
"--writer",
|
|
1097
|
+
"-wr",
|
|
1098
|
+
dest="writers",
|
|
1099
|
+
metavar="kwargs",
|
|
1100
|
+
nargs="?",
|
|
1101
|
+
action="append",
|
|
1102
|
+
required=False,
|
|
1103
|
+
const="",
|
|
1104
|
+
help=(
|
|
1105
|
+
"This option can be specified multiple times.\n"
|
|
1106
|
+
"\n"
|
|
1107
|
+
"The argument can be specified with the following form:\n"
|
|
1108
|
+
"\n"
|
|
1109
|
+
" - kwargs\n"
|
|
1110
|
+
"\n"
|
|
1111
|
+
" Example: a=1,b=2\n"
|
|
1112
|
+
"\n"
|
|
1113
|
+
"kwargs is optional\n"
|
|
1114
|
+
"\n"
|
|
1115
|
+
"It creates a system wide writer which outputs run data\n"
|
|
1116
|
+
"\n"
|
|
1117
|
+
"Please see the documentation for the available kwargs"
|
|
1118
|
+
),
|
|
1119
|
+
)
|
|
1120
|
+
|
|
1121
|
+
# Broker/Commissions
|
|
1122
|
+
group = parser.add_argument_group(title="Cash and Commission Scheme Args")
|
|
1123
|
+
group.add_argument(
|
|
1124
|
+
"--cash", "-cash", required=False, type=float, help="Cash to set to the broker"
|
|
1125
|
+
)
|
|
1126
|
+
group.add_argument(
|
|
1127
|
+
"--commission", "-comm", required=False, type=float, help="Commission value to set"
|
|
1128
|
+
)
|
|
1129
|
+
group.add_argument("--margin", "-marg", required=False, type=float, help="Margin type to set")
|
|
1130
|
+
group.add_argument("--mult", "-mul", required=False, type=float, help="Multiplier to use")
|
|
1131
|
+
|
|
1132
|
+
group.add_argument(
|
|
1133
|
+
"--interest",
|
|
1134
|
+
required=False,
|
|
1135
|
+
type=float,
|
|
1136
|
+
default=None,
|
|
1137
|
+
help="Credit Interest rate to apply (0.0x)",
|
|
1138
|
+
)
|
|
1139
|
+
|
|
1140
|
+
group.add_argument(
|
|
1141
|
+
"--interest_long",
|
|
1142
|
+
action="store_true",
|
|
1143
|
+
required=False,
|
|
1144
|
+
default=None,
|
|
1145
|
+
help="Apply credit interest to long positions",
|
|
1146
|
+
)
|
|
1147
|
+
|
|
1148
|
+
group.add_argument(
|
|
1149
|
+
"--slip_perc",
|
|
1150
|
+
required=False,
|
|
1151
|
+
default=None,
|
|
1152
|
+
type=float,
|
|
1153
|
+
help="Enable slippage with a percentage value",
|
|
1154
|
+
)
|
|
1155
|
+
group.add_argument(
|
|
1156
|
+
"--slip_fixed",
|
|
1157
|
+
required=False,
|
|
1158
|
+
default=None,
|
|
1159
|
+
type=float,
|
|
1160
|
+
help="Enable slippage with a fixed point value",
|
|
1161
|
+
)
|
|
1162
|
+
|
|
1163
|
+
group.add_argument(
|
|
1164
|
+
"--slip_open",
|
|
1165
|
+
required=False,
|
|
1166
|
+
action="store_true",
|
|
1167
|
+
help="enable slippage for when matching opening prices",
|
|
1168
|
+
)
|
|
1169
|
+
|
|
1170
|
+
group.add_argument(
|
|
1171
|
+
"--no-slip_match",
|
|
1172
|
+
required=False,
|
|
1173
|
+
action="store_true",
|
|
1174
|
+
help=(
|
|
1175
|
+
"Disable slip_match, ie: matching capped at \nhigh-low if slippage goes over those limits"
|
|
1176
|
+
),
|
|
1177
|
+
)
|
|
1178
|
+
group.add_argument(
|
|
1179
|
+
"--slip_out",
|
|
1180
|
+
required=False,
|
|
1181
|
+
action="store_true",
|
|
1182
|
+
help="with slip_match enabled, match outside high-low",
|
|
1183
|
+
)
|
|
1184
|
+
|
|
1185
|
+
# Output flushing
|
|
1186
|
+
group.add_argument(
|
|
1187
|
+
"--flush",
|
|
1188
|
+
required=False,
|
|
1189
|
+
action="store_true",
|
|
1190
|
+
help="flush the output - useful under win32 systems",
|
|
1191
|
+
)
|
|
1192
|
+
|
|
1193
|
+
# Plot options
|
|
1194
|
+
parser.add_argument(
|
|
1195
|
+
"--plot",
|
|
1196
|
+
"-p",
|
|
1197
|
+
nargs="?",
|
|
1198
|
+
metavar="kwargs",
|
|
1199
|
+
default=False,
|
|
1200
|
+
const=True,
|
|
1201
|
+
required=False,
|
|
1202
|
+
help=(
|
|
1203
|
+
"Plot the read data applying any kwargs passed\n"
|
|
1204
|
+
"\n"
|
|
1205
|
+
"For example:\n"
|
|
1206
|
+
"\n"
|
|
1207
|
+
' --plot style="candle" (to plot candlesticks)\n'
|
|
1208
|
+
),
|
|
1209
|
+
)
|
|
1210
|
+
|
|
1211
|
+
if pargs:
|
|
1212
|
+
return parser.parse_args(pargs)
|
|
1213
|
+
|
|
1214
|
+
return parser.parse_args()
|
|
1215
|
+
|
|
1216
|
+
|
|
1217
|
+
if __name__ == "__main__":
|
|
1218
|
+
btrun()
|