Dave-Vallance / bt-ccxt-store

Fork of Ed Bartosh's CCXT Store Work
MIT License
422 stars 185 forks source link

Bracket Order Parameters #4

Open Dave-Vallance opened 5 years ago

Dave-Vallance commented 5 years ago

When creating a buy and sell order, some exchanges (Binance) can return an error if Backtrader's Bracket order kwargs parent and transmit are present in the request.

The current workaround is to delete the kwargs before submitting the order. However, this might limit integration of bracket orders in the future.

As such, a more elegant solution would be good.

Example of deletion in current code base:

def buy(self, owner, data, size, price=None, plimit=None,
            exectype=None, valid=None, tradeid=0, oco=None,
            trailamount=None, trailpercent=None,
            **kwargs):

        del kwargs['parent']
        del kwargs['transmit']

        return self._submit(owner, data, exectype, 'buy', size, price, kwargs)

Reference to pull request: #2

Quanatee commented 5 years ago

I tried to trail the code with references from onandabroker.py, _transmit(), similar to _submit()

I don't have good grasp of your work yet, but it seems to me that enabling bracket orders could be a matter of restructing the broker code to function similar to the other samples in Backtrader's official repo? The biggest differences in style seem to be that ccxtbroker.py creates and submits the ccxt order first, before creating the Backtrader Order object in the submit() method;

def _submit(self, owner, data, exectype, side, amount, price, params):
...
        ret_ord = self.store.create_order(symbol=data.symbol, order_type=order_type, side=side,
                                          amount=amount, price=price, params=params)

        _order = self.store.fetch_order(ret_ord['id'], data.symbol)
        order = CCXTOrder(owner, data, _order) # Subclass of OrderBase
...

Whereas the official broker samples create the Order object in the buy() method and sends the object to transmit()/_submit(). Here is the buy() from onandabroker.py, where BuyOrder() is the subclass of OrderBase, just like CCXTOrder():

def buy(self, owner, data,
            size, price=None, plimit=None,
            exectype=None, valid=None, tradeid=0, oco=None,
            trailamount=None, trailpercent=None,
            parent=None, transmit=True,
            **kwargs):

        order = BuyOrder(owner=owner, data=data,
                         size=size, price=price, pricelimit=plimit,
                         exectype=exectype, valid=valid, tradeid=tradeid,
                         trailamount=trailamount, trailpercent=trailpercent,
                         parent=parent, transmit=transmit)

        order.addinfo(**kwargs)
        order.addcomminfo(self.getcommissioninfo(data))
        return self._transmit(order) # Equivalent to _submit() in ccxtbroker.py

Following which, getting bracket function could be a matter of adding the following code snippet (just an example):

def _submit(self, owner, data, exectype, side, amount, price, params):
...
       ...
        order = CCXTOrder(owner, data, _order) # Subclass of OrderBase

        oref = order.ref
        pref = getattr(order.parent, 'ref', oref)  # parent ref or self

        if params.transmit:
            if params.parent != order.ref:  # children order

                # Put parent in orders dict, but add stopside and takeside
                # to order creation. Return the takeside order, to have 3s
                takeside = order  # alias for clarity
                parent, stopside = self.opending.pop(pref)
                for o in parent, stopside, takeside:
                    self.orders[o.ref] = o  # write them down

                self.brackets[pref] = [parent, stopside, takeside]
                self.o.order_create(parent, stopside, takeside)
                return takeside  # parent was already returned

            else:  # Parent order, which is not being transmitted
                self.orders[order.ref] = order
                return self.o.order_create(order)