polakowo / vectorbt

Find your trading edge, using the fastest engine for backtesting, algorithmic trading, and research.
https://vectorbt.dev
Other
4.03k stars 590 forks source link

Open short position right after closing long position with full capital #697

Open kevin851066 opened 3 months ago

kevin851066 commented 3 months ago

First of all, thanks for developing this excellent library!

My strategy will enter short/long position right after closing long/short position, while short/long signal happens. And each postion will entry with full capital

Here is my code:

start = '2019-01-01 UTC'  # crypto is in UTC
end = '2020-01-01 UTC'
btc_price = vbt.YFData.download('BTC-USD', start=start, end=end).get('Close')

fast_ma = vbt.MA.run(btc_price, 10, short_name='fast')
slow_ma = vbt.MA.run(btc_price, 20, short_name='slow')

entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)

pf = vbt.Portfolio.from_signals(btc_price, 
                                entries=entries, 
                                exits=exits,
                                short_entries=exits,
                                short_exits=entries,
                                size = 1,
                                # size_type='amount',
                                size_type=2,
                                fees=0.001,
                                init_cash=1000)

And I got an error, seems like vectorbt does not support this behavior.

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[37], [line 12](vscode-notebook-cell:?execution_count=37&line=12)
      [9](vscode-notebook-cell:?execution_count=37&line=9) entries = fast_ma.ma_crossed_above(slow_ma)
     [10](vscode-notebook-cell:?execution_count=37&line=10) exits = fast_ma.ma_crossed_below(slow_ma)
---> [12](vscode-notebook-cell:?execution_count=37&line=12) pf = vbt.Portfolio.from_signals(btc_price, 
     [13](vscode-notebook-cell:?execution_count=37&line=13)                                 entries=entries, 
     [14](vscode-notebook-cell:?execution_count=37&line=14)                                 exits=exits,
     [15](vscode-notebook-cell:?execution_count=37&line=15)                                 short_entries=exits,
     [16](vscode-notebook-cell:?execution_count=37&line=16)                                 short_exits=entries,
     [17](vscode-notebook-cell:?execution_count=37&line=17)                                 size = 1,
     [18](vscode-notebook-cell:?execution_count=37&line=18)                                 # size_type='amount',
     [19](vscode-notebook-cell:?execution_count=37&line=19)                                 size_type=2,
     [20](vscode-notebook-cell:?execution_count=37&line=20)                                 fees=0.001,
     [21](vscode-notebook-cell:?execution_count=37&line=21)                                 init_cash=1000)

File [~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2960](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2960), in Portfolio.from_signals(cls, close, entries, exits, short_entries, short_exits, signal_func_nb, signal_args, size, size_type, price, fees, fixed_fees, slippage, min_size, max_size, size_granularity, reject_prob, lock_cash, allow_partial, raise_reject, log, accumulate, upon_long_conflict, upon_short_conflict, upon_dir_conflict, upon_opposite_entry, direction, val_price, open, high, low, sl_stop, sl_trail, tp_stop, stop_entry_price, stop_exit_price, upon_stop_exit, upon_stop_update, adjust_sl_func_nb, adjust_sl_args, adjust_tp_func_nb, adjust_tp_args, use_stops, init_cash, cash_sharing, call_seq, ffill_val_price, update_value, max_orders, max_logs, seed, group_by, broadcast_named_args, broadcast_kwargs, template_mapping, wrapper_kwargs, freq, attach_call_seq, **kwargs)
   [2957](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2957) checks.assert_numba_func(adjust_tp_func_nb)
   [2959](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2959) # Perform the simulation
-> [2960](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2960) order_records, log_records = nb.simulate_from_signal_func_nb(
   [2961](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2961)     target_shape_2d,
   [2962](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2962)     cs_group_lens,  # group only if cash sharing is enabled to speed up
   [2963](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2963)     init_cash,
   [2964](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2964)     call_seq,
   [2965](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2965)     signal_func_nb=signal_func_nb,
   [2966](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2966)     signal_args=signal_args,
   [2967](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2967)     size=broadcasted_args['size'],
   [2968](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2968)     price=broadcasted_args['price'],
   [2969](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2969)     size_type=broadcasted_args['size_type'],
   [2970](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2970)     fees=broadcasted_args['fees'],
   [2971](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2971)     fixed_fees=broadcasted_args['fixed_fees'],
   [2972](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2972)     slippage=broadcasted_args['slippage'],
   [2973](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2973)     min_size=broadcasted_args['min_size'],
   [2974](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2974)     max_size=broadcasted_args['max_size'],
   [2975](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2975)     size_granularity=broadcasted_args['size_granularity'],
   [2976](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2976)     reject_prob=broadcasted_args['reject_prob'],
   [2977](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2977)     lock_cash=broadcasted_args['lock_cash'],
   [2978](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2978)     allow_partial=broadcasted_args['allow_partial'],
   [2979](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2979)     raise_reject=broadcasted_args['raise_reject'],
   [2980](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2980)     log=broadcasted_args['log'],
   [2981](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2981)     accumulate=broadcasted_args['accumulate'],
   [2982](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2982)     upon_long_conflict=broadcasted_args['upon_long_conflict'],
   [2983](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2983)     upon_short_conflict=broadcasted_args['upon_short_conflict'],
   [2984](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2984)     upon_dir_conflict=broadcasted_args['upon_dir_conflict'],
   [2985](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2985)     upon_opposite_entry=broadcasted_args['upon_opposite_entry'],
   [2986](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2986)     val_price=broadcasted_args['val_price'],
   [2987](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2987)     open=broadcasted_args['open'],
   [2988](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2988)     high=broadcasted_args['high'],
   [2989](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2989)     low=broadcasted_args['low'],
   [2990](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2990)     close=broadcasted_args['close'],
   [2991](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2991)     sl_stop=broadcasted_args['sl_stop'],
   [2992](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2992)     sl_trail=broadcasted_args['sl_trail'],
   [2993](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2993)     tp_stop=broadcasted_args['tp_stop'],
   [2994](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2994)     stop_entry_price=broadcasted_args['stop_entry_price'],
   [2995](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2995)     stop_exit_price=broadcasted_args['stop_exit_price'],
   [2996](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2996)     upon_stop_exit=broadcasted_args['upon_stop_exit'],
   [2997](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2997)     upon_stop_update=broadcasted_args['upon_stop_update'],
   [2998](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2998)     adjust_sl_func_nb=adjust_sl_func_nb,
   [2999](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:2999)     adjust_sl_args=adjust_sl_args,
   [3000](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3000)     adjust_tp_func_nb=adjust_tp_func_nb,
   [3001](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3001)     adjust_tp_args=adjust_tp_args,
   [3002](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3002)     use_stops=use_stops,
   [3003](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3003)     auto_call_seq=auto_call_seq,
   [3004](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3004)     ffill_val_price=ffill_val_price,
   [3005](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3005)     update_value=update_value,
   [3006](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3006)     max_orders=max_orders,
   [3007](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3007)     max_logs=max_logs,
   [3008](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3008)     flex_2d=close.ndim == 2
   [3009](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3009) )
   [3011](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3011) # Create an instance
   [3012](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3012) return cls(
   [3013](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3013)     wrapper,
   [3014](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3014)     close,
   (...)
   [3020](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3020)     **kwargs
   [3021](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/base.py:3021) )

File [~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/nb.py:1724](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/nb.py:1724), in signals_to_size_nb()
   [1722](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/nb.py:1722) if not np.isnan(size):
   [1723](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/nb.py:1723)     if size_type == SizeType.Percent:
-> [1724](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/nb.py:1724)         raise ValueError(
   [1725](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/nb.py:1725)             "SizeType.Percent does not support position reversal using signals")
   [1726](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/nb.py:1726)     if size_type == SizeType.Value:
   [1727](https://file+.vscode-resource.vscode-cdn.net/Users/kevin851066/Desktop/JZ/range_filter/~/opt/anaconda3/envs/quant/lib/python3.8/site-packages/vectorbt/portfolio/nb.py:1727)         order_size -= size [/](https://file+.vscode-resource.vscode-cdn.net/) val_price_now

ValueError: SizeType.Percent does not support position reversal using signals

Is there any methods that can implement it?

The first straightforward method came up with my mind is that I can let short_entries = np.roll(exits, 1) to make them not to happen at the same time. But this method will make the backtest result inaccurate since my short_entries lag by one timeframe

Thanks in advance!

polakowo commented 3 months ago

You just need to remove size and size_type, VBT enters with full capital by default.

polakowo commented 3 months ago

And for position reversal, instead of providing short_entries and short_exits you can simply use direction="both".

kevin851066 commented 3 months ago

I see. Thank for your quick reply!

Another question: I would like to calculate the benchmark's mdd, sharpe ratio. Could it be done by pf.stats()?

My current usage for pf.stats():

stats = pf.stats(metrics=[
            'start',
            'end',
            'total_return',
            'benchmark_return',
            'sharpe_ratio',
            'max_dd',
            'total_trades',
            'win_rate'], agg_func=None)