Fractional Position Size - oandav20store.py
-
I am trying to alter the oandav20store.py file to allow for fractional position sizes. The following function (in its original format) altered all create order sizes to be integers.
When I delete the 'int' from the code and try to place an order with self.stake=1.4 it shows on OANDA activity as 'MARKET ORDER REJECT' with unit size 1.4.
To test this with a hardcoded figure, if I replace the 'order.created.size' with '1.6' (just to be different), then the order is placed correctly and accepted by OANDA at 1.6 Units with a Stop at 1.6 Units.
Anyone have idea on how to get order.created.size to work without being an integrer and equal the size of the buy order so that I can use self.stake as a dynamic variable?
#PLACE ORDER self.stake = 1.4 self.buy(exectype=bt.Order.Market, size=self.stake) self.sell(exectype=bt.Order.Stop, price=self.stop_loss, size=self.stake)
From oandav20store.py:
def order_create(self, order, stopside=None, takeside=None, **kwargs): '''Creates an order''' okwargs = dict() okwargs['instrument'] = order.data._dataname okwargs['units'] = ( #ORIGINAL CODE #abs(int(order.created.size)) if order.isbuy() #else -abs(int(order.created.size))) # negative for selling #DOES NOT WORK WITH 'int' REMOVED abs(order.created.size) if order.isbuy() else -abs(order.created.size)) # negative for selling #DOES WORK IF A FIXED DECIMAL VALUE IS USED FOR TESTING #abs(1.6) if order.isbuy() #else -abs(1.6)) # negative for selling okwargs['type'] = self._ORDEREXECS[order.exectype] if order.exectype != bt.Order.Market: okwargs['price'] = format( order.created.price, '.%df' % order.data.contractdetails['displayPrecision']) if order.valid is None: okwargs['timeInForce'] = 'GTC' # good to cancel else: okwargs['timeInForce'] = 'GTD' # good to date gtdtime = order.data.num2date(order.valid) okwargs['gtdTime'] = gtdtime.strftime(self._DATE_FORMAT) if order.exectype == bt.Order.StopLimit: okwargs['priceBound'] = order.created.pricelimit if order.exectype == bt.Order.StopTrail: okwargs['distance'] = format( order.trailamount, '.%df' % order.data.contractdetails['displayPrecision']) if stopside is not None: if stopside.exectype == bt.Order.StopTrail: okwargs['trailingStopLossOnFill'] = v20.transaction.TrailingStopLossDetails( distance=format( stopside.trailamount, '.%df' % order.data.contractdetails['displayPrecision']), clientExtensions=v20.transaction.ClientExtensions( id=str(stopside.ref) ).dict() ).dict() else: okwargs['stopLossOnFill'] = v20.transaction.StopLossDetails( price=format( stopside.price, '.%df' % order.data.contractdetails['displayPrecision']), clientExtensions=v20.transaction.ClientExtensions( id=str(stopside.ref) ).dict() ).dict() if takeside is not None and takeside.price is not None: okwargs['takeProfitOnFill'] = v20.transaction.TakeProfitDetails( price=format( takeside.price, '.%df' % order.data.contractdetails['displayPrecision']), clientExtensions=v20.transaction.ClientExtensions( id=str(takeside.ref) ).dict() ).dict() # store backtrader order ref in client extensions okwargs['clientExtensions'] = v20.transaction.ClientExtensions( id=str(order.ref) ).dict() okwargs.update(**kwargs) # anything from the user self.q_ordercreate.put((order.ref, okwargs,)) # notify orders of being submitted self.broker._submit(order.ref) if stopside is not None: # don't make price on stopside mandatory self.broker._submit(stopside.ref) if takeside is not None and takeside.price is not None: self.broker._submit(takeside.ref) return order```
-
SOLVED
Turns out that OANDA needs to received the value as a string. The following changes to oandav20store.py now allow for fractional order sizes.
Warning... make sure what ever sizer calculation you are using doesn't leave minor decimal positions open. Build your own sizer model!
def order_create(self, order, stopside=None, takeside=None, **kwargs): '''Creates an order''' okwargs = dict() okwargs['instrument'] = order.data._dataname okwargs['units'] = ( #ORIGINAL CODE #abs(int(order.created.size)) if order.isbuy() #else -abs(int(order.created.size))) # negative for selling #NEW CODE: WORKS WITH FRACTIONAL ORDER SIZES str(abs(order.created.size)) if order.isbuy() else str(-abs(order.created.size))) # negative for selling