Skip to content

Feat/order size #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed

Conversation

kongyujian
Copy link

Added order sizing feature. Usage is as per discussion in #3

Opted to use absolute position size instead of a percentage based off holding cash.

Ran with the code on the example, with a tweak on sizing:

from backtesting import Backtest, Strategy
from backtesting.lib import crossover

from backtesting.test import SMA, GOOG


class SmaCross(Strategy):
    n1 = 10
    n2 = 30

    def init(self):
        self.sma1 = self.I(SMA, self.data.Close, self.n1)
        self.sma2 = self.I(SMA, self.data.Close, self.n2)

    def next(self):
        if crossover(self.sma1, self.sma2):
            self.buy(size=1000)
        elif crossover(self.sma2, self.sma1):
            self.sell(size=1000)


bt = Backtest(GOOG, SmaCross, cash=10000, commission=.002)

output = bt.run()
bt.plot()

On a quick eyeball, it seems to work.

Did write one simple test. Let me know if there needs be further coverage.

Copy link
Owner

@kernc kernc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a daring endeavor, or at least that was the impression I got the last time I was looking at it. Hope it pans out.

Position size likely is the best first step towards #10. The next would probably be order pyramiding so one can do:

buy 2
buy 3
buy 5
sell 10

Have an idea how to go about that?

@@ -493,7 +503,8 @@ def _open_position(self, price, is_long):

i, price = self._get_market_price(price)

position = float(self._cash * self._leverage / (price * (1 + self._commission)))
size = self._cash if self.orders._size is None else self.orders._size
position = float(size * self._leverage / (price * (1 + self._commission)))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this position computation correct? You effectively replace former cash with size, but size actually corresponds to cash / price?

I think when size is given, position might better be float(size) if enough_cash else error?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention for the size variable being computed as such is because:

  1. If size is not specified when performing an order, it will default to the default behaviour of using entire holding cash amount.
  2. I did not notice there was a margin option to define a leveraged trade. (Only found out about it after reading: How to use backtesting.py for forex trading #10 ) Was initially under the assumption that all trades were on a 1:1 leverage. Under my assumption, i believed that specifying the margin option wasn't needed in order to define a "leveraged trade". In fact, by specifying an order size that is larger than your holding cash amount would technically mean you enter a trade on cross margin. (which was why I did not implement a check for if enough_cash). Reason for this assumption is largely influenced by Trading View's pine script, and how "leveraged trades" would be entered under their backtesting tools.

So my thoughts are as follows, and would need some clarification:

  1. Does the library actually calculate liquidations. If it does, the margin option would come in handy for that. A quick look into the code base shows how margin is used to calculate leverage amount. But margin and leverage are simply being used in reporting functions and likely no where else?
  2. Would it make sense to have margin be calculated dynamically if order size was specified, and larger than than holding cash. In that case, we could dynamically calculate a cross margin on init instead.

Would love to take a stab at pyramiding too, there are indeed many use cases for that. Let me know your thoughts. I might be working on a path different than the philosophy of this library. :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the library actually calculate liquidations.

It simulates them.

# Log account equity for the equity curve
equity = self.equity
self.log.equity[i] = equity
# Hovever, if negative, set all to 0 and stop the simulation
if equity < 0:
self._close_position()
self._cash = 0
self.log.equity[i:] = 0
raise _OutOfMoneyError

margin= therefore represents initial and maintenance margin of the trades. It's used only in stats and plots so the interested user gets a rough idea on how leveraged their strategy is allowed to be to still remain afloat.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In our case, position is actually equal to size when the user requests it. Thus, we only need to deduct the commission and then confirm we have (leveraged) cash to cover it all.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think? 😅

@javamon1174
Copy link

An error message is displayed during this operation.

self.sell(tp=7200)

An error occurs when only a target price is specified for a sell order.

Conditional error in broker class

Please solve.

@drajtrade
Copy link

Does this change work properly?

@kernc kernc mentioned this pull request Mar 10, 2020
@P-a-v-e
Copy link

P-a-v-e commented Apr 11, 2020

@kernc I am just starting out with backtesting and it seems like the order sizing isn't in place. I am running backtesting version 0.1.7 however the changes above seem no to be commited.

@kernc
Copy link
Owner

kernc commented Apr 11, 2020

@P-a-v-e PR #47 hasn't been merged yet. Welcome to try it out!

@zachrdlapetr
Copy link

zachrdlapetr commented Jun 1, 2020

@kernc when do you expect to release the 0.2.0 version with this feature?

@kernc kernc force-pushed the master branch 2 times, most recently from 429e859 to de1bcf8 Compare July 1, 2020 03:01
@kernc kernc closed this in #47 Jul 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants