Lumiwealth / lumibot

Backtesting and Trading Bots Made Easy for Crypto, Stocks, Options, Futures, FOREX and more
GNU General Public License v3.0
885 stars 162 forks source link

Backtesting broker should check cash before processing pending orders #439

Open Canx opened 5 months ago

Canx commented 5 months ago

In method process_pending_orders we should check if cash is enough to process order.

jimwhite commented 5 months ago

We routinely test strategies that use margin so we'd need to add and check that too. Margin limits are usually not fixed but a function of the account's cleared assets. Perhaps a little more complex than you expected but totally worthwhile in my opinion.

While it would be great to include modeling of the trade clearing process, we could approach margin in three stages:

1) Add a fixed margin limit which basically works like an extension to cash. An infinite option here preserves the current behavior and could/should be the default for backwards compatibility. This would require tracking whether a trade is done on cash or margin so the cash value of the portfolio (i.e. assets bought with cash plus the cash) can be calculated.

2) Add a margin percentage option. This is a typical policy.

3) Add margin interest. Margin is a loan and you pay daily interest. This will improve backtest accuracy.

4) Add modeling of trade clearing. This means there is a third state of a trade which means it hasn't settled yet and so is can't be counted as having cash value even when the trade is "cash".

Settlement used to be 3 biz days and is now 2 but I just found out it changes to 1 this month: https://www.finra.org/investors/insights/understanding-settlement-cycles

For details on how to do this you can check out backtester code that supports margin like Backtrader: https://github.com/mementum/backtrader/blob/b853d7c90b6721476eb5a5ea3135224e33db1f14/backtrader/order.py#L488

grzesir commented 5 months ago

We routinely test strategies that use margin so we'd need to add and check that too. Margin limits are usually not fixed but a function of the account's cleared assets. Perhaps a little more complex than you expected but totally worthwhile in my opinion.

While it would be great to include modeling of the trade clearing process, we could approach margin in three stages:

1) Add a fixed margin limit which basically works like an extension to cash. An infinite option here preserves the current behavior and could/should be the default for backwards compatibility. This would require tracking whether a trade is done on cash or margin so the cash value of the portfolio (i.e. assets bought with cash plus the cash) can be calculated.

2) Add a margin percentage option. This is a typical policy.

3) Add margin interest. Margin is a loan and you pay daily interest. This will improve backtest accuracy.

4) Add modeling of trade clearing. This means there is a third state of a trade which means it hasn't settled yet and so is can't be counted as having cash value even when the trade is "cash".

Settlement used to be 3 biz days and is now 2 but I just found out it changes to 1 this month:

https://www.finra.org/investors/insights/understanding-settlement-cycles

For details on how to do this you can check out backtester code that supports margin like Backtrader:

https://github.com/mementum/backtrader/blob/b853d7c90b6721476eb5a5ea3135224e33db1f14/backtrader/order.py#L488

Couldn't agree more. We should make some tests for this

grzesir commented 5 months ago

If we want to add margin into backtesting then we will need to be able to set parameters around it

Canx commented 5 months ago

Yes, more complex than expected @jimwhite!

@grzesir, maybe we could start adding an account_type parameter here that could have those types of accounts and make the INFINITE_MARGIN (or similar) the default value.

grzesir commented 5 months ago

Yes, more complex than expected @jimwhite!

@grzesir, maybe we could start adding an account_type parameter here that could have those types of accounts and make the INFINITE_MARGIN (or similar) the default value.

This sounds like a good idea. At the very least it would be good to specify whether margin is possible for not.

It might also make sense to go a little further and allow a "max margin" parameter or something. For example, most stock brokers only allow you to borrow maximum 30% of your account value. This would require more work but would be super valuable if you're up for the challenge.

Canx commented 5 months ago

Challenge accepted, let's see if we have time ;) As an exploratory idea we could pass an Account object to the broker, something like this:

account = MarginAccount(max_margin=0.3) broker = BacktestingBroker(data_source, account,...)

and then something like:

class MarginAccount(Account) class CashAccount(Account)

class Account(ABC): init(initial_cash): def withdraw(): def deposit(): def calculate overdrafts(): etc....

grzesir commented 5 months ago

Awesome! Love your energy Ruben!

Margin can be tricky. I think if you want to add margin as a capability in Backtesting it would be worthwhile to read/watch a bit about how margin calls happen. This can be different for long/short positions and different asset types.

One change I would recommend to what you outlined is to have a different margin for longs, shorts and asset types. Eg. Your MarginAccount object could have some way to specify whether it’s long or short, and what asset it pertains to.

Just a suggestion. I know this can be hard to implement in practice

Robert Grzesik 347-635-3416

On Wed, May 8, 2024 at 10:56 AM Ruben Cancho @.***> wrote:

Challenge accepted, let's see if we have time ;) As an exploratory idea we could pass an Account object to the broker, something like this:

account = MarginAccount(max_margin=0.3) broker = BacktestingBroker(data_source, account,...)

and then something like:

class MarginAccount(Account) class CashAccount(Account)

class Account(ABC): init(initial_cash): def withdraw(): def deposit(): def calculate overdrafts(): etc....

— Reply to this email directly, view it on GitHub https://github.com/Lumiwealth/lumibot/issues/439#issuecomment-2100780885, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAIYQK22CD6SWXETWK5T5O3ZBI4LTAVCNFSM6AAAAABHHRJXLKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCMBQG44DAOBYGU . You are receiving this because you were mentioned.Message ID: @.***>