polakowo / vectorbt

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

Applying Indicator to Multiple Assets to Create Combined Portfolio? #27

Closed kmcentush closed 4 years ago

kmcentush commented 4 years ago

I looked through the current examples but was unable to find any documentation on how to create/backtest a combined portfolio. Is this possible on the platform?

Loving everything else! The runtime is unparalleled.

polakowo commented 4 years ago

If you're looking to have multiple assets that are independent of each other (separate tests), this is possible, see here. But if you want to have a portfolio with multiple assets using the same capital and calculate performance metrics for them combined, it's not currently possible (but soon will be in the works).

kmcentush commented 4 years ago

Hi @polakowo, thanks for the response. I'm specifically looking for the latter where the combined portfolio uses the same capitol and performance metrics.

I'm not sure if now is the best time to also include this request, but will bet sizing also be implemented when you being work on this feature? Ex: some days an indicator may say to take a 25% position in BTC, 75% in ETH. The next day it may be 27% and 73%, etc.

polakowo commented 4 years ago

Yes it will be possible to rebalance each time step. I will first release the version 0.13 where you will have more options to specify size to buy/sell, for example in percentage of portfolio value for that column. And the version 0.14 will make possible gather columns into groups sharing the same capital.

kmcentush commented 4 years ago

That sounds like it will cover just about all of my use cases (especially if 0.13 also supports shorting). Thank you for the amazing work thus far!

polakowo commented 4 years ago

Shorting is a different story and much more complex to integrate into current codebase, so I do not expect to release it with 0.13 just yet.

kmcentush commented 4 years ago

Hi @polakowo, thanks for the v0.13 release! I took a look at the documentation and it looks like the size argument in portfolio.from_signals() may do the trick for varied sizes. Two questions about the implementation:

  1. Rather than specifying share count, can we specify a percentage of the portfolio? I think another SizeType is needed for portfolio percentage. Edit: I think you actually do support a SizeType for it. The docs here show it, while the docs on portfolio state only type 0 and 2 are supported.
  2. It looks like the entire position is closed if an exit signal is present. Is there a different portfolio method that will not close the entire position, but rather just sell until the desired holdings % is achieved? I'm not sure if the accumlate argument can achieve this or not. Maybe a custom implementation of simulate_nb() is needed...

Thank you again for your amazing work!

polakowo commented 4 years ago

Hi @kmcentush, the ‘from_signals- method only accepts those those two types because otherwise you could place an entry signal and specify target percentage which happens to be below the current portfolio value and so your entry would become an exit and this is counterintuitive. Signals should always remain binary and in the same direction the user has provided. Things such as target value can reverse that direction based on your current holdings and basically contradict the signal you placed. The most flexible method is ‘from_orders’, which can accept any size type. You need only to convert your both signal arrays to one size array and that’s it.

kmcentush commented 4 years ago

Got it. Looks like you addressed both of my questions with one answer! Thanks!

polakowo commented 4 years ago

Setting ‘accumulate’ to True will allow you to increase or decrease your position gradually, but again, you need absolute size numbers for it to work. Edit: True instead of False.

Glad I could help, and don’t hesitate to give any feedback or sources where it’s implemented in a more intuitive way, it’s great to discover different approaches to same problems.

kmcentush commented 4 years ago

I'm struggling to use the from_orders() function with a multidimensional orders array. Is this not supported at this time?

Code:

# Get prices
prices = pd.DataFrame(np.array([[1, 1.1, 1.05, 1.11, 1.12], [1.03, 1.13, 1.07, 1.12, 1.13]]).T, columns=['BTC', 'ETH'])
prices.columns.name = 'asset'
prices.index.name = 'Date'

print(prices)

# Get order target percentages
cols = pd.MultiIndex.from_product([[1, 2], [2, 3], ['BTC', 'ETH']])
tgt_pct = pd.DataFrame(np.array([[0, 0.02, 0.03, 0, 0.05], [0.01, 0.03, 0.01, 0.02, 0.04],
                                [0, 0.04, 0.01, 0.02, 0.04], [0.03, 0.05, 0, 0.02, 0.03],
                                [0.01, 0.03, 0.01, 0.02, 0.04], [0, 0.04, 0.01, 0.02, 0.04],
                                [0.03, 0.05, 0, 0.02, 0.03], [0.01, 0.03, 0.01, 0.02, 0.04],
]).T, columns=cols)
tgt_pct.columns.names = ['custom_param1', 'custom_param2', 'asset']
tgt_pct.index.name = 'Date'

print(tgt_pct)

# Run the portfolio
size_type = getattr(vbt.portfolio.enums.SizeType, 'TargetPercent')
portfolio = vbt.Portfolio.from_orders(prices, tgt_pct, size_type=size_type)

I'm getting a ValueError: shape mismatch: objects cannot be broadcast to a single shape error. The code above reproduces it on v0.13

polakowo commented 4 years ago

Based on Numpy broadcasting rules you need at least one array that has an axis of length one or both to have the same shape. Your first array is of shape (5, 2), second of shape (5, 8). To make it work you have two options: tile the first dataframe 4 times using ’prices.vbt.tile(4)’, or align it to the shape of the second array using ‘prices.vbt.align_to(tgt_pct)’. I tried to make vectorbt do it automatically but there are some caveats with multiple arrays broadcasting to each other, so for now you have to do it manually (which is easy anyway).

polakowo commented 4 years ago

Implemented in #46