Modifying Trade class to include entry and exit price
-
Hi guys,
I'd like to keep track of trades. So I've been using notify_trade() in my strategy to record trades my system made. [I could also code an Analyzer to do the same].
Ok, so I want to do several things with my list of trades made by system. E.g. plotting trades on my own chart so I can visually see entry and exits.
I need to know; entry price and exit price.If I print a Trade object, the output string is this;
ref:121
data:<backtrader.feeds.csvgeneric.GenericCSVData object at 0x110f19320>
tradeid:1
size:0
price:88.800003
value:0.0
commission:0.0
pnl:-1.0
pnlcomm:-1.0
justopened:False
isopen:False
isclosed:True
baropen:14
dtopen:736338.0006944444
barclose:14
dtclose:736338.0006944444
barlen:0
historyon:False
history:[]
status:2I can see entry price via: 'price' but I problem is I can't easily see exit price (unless I calculate via add/subtract 'pnl' depending if long or short).
I've added two lines of code in trade.py, in Trade's def update(..),
which creates member variables 'entry_price' and 'exit_price'.self.value = comminfo.getvaluesize(self.size, self.price) # My added code.. self.entry_price = self.price # Rich O'Regan added.. self.exit_price = price # Rich O'Regan added.. # Update the history if needed if self.historyon: dt0 = self.data.datetime[0] .. blah blah blah
This simple code addition is helpful because I can now access these values from a Trade object, and do useful things like plot trades on my own charts.
MY QUESTION:
I was surprised Daniel didn't initially have this included. Entry and exit price are a key attribute to have, so maybe there was good reason not to.I'm working on some code that I'd like to make public. Are there any red flags to what I'm doing? Something I may be missing?
Any comments greatly appreciated.
Thanks
-
You may be oversimplifying the problem.
- What is your definition of entry price?
Some background:
- A trade as it seems to be defined can accumulate, for example, 20 buy orders with 20 different entry prices before a reduction of the position is undertaken
In this example it would seem straightforward to average the price using the sizes, but
- Another trade can buy 20 times, sell 10 times, buy again 20 times and then sell 5 to close the position.
It would seem difficult to define what's the entry price, because the position has been growing, diminishing, growing again and is then finally closed.
You can apply the same examples to the exit price, which in the 2nd example is very difficult to define.
If you obviously only have 1 opening order and 1 closing order, your entry and exit prices are clearly defined, but only because the match the 1st and last prices the trade see.
-
Hi Paska, thanks very much for your quick response.
Yeah I was afraid there might be some other issues.
The way I personally backtest, I only buy or sell one future contract. I only test with a size of 1. It makes comparisons between systems easier.
Yes for your example, my code would break!Personally if I want to add more to my position then I would consider that a separate trade. (It seems confusing to buy and sell different amounts at different times and consider this as one trade.)
Just thinking the best way forward. I was going through trade.py code trying to figure out how it all works. Perhaps I could subclass a SimpleTrade object and enforce it to allow only one buy and sell etc and no multiple positions like in your example. And then place trades e.g. buy(mode='simple') e.t.c.
Feel like it's gonna take far too long to implement.
Maybe I should just keep my code private, since I am aware of the issue and will only be using it to trade 1 unit. Therefore don't have to worry about it not fitting correctly in more complex cases.
Thoughts comments greatly appreciated, thanks guys.
-
The above examples model a "long trade" or "short trade" in which you can keep the trade going for a long time. Yours is an order based trade (entry/exit)
Given how easy it is to monkey patch in Python, your best bet would be to override some methods in
Trade
before anything else happens in the system.It may be enough with saving the existing
update
method asold_update
and then adding your own update which on the first heartbeat writes down the entry price and then callsold_update
. And with the second heartbeat, the exit price is noted, piggybacking again onold_update
-
If you want to see by and sell points on the chart, how about tracking it via
notify_order()
?Each time an order is completed you can mark the executed order price and size on the chart (a little like how Tradingview does). That way you can see for example 10 contracts were bought here, 5 sold here, remaining 5 sold there etc.
In your case, buying and selling one contract at a time, the markers would line up with the trade.
-
@Paska-Houso Hey Paska, thanks again. Ok just to clarify what you are saying.
Understood creating 'old_update(..)' and calling it from my overridden update(..).
Couple of issues.
-
If I'm using the first, second heartbeat, is it correct that I will also need to check for tradeid because I often have trades overlapping each other. i.e. I will need to eg put trade in a dict with tradeid as key and count first and 2nd heartbeat associated to that unique id.
-
What advantage will this give me over what I'm doing now. i.e the simple 2 line code I first posted. Both are incompatible with more complex cases and both will work for simple cases.
Many thanks
-
-
@ThatBlokeDave Hey cheers Dave. Another interesting way to solve problem. Yeah I think your method is better actually, I hadn't considered this.
However since for myself I will likely only ever backtest the simple type of entry/exit, I think its easier to not worry about trying to implement the more complex open ended trade. Though if I wanted to make public I'd definitely reconsider.
Check this out what I've done so far. Using Plot.ly for convenience as nice interactivity (if anybody knows anything better pls let me know). This is a fictional test system and doesn't actually make money. You can see I can enter multiple trades and see entry, exit and profit. (Green lines = profitable trade, red = loss).
I also made profit use R-Multiples instead of raw price profit (another hack that probably will crash in complex cases!). I'll prob make a separate post of this plotting another time, I find it really helpful to check my logic is working correctly.
-
@Richard-O'Regan
Looks great - Is this plotting after the backtest has ended? On my long list of "things to do but probably will never get around to", is to create an extension for live plotting. Like your example, visualizing what is happening really helps when working with people who are not coming from a technical background.
I looked at Bokeh a while ago when I was day dreaming about it.
https://bokeh.pydata.org/en/latest/
Not sure if it is better than plotly but worth a look for comparison.
-
Yeah I was initially considering Bokeh. It seems the plotting wasn't quite as nice but I think is more flexible. It was 50:50 between them.. if anyone can say for sure pros / cons which is better, I may switch to Bokeh.
Yes correct, plots after backtest complete.
Right now my code is spread out in a Jupiter notebook, I can plot trades and also do heatmaps/3d charts of optimisations. I'm looking to make more of a solid environment to test my ideas. Once I set up I can release the code, you should be able to plug into your backtesting easy. It's not too long or complex.
Here are a couple of screenshots from backtesting another system. Surprisingly quick to do these plots.
-
@Richard-O-Regan
Hi! Nice plots. I was looking intoBokeh
sometime ago, but wasn't able to do any valuable staff. Not a lot of experience with python.Can you share more details on the plotting with alternative package? Do you pass
bt
data directly into it or pass it topandas
(for example) first and then use in the plotting package?