Cost of sell and buy is always the same?
In def notify_order(self, order):
the cost of sell and buy is always the same.
Why doesn't order.executed.value change with price multiplied by size?
@bomby9012 Could you please be more specific:
when do you expect the order.executed.value to be updated ?
could you provide a code sample where the unexpected behavior is observed ( some logs would be helpful )
@vladisld yes, of course.
def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted to/by broker - Nothing to do return # Check if an order has been completed # Attention: broker could reject order if not enough cash if order.status in [order.Completed]: if order.isbuy(): # Buy self.log( 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.buyprice = order.executed.price self.buycomm = order.executed.comm else: # Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log('Order Canceled/Margin/Rejected') # Write down: no pending order self.order = None def notify_trade(self, trade): if not trade.isclosed: return self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f, TV %.2f' % (trade.pnl, trade.pnlcomm, trade.value))
In def notify_trader(), the Sell cost is the same with Buy cost, even when the sell exectuted price has changed.
In another post, https://community.backtrader.com/topic/1038/order-execution-price
you can see it is suggested that 'what you might want to do is to use the notify_trade() method and check trade.value', and I did. I added a 'trade.value' in def notify_trade(), the nothing was changed except a zero tradevalue was added in OPERATION PROFIT log. The sell and buy cost remained the same afterall.
What is wrong with my program or me? Please help me ... having lost two days of quality sleep over this SINGLE problem...
@vladisld this is the result:
2020-12-24, Close, 18.26
2020-12-25, Close, 18.04
2020-12-28, Close, 18.85
2020-12-28, BUY CREATE, 18.85
2020-12-29, BUY EXECUTED, Price: 18.87, Cost: 1887.00, Comm 0.94
2020-12-29, Close, 19.17
2020-12-30, Close, 19.20
2020-12-31, Close, 19.34
2021-01-04, Close, 18.60
2021-01-04, SELL CREATE, 18.60
2021-01-05, SELL EXECUTED, Price: 18.40, Cost: 1887.00, Comm 0.92
2021-01-05, OPERATION PROFIT, GROSS -47.00, NET -48.86, TV 0.00
2021-01-05, Close, 18.17
2021-01-06, Close, 19.56
Yes, but after reading it about a million times, I still couldn't figure it out.
Why doesn't the execution price change when calculating executed.value?
@bomby9012 you're right - it is not documented well enough.
order.executed.valueis not straight forward and is not always equal
price * size, as it takes into account the commissions info, margins, short sales - not sure I could correctly and fully describe the logic here as well ( will appreciate if somebody could provide it please )
IMHO if all you need is actually the
price * size- just calculate it yourself ( or probably just simply print the
IMHO if all you need is actually the price * size - just calculate it yourself ( or probably just simply print the price and size instead )
Agreed, for anything more than a simple transaction, eg buy then sell, no commission, I calculate whatever value I want myself. It's not to say that the value is incorrect in backtrader, it's just that it takes in a lot of factors.
Thank you, sir! I think I'm getting the hang of it.
Let me put it this way, plz check if it's correct.
If the buy and sell sizes are the same:
after buying a stock, the broker gives a cost,
then at selling, because no extra stock is borrowed from him,
the broker just sells what was bought and presents the buy price*size.
So the 'cost' of selling the same amount of buy size doesn't actually make sense.
but if the sell size is bigger than buy size:
at selling, I have to actually borrow some size (sell size - buy size) of stocks from broker,
and the broker will have to use the current price (sell executed price) to calculate its value.
Hence a meaningful sell cost (a portion of it of course) is presented.
Here is the result from adding another 100 sell size at selling.
2001-09-18, Close, 4.90
2001-09-19, Close, 4.99
2001-09-19, BUY CREATE, 4.99
2001-09-20, BUY EXECUTED, Price: 4.98, Size: 100.00, Cost: 498.38
2001-09-20, Close, 4.94
2001-09-21, Close, 4.87
2001-09-21, SELL CREATE, 4.87
2001-09-24, SELL EXECUTED, Price: 4.87, Size: -100.00, Cose: 498.38, Comm 0.49
2001-09-24, SELL EXECUTED, Price: 4.87, Size: -100.00, Cose: -487.03, Comm 0.49
2001-09-24, OPERATION PROFIT, GROSS -11.35, NET -12.34
2001-09-24, Close, 4.87
2001-09-25, Close, 4.89
Your question caused me to do a bit of a deep dive.
The code for orders is in bt.order.
All orders are created in what are called bits. Bits are collected and form orders.
The order life starts with:
class OrderExecutionBit(object): Member Attributes: - dt: datetime (float) execution time - size: how much was executed - price: execution price - closed: how much of the execution closed an existing postion - opened: how much of the execution opened a new position - openedvalue: market value of the "opened" part - closedvalue: market value of the "closed" part - closedcomm: commission for the "closed" part - openedcomm: commission for the "opened" part - value: market value for the entire bit size - comm: commission for the entire bit execution - pnl: pnl generated by this bit (if something was closed) - psize: current open position size - pprice: current open position price def __init__(self, dt=None, size=0, price=0.0, closed=0, closedvalue=0.0, closedcomm=0.0, opened=0, openedvalue=0.0, openedcomm=0.0, pnl=0.0, psize=0, pprice=0.0): ... self.value = closedvalue + openedvalue
We can see the
valuecreated in this
bit. It is the total of the open and
closed portions (total) of the order. This class gets called from
class OrderData(object): ''' Holds actual order data for Creation and Execution. In the case of Creation the request made and in the case of Execution the actual outcome. Member Attributes: - exbits : iterable of OrderExecutionBits for this OrderData - dt: datetime (float) creation/execution time - size: requested/executed size - price: execution price Note: if no price is given and no pricelimite is given, the closing price at the time or order creation will be used as reference - pricelimit: holds pricelimit for StopLimit (which has trigger first) - trailamount: absolute price distance in trailing stops - trailpercent: percentage price distance in trailing stops - value: market value for the entire bit size - comm: commission for the entire bit execution - pnl: pnl generated by this bit (if something was closed) - margin: margin incurred by the Order (if any) - psize: current open position size - pprice: current open position price '''
It has the express purpose of holding the data for creation and execution, which is
where you will find the value bits and totals.
Following are two methods when an order is create.
OrderExecutionBitgets sent to
AddBit. Following are two methods when an order is create.
def add(self, dt, size, price, closed=0, closedvalue=0.0, closedcomm=0.0, opened=0, openedvalue=0.0, openedcomm=0.0, pnl=0.0, psize=0, pprice=0.0): self.addbit( OrderExecutionBit(dt, size, price, closed, closedvalue, closedcomm, opened, openedvalue, openedcomm, pnl, psize, pprice)) def addbit(self, exbit): # Stores an ExecutionBit and recalculates own values from ExBit self.exbits.append(exbit) self.remsize -= exbit.size self.dt = exbit.dt oldvalue = self.size * self.price newvalue = exbit.size * exbit.price self.size += exbit.size self.price = (oldvalue + newvalue) / self.size self.value += exbit.value self.comm += exbit.comm self.pnl += exbit.pnl self.psize = exbit.psize self.pprice = exbit.pprice
Here is where the rubber meets the road and we can see what's going on.
The old and new values are calculated. The new price is:
self.size += exbit.size self.price = (oldvalue + newvalue) / self.size self.value += exbit.value
The exbit.value is from above:
self.value = closedvalue + openedvalue
self.value = closedvalue + openedvalue
The interesting part start where the
openedvalueare calculated and passed to
OrderBase.executemethod. It's the actual calculation of those values that adds all this complexity.
Those values are calculated in the
BackBroker._executemethod. This method is pretty complicated to the point where grasping the full logic is not obvious since it supports various scenarios I've mentioned above (margins, shortcash, commission info ... )
It would be interesting to play with this method and come up with some intuitive explanation about its behavior.
But we are close @run-out :-)
@vladisld Right. I wonder sometimes if there weren't simpler ways of doing the same code.
@run-out Incredibly concrete and detailed! Thank you! Need more time to digest...