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/order.py
ADDED
|
@@ -0,0 +1,1114 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Backtrader Order Module.
|
|
3
|
+
|
|
4
|
+
This module provides order data structures and execution tracking.
|
|
5
|
+
|
|
6
|
+
Key Classes:
|
|
7
|
+
OrderExecutionBit: Holds information about a single order execution.
|
|
8
|
+
OrderData: Holds the full order data including creation and execution details.
|
|
9
|
+
|
|
10
|
+
The order system supports:
|
|
11
|
+
- Order creation and execution tracking
|
|
12
|
+
- Partial execution handling
|
|
13
|
+
- Commission calculation
|
|
14
|
+
- PnL calculation for closed positions
|
|
15
|
+
- Position size and price tracking
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import collections
|
|
19
|
+
import datetime
|
|
20
|
+
import itertools
|
|
21
|
+
from copy import copy
|
|
22
|
+
from typing import Optional
|
|
23
|
+
|
|
24
|
+
from .utils import AutoOrderedDict
|
|
25
|
+
from .utils.py3 import iteritems, range
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Store order execution related information. This information does not determine if the order is fully or partially executed, it only stores the information
|
|
29
|
+
class OrderExecutionBit:
|
|
30
|
+
"""
|
|
31
|
+
Intended to hold information about order execution. A "bit" does not
|
|
32
|
+
determine if the order has been fully/partially executed, it just holds
|
|
33
|
+
information.
|
|
34
|
+
|
|
35
|
+
Member Attributes:
|
|
36
|
+
|
|
37
|
+
- dt: datetime (float) execution time
|
|
38
|
+
# Execution time, float
|
|
39
|
+
- size: how much was executed
|
|
40
|
+
# How much was executed
|
|
41
|
+
- price: execution price
|
|
42
|
+
# Execution price
|
|
43
|
+
- closed: how much of the execution closed an existing position
|
|
44
|
+
# How much of existing position was closed
|
|
45
|
+
- opened: how much of the execution opened a new position
|
|
46
|
+
# How much new position was opened
|
|
47
|
+
- openedvalue: market value of the "opened" part
|
|
48
|
+
# Market value of opened position
|
|
49
|
+
- closedvalue: market value of the "closed" part
|
|
50
|
+
# Market value of closed position part
|
|
51
|
+
- closedcomm: commission for the "closed" part
|
|
52
|
+
# Commission for closed position part
|
|
53
|
+
- openedcomm: commission for the "opened" part
|
|
54
|
+
# Commission for opened position part
|
|
55
|
+
- value: market value for the entire bit size
|
|
56
|
+
# Market value of entire position
|
|
57
|
+
- comm: commission for the entire bit execution
|
|
58
|
+
# Commission for entire position
|
|
59
|
+
- pnl: pnl generated by this bit (if something was closed)
|
|
60
|
+
# PnL from closing part of position
|
|
61
|
+
- psize: current open position size
|
|
62
|
+
# Size of already opened position
|
|
63
|
+
- pprice: current open position price
|
|
64
|
+
# Price of already opened position
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
# Initialize order execution information
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
dt=None,
|
|
72
|
+
size=0,
|
|
73
|
+
price=0.0,
|
|
74
|
+
closed=0,
|
|
75
|
+
closedvalue=0.0,
|
|
76
|
+
closedcomm=0.0,
|
|
77
|
+
opened=0,
|
|
78
|
+
openedvalue=0.0,
|
|
79
|
+
openedcomm=0.0,
|
|
80
|
+
pnl=0.0,
|
|
81
|
+
psize=0,
|
|
82
|
+
pprice=0.0,
|
|
83
|
+
):
|
|
84
|
+
"""Initialize order execution bit information.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
dt: Execution datetime.
|
|
88
|
+
size: Executed size.
|
|
89
|
+
price: Execution price.
|
|
90
|
+
closed: Size of position closed.
|
|
91
|
+
closedvalue: Value of closed position.
|
|
92
|
+
closedcomm: Commission for closed position.
|
|
93
|
+
opened: Size of new position opened.
|
|
94
|
+
openedvalue: Value of opened position.
|
|
95
|
+
openedcomm: Commission for opened position.
|
|
96
|
+
pnl: Profit/loss from closed position.
|
|
97
|
+
psize: Current position size.
|
|
98
|
+
pprice: Current position price.
|
|
99
|
+
"""
|
|
100
|
+
self.dt = dt
|
|
101
|
+
self.size = size
|
|
102
|
+
self.price = price
|
|
103
|
+
|
|
104
|
+
self.closed = closed
|
|
105
|
+
self.opened = opened
|
|
106
|
+
self.closedvalue = closedvalue
|
|
107
|
+
self.openedvalue = openedvalue
|
|
108
|
+
self.closedcomm = closedcomm
|
|
109
|
+
self.openedcomm = openedcomm
|
|
110
|
+
|
|
111
|
+
self.value = closedvalue + openedvalue
|
|
112
|
+
self.comm = closedcomm + openedcomm
|
|
113
|
+
self.pnl = pnl
|
|
114
|
+
|
|
115
|
+
self.psize = psize
|
|
116
|
+
self.pprice = pprice
|
|
117
|
+
|
|
118
|
+
def __repr__(self):
|
|
119
|
+
return (
|
|
120
|
+
f"OrderExecutionBit(size={self.size}, price={self.price}, "
|
|
121
|
+
f"closed={self.closed}, opened={self.opened}, pnl={self.pnl})"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# Store actual order information for creation and execution. When creating, it requests creation; when executing, it produces the final result
|
|
126
|
+
class OrderData:
|
|
127
|
+
"""
|
|
128
|
+
Holds actual order data for Creation and Execution.
|
|
129
|
+
|
|
130
|
+
In the case of Creation the request made and in the case of Execution the
|
|
131
|
+
actual outcome.
|
|
132
|
+
|
|
133
|
+
Member Attributes:
|
|
134
|
+
|
|
135
|
+
- exbits : iterable of OrderExecutionBits for this OrderData
|
|
136
|
+
# Serialized order execution information for this order
|
|
137
|
+
- dt: datetime (float) creation/execution time
|
|
138
|
+
# Order creation or execution time, string format
|
|
139
|
+
- size: requested/executed size
|
|
140
|
+
# Creation or execution size
|
|
141
|
+
- price: execution price
|
|
142
|
+
# Execution price. If no price or limit price is given, the order creation
|
|
143
|
+
# or current closing price will be used as reference
|
|
144
|
+
Note: if no price is given and no pricelimite is given, the closing
|
|
145
|
+
price at the time or order creation will be used as reference
|
|
146
|
+
- pricelimit: holds pricelimit for StopLimit (which has trigger first)
|
|
147
|
+
# Limit price for stop limit (triggered first)
|
|
148
|
+
- trailamount: absolute price distance in trailing stops
|
|
149
|
+
# Absolute price distance in trailing stop
|
|
150
|
+
- trailpercent: percentage price distance in trailing stops
|
|
151
|
+
# Percentage distance in trailing stop
|
|
152
|
+
- value: market value for the entire bit size
|
|
153
|
+
# Market value of entire position
|
|
154
|
+
- comm: commission for the entire bit execution
|
|
155
|
+
# Commission for entire position execution
|
|
156
|
+
- pnl: pnl generated by this bit (if something was closed)
|
|
157
|
+
# PnL after closing position
|
|
158
|
+
- margin: margin incurred by the Order (if any)
|
|
159
|
+
# Margin required for order
|
|
160
|
+
- psize: current open position size
|
|
161
|
+
# Current position size
|
|
162
|
+
- pprice: current open position price
|
|
163
|
+
# Current position price
|
|
164
|
+
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
# According to the docs, collections.deque is thread-safe with appends at
|
|
168
|
+
# both ends, there will be no pop (nowhere) and therefore to know which the
|
|
169
|
+
# new exbits are two indices are needed. At time of cloning (__copy__) the
|
|
170
|
+
# indices can be updated to match the previous end, and the new end
|
|
171
|
+
# (len(exbits)
|
|
172
|
+
# Example: start 0, 0 -> islice(exbits, 0, 0) -> []
|
|
173
|
+
# One added -> copy -> updated 0, 1 -> islice(exbits, 0, 1) -> [1 elem]
|
|
174
|
+
# Other added -> copy -> updated 1, 2 -> islice(exbits, 1, 2) -> [1 elem]
|
|
175
|
+
# "add" and "__copy__" happen always in the same thread (with all current
|
|
176
|
+
# implementations) and therefore no append will happen during a copy and
|
|
177
|
+
# the len of the exbits can be queried with no concerns about another
|
|
178
|
+
# thread making an append and with no need for a lock
|
|
179
|
+
|
|
180
|
+
def __init__(
|
|
181
|
+
self,
|
|
182
|
+
dt=None,
|
|
183
|
+
size=0,
|
|
184
|
+
price=0.0,
|
|
185
|
+
pricelimit=0.0,
|
|
186
|
+
remsize=0,
|
|
187
|
+
pclose=0.0,
|
|
188
|
+
trailamount=0.0,
|
|
189
|
+
trailpercent=0.0,
|
|
190
|
+
):
|
|
191
|
+
"""Initialize order data.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
dt: Order datetime.
|
|
195
|
+
size: Order size.
|
|
196
|
+
price: Order price.
|
|
197
|
+
pricelimit: Limit price for stop orders.
|
|
198
|
+
remsize: Remaining size to execute.
|
|
199
|
+
pclose: Previous close price.
|
|
200
|
+
trailamount: Trailing amount for stop orders.
|
|
201
|
+
trailpercent: Trailing percent for stop orders.
|
|
202
|
+
"""
|
|
203
|
+
self.pclose = pclose
|
|
204
|
+
self.exbits: collections.deque = collections.deque() # for historical purposes
|
|
205
|
+
self.p1, self.p2 = 0, 0 # indices to pending notifications
|
|
206
|
+
|
|
207
|
+
self.dt = dt
|
|
208
|
+
self.size = size
|
|
209
|
+
self.remsize = remsize
|
|
210
|
+
self.price = price
|
|
211
|
+
self.pricelimit = pricelimit
|
|
212
|
+
self.trailamount = trailamount
|
|
213
|
+
self.trailpercent = trailpercent
|
|
214
|
+
# If no limit price, use the price as limit price
|
|
215
|
+
if not pricelimit:
|
|
216
|
+
# if no pricelimit is given, use the given price
|
|
217
|
+
self.pricelimit = self.price
|
|
218
|
+
# If there is limit price but no price, price equals limit price
|
|
219
|
+
if pricelimit and not price:
|
|
220
|
+
# price must always be set if pricelimit is set ...
|
|
221
|
+
self.price = pricelimit
|
|
222
|
+
# Limit price
|
|
223
|
+
self.plimit = pricelimit
|
|
224
|
+
|
|
225
|
+
self.value = 0.0
|
|
226
|
+
self.comm = 0.0
|
|
227
|
+
self.margin = None
|
|
228
|
+
self.pnl = 0.0
|
|
229
|
+
|
|
230
|
+
self.psize = 0
|
|
231
|
+
self.pprice = 0
|
|
232
|
+
|
|
233
|
+
# Set plimit property
|
|
234
|
+
|
|
235
|
+
def _getplimit(self):
|
|
236
|
+
return self._plimit
|
|
237
|
+
|
|
238
|
+
def _setplimit(self, val):
|
|
239
|
+
self._plimit = val
|
|
240
|
+
|
|
241
|
+
plimit = property(_getplimit, _setplimit)
|
|
242
|
+
|
|
243
|
+
# Return length of execution information
|
|
244
|
+
def __len__(self):
|
|
245
|
+
return len(self.exbits)
|
|
246
|
+
|
|
247
|
+
# Get execution information value
|
|
248
|
+
def __getitem__(self, key):
|
|
249
|
+
return self.exbits[key]
|
|
250
|
+
|
|
251
|
+
# Add execution information
|
|
252
|
+
def add(
|
|
253
|
+
self,
|
|
254
|
+
dt,
|
|
255
|
+
size,
|
|
256
|
+
price,
|
|
257
|
+
closed=0,
|
|
258
|
+
closedvalue=0.0,
|
|
259
|
+
closedcomm=0.0,
|
|
260
|
+
opened=0,
|
|
261
|
+
openedvalue=0.0,
|
|
262
|
+
openedcomm=0.0,
|
|
263
|
+
pnl=0.0,
|
|
264
|
+
psize=0,
|
|
265
|
+
pprice=0.0,
|
|
266
|
+
):
|
|
267
|
+
"""Add execution information to this order.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
dt: Execution datetime.
|
|
271
|
+
size: Executed size.
|
|
272
|
+
price: Execution price.
|
|
273
|
+
closed: Size of position closed.
|
|
274
|
+
closedvalue: Value of closed position.
|
|
275
|
+
closedcomm: Commission for closed position.
|
|
276
|
+
opened: Size of new position opened.
|
|
277
|
+
openedvalue: Value of opened position.
|
|
278
|
+
openedcomm: Commission for opened position.
|
|
279
|
+
pnl: Profit/loss from closed position.
|
|
280
|
+
psize: Current position size.
|
|
281
|
+
pprice: Current position price.
|
|
282
|
+
"""
|
|
283
|
+
self.addbit(
|
|
284
|
+
OrderExecutionBit(
|
|
285
|
+
dt,
|
|
286
|
+
size,
|
|
287
|
+
price,
|
|
288
|
+
closed,
|
|
289
|
+
closedvalue,
|
|
290
|
+
closedcomm,
|
|
291
|
+
opened,
|
|
292
|
+
openedvalue,
|
|
293
|
+
openedcomm,
|
|
294
|
+
pnl,
|
|
295
|
+
psize,
|
|
296
|
+
pprice,
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# Adjust current attributes based on order execution
|
|
301
|
+
def addbit(self, exbit):
|
|
302
|
+
"""Store an execution bit and recalculate order values.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
exbit: OrderExecutionBit to add.
|
|
306
|
+
"""
|
|
307
|
+
# Stores an ExecutionBit and recalculates own values from ExBit
|
|
308
|
+
self.exbits.append(exbit)
|
|
309
|
+
|
|
310
|
+
self.remsize -= exbit.size
|
|
311
|
+
|
|
312
|
+
self.dt = exbit.dt
|
|
313
|
+
oldvalue = self.size * self.price
|
|
314
|
+
newvalue = exbit.size * exbit.price
|
|
315
|
+
self.size += exbit.size
|
|
316
|
+
self.price = (oldvalue + newvalue) / self.size if self.size else 0.0
|
|
317
|
+
self.value += exbit.value
|
|
318
|
+
self.comm += exbit.comm
|
|
319
|
+
self.pnl += exbit.pnl
|
|
320
|
+
self.psize = exbit.psize
|
|
321
|
+
self.pprice = exbit.pprice
|
|
322
|
+
|
|
323
|
+
# Get current pending execution information
|
|
324
|
+
def getpending(self):
|
|
325
|
+
"""Get list of pending execution bits.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
list: List of pending OrderExecutionBit objects.
|
|
329
|
+
"""
|
|
330
|
+
return list(self.iterpending())
|
|
331
|
+
|
|
332
|
+
# Slice order pending execution information, if p1 and p2 both equal 0, returns empty
|
|
333
|
+
def iterpending(self):
|
|
334
|
+
"""Iterate over pending execution bits.
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
iterator: Iterator over pending OrderExecutionBit objects.
|
|
338
|
+
"""
|
|
339
|
+
return itertools.islice(self.exbits, self.p1, self.p2)
|
|
340
|
+
|
|
341
|
+
# Mark which pending order execution information
|
|
342
|
+
def markpending(self):
|
|
343
|
+
"""Mark current execution bits as pending.
|
|
344
|
+
|
|
345
|
+
Rebuilds the indices to mark which exbits are pending in clone.
|
|
346
|
+
"""
|
|
347
|
+
# rebuild the indices to mark which exbits are pending in clone
|
|
348
|
+
self.p1, self.p2 = self.p2, len(self.exbits)
|
|
349
|
+
|
|
350
|
+
# Clone the object
|
|
351
|
+
def clone(self):
|
|
352
|
+
"""Clone the OrderData object.
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
OrderData: A cloned copy with marked pending bits.
|
|
356
|
+
"""
|
|
357
|
+
self.markpending()
|
|
358
|
+
obj = copy(self)
|
|
359
|
+
return obj
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
# Simple parameter container to replace metaclass functionality
|
|
363
|
+
class OrderParams:
|
|
364
|
+
"""Simple parameter container for Order classes.
|
|
365
|
+
|
|
366
|
+
Stores order parameters like owner, data, size, price, execution type, etc.
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
def __init__(self, **kwargs):
|
|
370
|
+
"""Initialize order parameters with defaults.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
**kwargs: Keyword arguments to override default parameters.
|
|
374
|
+
|
|
375
|
+
Raises:
|
|
376
|
+
AttributeError: If an invalid parameter is provided.
|
|
377
|
+
"""
|
|
378
|
+
# Default parameters
|
|
379
|
+
defaults = {
|
|
380
|
+
"owner": None,
|
|
381
|
+
"data": None,
|
|
382
|
+
"size": None,
|
|
383
|
+
"price": None,
|
|
384
|
+
"pricelimit": None,
|
|
385
|
+
"exectype": None,
|
|
386
|
+
"valid": None,
|
|
387
|
+
"tradeid": 0,
|
|
388
|
+
"oco": None,
|
|
389
|
+
"trailamount": None,
|
|
390
|
+
"trailpercent": None,
|
|
391
|
+
"parent": None,
|
|
392
|
+
"transmit": True,
|
|
393
|
+
"simulated": False,
|
|
394
|
+
"histnotify": False,
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
# Set defaults first
|
|
398
|
+
for key, value in defaults.items():
|
|
399
|
+
setattr(self, key, value)
|
|
400
|
+
|
|
401
|
+
# Override with provided kwargs
|
|
402
|
+
for key, value in kwargs.items():
|
|
403
|
+
if hasattr(self, key):
|
|
404
|
+
setattr(self, key, value)
|
|
405
|
+
else:
|
|
406
|
+
raise AttributeError(f"Invalid parameter: {key}")
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class OrderBase:
|
|
410
|
+
"""Base class for order objects.
|
|
411
|
+
|
|
412
|
+
Provides the foundation for all order types with common attributes
|
|
413
|
+
and methods for order tracking, status management, and execution.
|
|
414
|
+
|
|
415
|
+
Class Attributes:
|
|
416
|
+
DAY: Constant for day order identification.
|
|
417
|
+
Market, Close, Limit, Stop, StopLimit, StopTrail, StopTrailLimit: Order execution types.
|
|
418
|
+
Buy, Sell: Order direction types.
|
|
419
|
+
Created, Submitted, Accepted, Partial, Completed, Canceled, Expired, Margin, Rejected: Order status codes.
|
|
420
|
+
|
|
421
|
+
Attributes:
|
|
422
|
+
ref: Unique order reference number.
|
|
423
|
+
broker: Broker instance handling this order.
|
|
424
|
+
p: OrderParams instance containing order parameters.
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
# Basic parameters for orders - removed metaclass usage
|
|
428
|
+
# DAY currently represents empty time delta
|
|
429
|
+
DAY = datetime.timedelta() # constant for DAY order identification
|
|
430
|
+
|
|
431
|
+
# Time Restrictions for orders
|
|
432
|
+
# Time restrictions for orders
|
|
433
|
+
T_Close, T_Day, T_Date, T_None = range(4)
|
|
434
|
+
|
|
435
|
+
# Volume Restrictions for orders
|
|
436
|
+
# Volume restrictions for orders
|
|
437
|
+
V_None = range(1)
|
|
438
|
+
|
|
439
|
+
# Different order types, represented by different numbers
|
|
440
|
+
Market, Close, Limit, Stop, StopLimit, StopTrail, StopTrailLimit, Historical = range(8)
|
|
441
|
+
ExecTypes = [
|
|
442
|
+
"Market",
|
|
443
|
+
"Close",
|
|
444
|
+
"Limit",
|
|
445
|
+
"Stop",
|
|
446
|
+
"StopLimit",
|
|
447
|
+
"StopTrail",
|
|
448
|
+
"StopTrailLimit",
|
|
449
|
+
"Historical",
|
|
450
|
+
]
|
|
451
|
+
# Order direction types
|
|
452
|
+
OrdTypes = ["Buy", "Sell"]
|
|
453
|
+
Buy, Sell = range(2)
|
|
454
|
+
# Different order statuses
|
|
455
|
+
Created, Submitted, Accepted, Partial, Completed, Canceled, Expired, Margin, Rejected = range(9)
|
|
456
|
+
|
|
457
|
+
Cancelled = Canceled # alias
|
|
458
|
+
|
|
459
|
+
Status = [
|
|
460
|
+
"Created",
|
|
461
|
+
"Submitted",
|
|
462
|
+
"Accepted",
|
|
463
|
+
"Partial",
|
|
464
|
+
"Completed",
|
|
465
|
+
"Canceled",
|
|
466
|
+
"Expired",
|
|
467
|
+
"Margin",
|
|
468
|
+
"Rejected",
|
|
469
|
+
]
|
|
470
|
+
# Add a number for each order
|
|
471
|
+
refbasis = itertools.count(1) # for a unique identifier per order
|
|
472
|
+
|
|
473
|
+
# Set/get plimit property
|
|
474
|
+
def _getplimit(self):
|
|
475
|
+
return self._plimit
|
|
476
|
+
|
|
477
|
+
def _setplimit(self, val):
|
|
478
|
+
self._plimit = val
|
|
479
|
+
|
|
480
|
+
plimit = property(_getplimit, _setplimit)
|
|
481
|
+
|
|
482
|
+
# Get order attribute - modified to work with OrderParams
|
|
483
|
+
def __getattr__(self, name):
|
|
484
|
+
# Return attr from params if not found in order
|
|
485
|
+
# PERFORMANCE OPTIMIZATION: Use try/except instead of hasattr
|
|
486
|
+
# Called 1.65M+ times, reduce attribute lookups
|
|
487
|
+
if name == "p": # Avoid recursion when checking for 'p' itself
|
|
488
|
+
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
|
|
489
|
+
try:
|
|
490
|
+
p = object.__getattribute__(self, "p")
|
|
491
|
+
return getattr(p, name)
|
|
492
|
+
except AttributeError:
|
|
493
|
+
# ``copy.copy`` legitimately probes ``__setstate__`` here for
|
|
494
|
+
# every order clone. This is an expected protocol miss, not a
|
|
495
|
+
# recoverable runtime failure, so keep the compatibility fallback
|
|
496
|
+
# silent on the hot path.
|
|
497
|
+
raise AttributeError(
|
|
498
|
+
f"'{self.__class__.__name__}' object has no attribute '{name}'"
|
|
499
|
+
) from None
|
|
500
|
+
|
|
501
|
+
# Set order attribute - modified to work with OrderParams
|
|
502
|
+
def __setattr__(self, name, value):
|
|
503
|
+
# Check if we have params and the name exists in params
|
|
504
|
+
# Use object.__getattribute__ to avoid recursion
|
|
505
|
+
try:
|
|
506
|
+
p = object.__getattribute__(self, "p")
|
|
507
|
+
if hasattr(p, name):
|
|
508
|
+
setattr(p, name, value)
|
|
509
|
+
return
|
|
510
|
+
except AttributeError:
|
|
511
|
+
# ``p`` is deliberately absent during early construction.
|
|
512
|
+
# Logging that expected state once per new order adds avoidable
|
|
513
|
+
# work before the parameter object exists.
|
|
514
|
+
pass
|
|
515
|
+
|
|
516
|
+
super().__setattr__(name, value)
|
|
517
|
+
|
|
518
|
+
# Content displayed when printing order
|
|
519
|
+
def __str__(self):
|
|
520
|
+
tojoin = []
|
|
521
|
+
tojoin.append(f"Ref: {self.ref}")
|
|
522
|
+
tojoin.append(f"OrdType: {self.ordtype}")
|
|
523
|
+
tojoin.append(f"OrdType: {self.ordtypename()}")
|
|
524
|
+
tojoin.append(f"Status: {self.status}")
|
|
525
|
+
tojoin.append(f"Status: {self.getstatusname()}")
|
|
526
|
+
tojoin.append(f"Size: {self.size}")
|
|
527
|
+
tojoin.append(f"Price: {self.price}")
|
|
528
|
+
tojoin.append(f"Price Limit: {self.pricelimit}")
|
|
529
|
+
tojoin.append(f"TrailAmount: {self.trailamount}")
|
|
530
|
+
tojoin.append(f"TrailPercent: {self.trailpercent}")
|
|
531
|
+
tojoin.append(f"ExecType: {self.exectype}")
|
|
532
|
+
tojoin.append(f"ExecType: {self.getordername()}")
|
|
533
|
+
tojoin.append(f"CommInfo: {self.comminfo}")
|
|
534
|
+
tojoin.append(f"End of Session: {self.dteos}")
|
|
535
|
+
tojoin.append(f"Info: {self.info}")
|
|
536
|
+
tojoin.append(f"Broker: {self.broker}")
|
|
537
|
+
tojoin.append(f"Alive: {self.alive()}")
|
|
538
|
+
|
|
539
|
+
return "\n".join(tojoin)
|
|
540
|
+
|
|
541
|
+
# Initialize class - modified to accept kwargs and create params manually
|
|
542
|
+
def __init__(self, **kwargs):
|
|
543
|
+
"""Initialize the order base instance.
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
**kwargs: Order parameters (owner, data, size, price, etc.).
|
|
547
|
+
"""
|
|
548
|
+
# Create params object manually instead of using metaclass
|
|
549
|
+
self.p = OrderParams(**kwargs)
|
|
550
|
+
# Create convenient direct access to params - alias for backward compatibility
|
|
551
|
+
self.params = self.p
|
|
552
|
+
|
|
553
|
+
# Increment a number each time an instance is created
|
|
554
|
+
self.plen = None
|
|
555
|
+
self.ref = next(self.refbasis)
|
|
556
|
+
# broker defaults to None
|
|
557
|
+
self.broker = None
|
|
558
|
+
# order info information
|
|
559
|
+
self.info = AutoOrderedDict()
|
|
560
|
+
# commission defaults to None
|
|
561
|
+
self.comminfo = None
|
|
562
|
+
# triggered defaults to None
|
|
563
|
+
self.triggered = False
|
|
564
|
+
# If self.parent is None, self._active is True, otherwise it's None
|
|
565
|
+
self._active = self.parent is None
|
|
566
|
+
# Order status, when order initializes, defaults to Created
|
|
567
|
+
self.status = OrderBase.Created
|
|
568
|
+
# Set plimit property value
|
|
569
|
+
self.plimit = self.p.pricelimit # alias via property
|
|
570
|
+
# If order execution type is None, default is Market order
|
|
571
|
+
if self.exectype is None:
|
|
572
|
+
self.exectype = OrderBase.Market
|
|
573
|
+
# If order is not a buy order, order size becomes negative
|
|
574
|
+
if not self.isbuy():
|
|
575
|
+
self.size = -self.size
|
|
576
|
+
|
|
577
|
+
# Set a reference price if price is not set using the close price
|
|
578
|
+
pclose = self.data.close[0] if not self.p.simulated else self.price
|
|
579
|
+
price = pclose if self.price is None and self.pricelimit is None else self.price
|
|
580
|
+
# If not simulated, order creation time equals current data time, otherwise it's 0
|
|
581
|
+
dcreated = self.data.datetime[0] if not self.p.simulated else 0.0
|
|
582
|
+
# Order creation
|
|
583
|
+
self.created = OrderData(
|
|
584
|
+
dt=dcreated,
|
|
585
|
+
size=self.size,
|
|
586
|
+
price=price,
|
|
587
|
+
pricelimit=self.pricelimit,
|
|
588
|
+
pclose=pclose,
|
|
589
|
+
trailamount=self.trailamount,
|
|
590
|
+
trailpercent=self.trailpercent,
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
# Adjust price in case a trailing limit is wished
|
|
594
|
+
# If execution type is trailing stop, price needs adjustment. Limit offset equals created price minus created limit price
|
|
595
|
+
# Price equals order creation price, reset created order price to infinity for buy orders, negative infinity for sell orders
|
|
596
|
+
# Then adjust price; if not trailing stop type, limit offset is 0
|
|
597
|
+
if self.exectype in [OrderBase.StopTrail, OrderBase.StopTrailLimit]:
|
|
598
|
+
self._limitoffset = self.created.price - self.created.pricelimit
|
|
599
|
+
price = self.created.price
|
|
600
|
+
self.created.price = float("inf" * self.isbuy() or "-inf")
|
|
601
|
+
self.trailadjust(price)
|
|
602
|
+
else:
|
|
603
|
+
self._limitoffset = 0.0
|
|
604
|
+
# Order execution
|
|
605
|
+
self.executed = OrderData(remsize=self.size)
|
|
606
|
+
# Position set to 0
|
|
607
|
+
self.position = 0
|
|
608
|
+
# Next is to determine order validity period
|
|
609
|
+
# If validity parameter is a date format
|
|
610
|
+
if isinstance(self.valid, datetime.date):
|
|
611
|
+
# comparison will later be done against the raw datetime[0] value
|
|
612
|
+
# Convert date format to number
|
|
613
|
+
self.valid = self.data.date2num(self.valid)
|
|
614
|
+
# If validity parameter is a time delta format, if time delta is 0, valid for the day, otherwise current time plus time delta
|
|
615
|
+
# Then convert obtained validity to number
|
|
616
|
+
elif isinstance(self.valid, datetime.timedelta):
|
|
617
|
+
# offset with regards to now ... get utcnow + offset
|
|
618
|
+
# when reading with date2num ... it will be automatically localized
|
|
619
|
+
if self.valid == self.DAY:
|
|
620
|
+
valid = datetime.datetime.combine(
|
|
621
|
+
self.data.datetime.date(), datetime.time(23, 59, 59, 9999)
|
|
622
|
+
)
|
|
623
|
+
else:
|
|
624
|
+
valid = self.data.datetime.datetime() + self.valid
|
|
625
|
+
|
|
626
|
+
self.valid = self.data.date2num(valid)
|
|
627
|
+
# If validity is not None, if not 0, valid for the day, if 0, currently valid
|
|
628
|
+
elif self.valid is not None:
|
|
629
|
+
if not self.valid: # avoid comparing None and 0
|
|
630
|
+
valid = datetime.datetime.combine(
|
|
631
|
+
self.data.datetime.date(), datetime.time(23, 59, 59, 9999)
|
|
632
|
+
)
|
|
633
|
+
else: # assume float
|
|
634
|
+
valid = self.data.datetime[0] + self.valid
|
|
635
|
+
# If not simulated, get dteos, if simulated, dteos is 0
|
|
636
|
+
# dteos: provisional end-of-session datetime, used by broker for order expiry checks
|
|
637
|
+
if not self.p.simulated:
|
|
638
|
+
# provisional end-of-session
|
|
639
|
+
# get next session end
|
|
640
|
+
dtime = self.data.datetime.datetime(0)
|
|
641
|
+
session = self.data.p.sessionend
|
|
642
|
+
dteos = dtime.replace(
|
|
643
|
+
hour=session.hour,
|
|
644
|
+
minute=session.minute,
|
|
645
|
+
second=session.second,
|
|
646
|
+
microsecond=session.microsecond,
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
if dteos < dtime:
|
|
650
|
+
# eos before current time ... no ... must be at least next day
|
|
651
|
+
dteos += datetime.timedelta(days=1)
|
|
652
|
+
|
|
653
|
+
self.dteos = self.data.date2num(dteos)
|
|
654
|
+
else:
|
|
655
|
+
self.dteos = 0.0
|
|
656
|
+
|
|
657
|
+
# Clone the order itself
|
|
658
|
+
def clone(self):
|
|
659
|
+
"""Clone the order.
|
|
660
|
+
|
|
661
|
+
Returns:
|
|
662
|
+
OrderBase: A cloned copy with cloned executed OrderData.
|
|
663
|
+
"""
|
|
664
|
+
# status, triggered and executed are the only moving parts in order
|
|
665
|
+
# status and triggered are covered by copy
|
|
666
|
+
# executed has to be replaced with an intelligent clone of itself
|
|
667
|
+
obj = copy(self)
|
|
668
|
+
obj.executed = self.executed.clone()
|
|
669
|
+
return obj # status could change in next to completed
|
|
670
|
+
|
|
671
|
+
@property
|
|
672
|
+
def position_side(self):
|
|
673
|
+
"""Read-only alias for order.info.position_side."""
|
|
674
|
+
return getattr(self.info, "position_side", None)
|
|
675
|
+
|
|
676
|
+
@property
|
|
677
|
+
def offset(self):
|
|
678
|
+
"""Read-only alias for order.info.offset."""
|
|
679
|
+
return getattr(self.info, "offset", None)
|
|
680
|
+
|
|
681
|
+
# Get order status name
|
|
682
|
+
def getstatusname(self, status=None):
|
|
683
|
+
"""Returns the name for a given status or the one of the order"""
|
|
684
|
+
idx = self.status if status is None else status
|
|
685
|
+
try:
|
|
686
|
+
return self.Status[idx]
|
|
687
|
+
except (IndexError, TypeError):
|
|
688
|
+
return f"Unknown({idx})"
|
|
689
|
+
|
|
690
|
+
# Get order name
|
|
691
|
+
def getordername(self, exectype=None):
|
|
692
|
+
"""Returns the name for a given exectype or the one of the order"""
|
|
693
|
+
idx = self.exectype if exectype is None else exectype
|
|
694
|
+
try:
|
|
695
|
+
return self.ExecTypes[idx]
|
|
696
|
+
except (IndexError, TypeError):
|
|
697
|
+
return f"Unknown({idx})"
|
|
698
|
+
|
|
699
|
+
@classmethod
|
|
700
|
+
def ExecType(cls, exectype):
|
|
701
|
+
"""Get the execution type constant from the class.
|
|
702
|
+
|
|
703
|
+
Args:
|
|
704
|
+
exectype: String name of the execution type.
|
|
705
|
+
|
|
706
|
+
Returns:
|
|
707
|
+
int: The execution type constant.
|
|
708
|
+
"""
|
|
709
|
+
return getattr(cls, exectype)
|
|
710
|
+
|
|
711
|
+
# Get order type name
|
|
712
|
+
def ordtypename(self, ordtype=None):
|
|
713
|
+
"""Returns the name for a given ordtype or the one of the order"""
|
|
714
|
+
return self.OrdTypes[self.ordtype if ordtype is None else ordtype]
|
|
715
|
+
|
|
716
|
+
# Get active status
|
|
717
|
+
def active(self):
|
|
718
|
+
"""Check if the order is active.
|
|
719
|
+
|
|
720
|
+
Returns:
|
|
721
|
+
bool: True if order is active, False otherwise.
|
|
722
|
+
"""
|
|
723
|
+
return self._active
|
|
724
|
+
|
|
725
|
+
# Activate order
|
|
726
|
+
def activate(self):
|
|
727
|
+
"""Activate the order."""
|
|
728
|
+
self._active = True
|
|
729
|
+
|
|
730
|
+
# Frozenset for O(1) alive status lookup
|
|
731
|
+
_ALIVE_STATUSES = frozenset(range(4)) # Created, Submitted, Accepted, Partial
|
|
732
|
+
|
|
733
|
+
# Order is alive if it's in Created, Submitted, Partial, or Accepted status
|
|
734
|
+
def alive(self):
|
|
735
|
+
"""Returns True if the order is in a status in which it can still be
|
|
736
|
+
executed
|
|
737
|
+
"""
|
|
738
|
+
return self.status in self._ALIVE_STATUSES
|
|
739
|
+
|
|
740
|
+
# Add commission related information
|
|
741
|
+
def addcomminfo(self, comminfo):
|
|
742
|
+
"""Stores a CommInfo scheme associated with the asset"""
|
|
743
|
+
self.comminfo = comminfo
|
|
744
|
+
|
|
745
|
+
# Add information
|
|
746
|
+
def addinfo(self, **kwargs):
|
|
747
|
+
"""Add the keys, values of kwargs to the internal info dictionary to
|
|
748
|
+
hold custom information in the order
|
|
749
|
+
"""
|
|
750
|
+
for key, val in iteritems(kwargs):
|
|
751
|
+
self.info[key] = val
|
|
752
|
+
|
|
753
|
+
# Check if two orders are equal
|
|
754
|
+
def __eq__(self, other):
|
|
755
|
+
return other is not None and self.ref == other.ref
|
|
756
|
+
|
|
757
|
+
# Check if two orders are not equal
|
|
758
|
+
def __ne__(self, other):
|
|
759
|
+
return other is None or self.ref != other.ref
|
|
760
|
+
|
|
761
|
+
# Order ref is immutable, so hash based on ref is safe
|
|
762
|
+
def __hash__(self):
|
|
763
|
+
return hash(self.ref)
|
|
764
|
+
|
|
765
|
+
# Check if current order is a buy order
|
|
766
|
+
def isbuy(self):
|
|
767
|
+
"""Returns True if the order is a Buy order"""
|
|
768
|
+
return self.ordtype == OrderBase.Buy
|
|
769
|
+
|
|
770
|
+
# Check if current order is a sell order
|
|
771
|
+
def issell(self):
|
|
772
|
+
"""Returns True if the order is a Sell order"""
|
|
773
|
+
return self.ordtype == OrderBase.Sell
|
|
774
|
+
|
|
775
|
+
# Set specific position size for order
|
|
776
|
+
def setposition(self, position):
|
|
777
|
+
"""Receives the current position for the asset and stores it"""
|
|
778
|
+
self.position = position
|
|
779
|
+
|
|
780
|
+
# Submit order to broker
|
|
781
|
+
def submit(self, broker=None):
|
|
782
|
+
"""Marks an order as submitted and stores the broker to which it was
|
|
783
|
+
submitted"""
|
|
784
|
+
self.status = OrderBase.Submitted
|
|
785
|
+
self.broker = broker
|
|
786
|
+
self.plen = len(self.data)
|
|
787
|
+
|
|
788
|
+
# Accept order
|
|
789
|
+
def accept(self, broker=None):
|
|
790
|
+
"""Marks an order as accepted"""
|
|
791
|
+
self.status = OrderBase.Accepted
|
|
792
|
+
self.broker = broker
|
|
793
|
+
|
|
794
|
+
# Broker status, if broker is not None or 0, try to get order status from broker, if broker is None, directly return order status
|
|
795
|
+
def brokerstatus(self):
|
|
796
|
+
"""Tries to retrieve the status from the broker in which the order is.
|
|
797
|
+
|
|
798
|
+
Defaults to last known status if no broker is associated"""
|
|
799
|
+
if self.broker:
|
|
800
|
+
return self.broker.orderstatus(self)
|
|
801
|
+
|
|
802
|
+
return self.status
|
|
803
|
+
|
|
804
|
+
# Reject order, if already rejected return False, otherwise set order status and rejection execution time, broker, then return True
|
|
805
|
+
def reject(self, broker=None):
|
|
806
|
+
"""Marks an order as rejected"""
|
|
807
|
+
if self.status == OrderBase.Rejected:
|
|
808
|
+
return False
|
|
809
|
+
|
|
810
|
+
self.status = OrderBase.Rejected
|
|
811
|
+
# self.executed.dt = self.data.datetime[0]
|
|
812
|
+
self.broker = broker
|
|
813
|
+
if not self.p.simulated:
|
|
814
|
+
self.executed.dt = self.data.datetime[0]
|
|
815
|
+
return True
|
|
816
|
+
|
|
817
|
+
# Cancel order
|
|
818
|
+
def cancel(self):
|
|
819
|
+
"""Marks an order as cancelled"""
|
|
820
|
+
self.status = OrderBase.Canceled
|
|
821
|
+
# self.executed.dt = self.data.datetime[0]
|
|
822
|
+
if not self.p.simulated:
|
|
823
|
+
self.executed.dt = self.data.datetime[0]
|
|
824
|
+
|
|
825
|
+
# Insufficient margin, add margin
|
|
826
|
+
def margin(self):
|
|
827
|
+
"""Marks an order as having met a margin call"""
|
|
828
|
+
self.status = OrderBase.Margin
|
|
829
|
+
# self.executed.dt = self.data.datetime[0]
|
|
830
|
+
if not self.p.simulated:
|
|
831
|
+
self.executed.dt = self.data.datetime[0]
|
|
832
|
+
|
|
833
|
+
# Complete
|
|
834
|
+
def completed(self):
|
|
835
|
+
"""Marks an order as completely filled"""
|
|
836
|
+
self.status = OrderBase.Completed
|
|
837
|
+
|
|
838
|
+
# Partial execution
|
|
839
|
+
def partial(self):
|
|
840
|
+
"""Marks an order as partially filled"""
|
|
841
|
+
self.status = OrderBase.Partial
|
|
842
|
+
|
|
843
|
+
# Execute order
|
|
844
|
+
def execute(
|
|
845
|
+
self,
|
|
846
|
+
dt,
|
|
847
|
+
size,
|
|
848
|
+
price,
|
|
849
|
+
closed,
|
|
850
|
+
closedvalue,
|
|
851
|
+
closedcomm,
|
|
852
|
+
opened,
|
|
853
|
+
openedvalue,
|
|
854
|
+
openedcomm,
|
|
855
|
+
margin,
|
|
856
|
+
pnl,
|
|
857
|
+
psize,
|
|
858
|
+
pprice,
|
|
859
|
+
):
|
|
860
|
+
"""Receives data execution input and stores it"""
|
|
861
|
+
if not size:
|
|
862
|
+
return
|
|
863
|
+
|
|
864
|
+
self.executed.add(
|
|
865
|
+
dt,
|
|
866
|
+
size,
|
|
867
|
+
price,
|
|
868
|
+
closed,
|
|
869
|
+
closedvalue,
|
|
870
|
+
closedcomm,
|
|
871
|
+
opened,
|
|
872
|
+
openedvalue,
|
|
873
|
+
openedcomm,
|
|
874
|
+
pnl,
|
|
875
|
+
psize,
|
|
876
|
+
pprice,
|
|
877
|
+
)
|
|
878
|
+
|
|
879
|
+
self.executed.margin = margin
|
|
880
|
+
|
|
881
|
+
# Order expiration
|
|
882
|
+
def expire(self):
|
|
883
|
+
"""Marks an order as expired. Returns True if it worked"""
|
|
884
|
+
self.status = OrderBase.Expired
|
|
885
|
+
return True
|
|
886
|
+
|
|
887
|
+
# Trail price adjustment
|
|
888
|
+
def trailadjust(self, price):
|
|
889
|
+
"""Adjust trailing stop price.
|
|
890
|
+
|
|
891
|
+
Args:
|
|
892
|
+
price: Current price for trailing calculation.
|
|
893
|
+
|
|
894
|
+
Note:
|
|
895
|
+
Generic interface - override in subclasses for specific behavior.
|
|
896
|
+
"""
|
|
897
|
+
# generic interface
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
# Modified Order class to work without metaclass
|
|
901
|
+
class Order(OrderBase):
|
|
902
|
+
"""Order class for buy/sell orders.
|
|
903
|
+
|
|
904
|
+
Extends OrderBase with order type (buy/sell) and session end time
|
|
905
|
+
handling. This is the main order class used for creating and
|
|
906
|
+
managing trading orders.
|
|
907
|
+
|
|
908
|
+
Attributes:
|
|
909
|
+
ordtype: Order type (Buy or Sell).
|
|
910
|
+
dteos: Date/time of end of session for order validity.
|
|
911
|
+
"""
|
|
912
|
+
|
|
913
|
+
# Above is processing of OrderBase, below is processing of Order, Order inherits from OrderBase
|
|
914
|
+
# Order class mainly adds dteos, ordtype and other information, also rewrites some functions, adds ordtype, a tracking price
|
|
915
|
+
# ordtype variable determines whether this order is a buy order or sell order, not set by default
|
|
916
|
+
ordtype: Optional[int] = None
|
|
917
|
+
|
|
918
|
+
# Override initialization function, add processing for ordtype and dteos
|
|
919
|
+
def __init__(self, **kwargs):
|
|
920
|
+
"""Initialize the order instance.
|
|
921
|
+
|
|
922
|
+
Args:
|
|
923
|
+
**kwargs: Order parameters (owner, data, size, price, etc.).
|
|
924
|
+
"""
|
|
925
|
+
super().__init__(**kwargs)
|
|
926
|
+
|
|
927
|
+
# For Order, additional operations on dteos are needed
|
|
928
|
+
# dteos represents the end time of this session
|
|
929
|
+
# The code logic below is:
|
|
930
|
+
# dteos == 0.0 represents day order, i.e., order valid for the day, in this case dteos is the session end time of the day
|
|
931
|
+
# dteos >= self.data.datetime[0] means the order's validity period is greater than the current data time, no need to modify dteos
|
|
932
|
+
# In other cases, set dteos to 0, making it a day order
|
|
933
|
+
if self.dteos == 0.0:
|
|
934
|
+
# day order -> till session end if not changed before
|
|
935
|
+
pass
|
|
936
|
+
elif self.dteos >= self.data.datetime[0]:
|
|
937
|
+
# if dteos is in future -> inform order it's a GTD (good till date)
|
|
938
|
+
pass
|
|
939
|
+
else:
|
|
940
|
+
# If current time exceeds dteos, set dteos to 0.0
|
|
941
|
+
# Expiration date less than current time -> becomes day order
|
|
942
|
+
self.dteos = 0.0
|
|
943
|
+
|
|
944
|
+
# Execute this order, many parameters need to be passed during execution
|
|
945
|
+
def execute(
|
|
946
|
+
self,
|
|
947
|
+
dt,
|
|
948
|
+
size,
|
|
949
|
+
price,
|
|
950
|
+
closed,
|
|
951
|
+
closedvalue,
|
|
952
|
+
closedcomm,
|
|
953
|
+
opened,
|
|
954
|
+
openedvalue,
|
|
955
|
+
openedcomm,
|
|
956
|
+
margin,
|
|
957
|
+
pnl,
|
|
958
|
+
psize,
|
|
959
|
+
pprice,
|
|
960
|
+
):
|
|
961
|
+
"""Execute the order with given parameters.
|
|
962
|
+
|
|
963
|
+
Args:
|
|
964
|
+
dt: Execution datetime.
|
|
965
|
+
size: Executed size.
|
|
966
|
+
price: Execution price.
|
|
967
|
+
closed: Size of position closed.
|
|
968
|
+
closedvalue: Value of closed position.
|
|
969
|
+
closedcomm: Commission for closed position.
|
|
970
|
+
opened: Size of new position opened.
|
|
971
|
+
openedvalue: Value of opened position.
|
|
972
|
+
openedcomm: Commission for opened position.
|
|
973
|
+
margin: Margin required for the order.
|
|
974
|
+
pnl: Profit/loss from closed position.
|
|
975
|
+
psize: Current position size.
|
|
976
|
+
pprice: Current position price.
|
|
977
|
+
"""
|
|
978
|
+
self.executed.add(
|
|
979
|
+
dt,
|
|
980
|
+
size,
|
|
981
|
+
price,
|
|
982
|
+
closed,
|
|
983
|
+
closedvalue,
|
|
984
|
+
closedcomm,
|
|
985
|
+
opened,
|
|
986
|
+
openedvalue,
|
|
987
|
+
openedcomm,
|
|
988
|
+
pnl,
|
|
989
|
+
psize,
|
|
990
|
+
pprice,
|
|
991
|
+
)
|
|
992
|
+
|
|
993
|
+
if margin is not None:
|
|
994
|
+
self.executed.margin = margin
|
|
995
|
+
|
|
996
|
+
if self.executed.remsize:
|
|
997
|
+
self.status = OrderBase.Partial
|
|
998
|
+
else:
|
|
999
|
+
self.status = OrderBase.Completed
|
|
1000
|
+
|
|
1001
|
+
# Order expiration
|
|
1002
|
+
def expire(self):
|
|
1003
|
+
"""Check if order should be expired
|
|
1004
|
+
|
|
1005
|
+
Returns:
|
|
1006
|
+
True: If order has expired
|
|
1007
|
+
False: If order has not expired
|
|
1008
|
+
"""
|
|
1009
|
+
# Market orders don't expire, will always be executed
|
|
1010
|
+
if self.exectype == Order.Market:
|
|
1011
|
+
return False
|
|
1012
|
+
|
|
1013
|
+
# Check if order exceeds validity period
|
|
1014
|
+
if self.valid and self.data.datetime[0] > self.valid:
|
|
1015
|
+
self.status = Order.Expired
|
|
1016
|
+
self.executed.dt = self.data.datetime[0]
|
|
1017
|
+
return True
|
|
1018
|
+
|
|
1019
|
+
return False
|
|
1020
|
+
|
|
1021
|
+
# Trail adjust price, trail adjust price is for trailing stop orders. Trailing stop order is also a moving stop order,
|
|
1022
|
+
# the moving distance can be represented by absolute value or percentage. This function is mainly to calculate
|
|
1023
|
+
# the price after trailing stop order adjustment
|
|
1024
|
+
def trailadjust(self, price):
|
|
1025
|
+
"""Adjust trailing stop order price.
|
|
1026
|
+
|
|
1027
|
+
Args:
|
|
1028
|
+
price: Current market price for trailing calculation.
|
|
1029
|
+
|
|
1030
|
+
For buy orders: stop price moves up as price increases.
|
|
1031
|
+
For sell orders: stop price moves down as price decreases.
|
|
1032
|
+
"""
|
|
1033
|
+
# If moving amount, price adjustment amount is the moving amount; if moving percentage,
|
|
1034
|
+
# price adjustment amount is price multiplied by percentage, otherwise price adjustment amount is 0
|
|
1035
|
+
if self.trailamount:
|
|
1036
|
+
adjsize = self.trailamount
|
|
1037
|
+
elif self.trailpercent:
|
|
1038
|
+
adjsize = price * self.trailpercent
|
|
1039
|
+
else:
|
|
1040
|
+
adjsize = 0.0
|
|
1041
|
+
|
|
1042
|
+
# CRITICAL FIX: BUY stop is ABOVE market (+adjsize), SELL stop is BELOW market (-adjsize)
|
|
1043
|
+
# Original formula was backwards: (1 - 2*isbuy) gave -1 for buy, +1 for sell
|
|
1044
|
+
# Correct formula: (2*isbuy - 1) gives +1 for buy, -1 for sell
|
|
1045
|
+
price_new = price + adjsize * (2 * self.isbuy() - 1)
|
|
1046
|
+
|
|
1047
|
+
# If price_new surpasses self.created.price -> readjust
|
|
1048
|
+
# If new price exceeds originally created price, readjust.
|
|
1049
|
+
# For buy orders, if new price is less than created price, use this new price
|
|
1050
|
+
# For sell orders, if new price is greater than created price, use this new price
|
|
1051
|
+
if price_new != self.created.price:
|
|
1052
|
+
if (
|
|
1053
|
+
self.isbuy()
|
|
1054
|
+
and price_new < self.created.price
|
|
1055
|
+
or self.issell()
|
|
1056
|
+
and price_new > self.created.price
|
|
1057
|
+
):
|
|
1058
|
+
self.created.price = price_new
|
|
1059
|
+
|
|
1060
|
+
# For both trailing stop types, limitprice also needs adjustment
|
|
1061
|
+
if self.exectype == OrderBase.StopTrailLimit:
|
|
1062
|
+
self.created.pricelimit = self.created.price + self._limitoffset
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
# Buy order
|
|
1066
|
+
class BuyOrder(Order):
|
|
1067
|
+
"""Buy order class.
|
|
1068
|
+
|
|
1069
|
+
Represents a buy order with ordtype set to Order.Buy.
|
|
1070
|
+
"""
|
|
1071
|
+
|
|
1072
|
+
ordtype = Order.Buy
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
# Stop buy order
|
|
1076
|
+
class StopBuyOrder(BuyOrder):
|
|
1077
|
+
"""Stop buy order class.
|
|
1078
|
+
|
|
1079
|
+
Used for buy orders that trigger when price crosses a threshold.
|
|
1080
|
+
"""
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
# Create stop limit buy order
|
|
1084
|
+
class StopLimitBuyOrder(BuyOrder):
|
|
1085
|
+
"""Stop limit buy order class.
|
|
1086
|
+
|
|
1087
|
+
Used for buy orders that become limit orders after stop price is triggered.
|
|
1088
|
+
"""
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
# Create sell order
|
|
1092
|
+
class SellOrder(Order):
|
|
1093
|
+
"""Sell order class.
|
|
1094
|
+
|
|
1095
|
+
Represents a sell order with ordtype set to Order.Sell.
|
|
1096
|
+
"""
|
|
1097
|
+
|
|
1098
|
+
ordtype = Order.Sell
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
# Create stop sell order
|
|
1102
|
+
class StopSellOrder(SellOrder):
|
|
1103
|
+
"""Stop sell order class.
|
|
1104
|
+
|
|
1105
|
+
Used for sell orders that trigger when price crosses a threshold.
|
|
1106
|
+
"""
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
# Create stop limit sell order
|
|
1110
|
+
class StopLimitSellOrder(SellOrder):
|
|
1111
|
+
"""Stop limit sell order class.
|
|
1112
|
+
|
|
1113
|
+
Used for sell orders that become limit orders after stop price is triggered.
|
|
1114
|
+
"""
|