Passing *kwargs to a broker



  • Hello,
    I see in the documentation there is a note in the documentation stating order *kwargs will be passed to the broker and create a LIMIT IF TOUCHED order.

    Lets say I pass the optional "stopLoss" parameter to Oanda through a kwarg will backtrader be aware of the existence of this order opposing order? I.e if it is triggered, will backtrader still think self.position == True?

    stopLoss kwarg is documented here
    https://developer.oanda.com/rest-live/orders/#createNewOrder

    I would like to pass the stopLoss kwarg to ensure that the stop loss is only created when the order is accepted/completed. Currently I am handling this by creating a stop loss order when I receive an order.Completed notification when the entry order has completed.

    Thanks!


  • administrators

    Actually that was written when only Interactive Brokers was in place. The extra **kwargs in orders with Oanda are stored as additional information in the order, but not used. The Docs - Oanda shows the actual supported orders. Passing the **kwargs wouldn't be an issued.

    The stopLoss field is watched in the implementation of bracket orders and a similar approach could be used. But it cannot be automatically used. Those are orders running internally in Oanda



  • Ok thanks...

    So using a bracket approach, I suppose I could do the following to set a stop loss without a take profit.

    self.b1 = self.buy(size=size, transmit=False)
    self.s1 = self.sell(exectype=bt.Order.Stop, price=stop_price, size=stop_size, parent=self.b1, transmit=True)
    

    Then when closing manually with self.close() I would need to also manually cancel the stop loss too. (assuming the SL is not hit first)

    self.close()
    self.cancel(self.s1)
    

    Note I tried to do the following which I thought would be a nice way to close a position so I don't have to track the open stop loss order and cancel it myself.

    self.close(parent=self.b1)
    

    I thought it might trigger the cancellation of the stop loss but it appears to create an order that is not executed / completed so I guess it is an invalid use case. (It appears as an open order)


  • administrators

    If you close your position, the stoploss pseudo-order will be cancelled automatically. If you do it manually, you are actually cancelling the real order, because in Oanda, the 3 different orders are actually 1 for the end-user, even if they are clearly 3 on the server side.



  • [Original Reply Removed]

    @backtrader

    Ok - Not sure what I was doing yesterday :p . Using a bracket to create a market order without a take profit on Oanda is as simple as:

    self.buy_bracket(exectype=bt.Order.Market, stopprice=stop_price, size=10)
    

    This works very well.

    One note: If I try to do the same by manually making the orders, like this:

    buy = self.buy(size=size, transmit=False)
    self.sell(exectype=bt.Order.Stop, price=stop_price, size=stop_size, parent=buy, transmit=True)
    
    

    Oanda throws an exception:

     File "/home/dave/.local/lib/python3.5/site-packages/backtrader/brokers/oandabroker.py", line 291, in _transmit
        parent, stopside = self.opending.pop(pref)
    ValueError: not enough values to unpack (expected 2, got 1)
    

    Not sure if it is expected or not but I am happy using the bracket method anyway.

    Also for me issuing a buy_bracket with only a stoploss specified during backtesting (i.e using cerebro broker) results in an additional order being created and they are not removed when issuing self.close().

    In the example below I used a simple script with basic ouput to test. Note:

    • When a buy is executed, sell order is created at the stoploss level and a second sell order is created at the data close level
    • 2 bars later when the SELL is executed, the orders placed above are still open.
    2016-03-01, Close, 1.39856
    Orders open: 0
    SMA Long Signal Received
    Price: 1.39856 vs SMA: 1.3950407142857144
    Stop Price = 1.38856
    2016-03-01, BUY EXECUTED, 1.39856
    2016-03-01, Close, 1.39763
    Orders open: 2
    Order Ref: 2
    Order size: -100
    Order Price: 1.38856
    Order Ref: 3
    Order size: -100
    Order Price: 1.39856
    2016-03-01, Close, 1.39360
    Orders open: 2
    Order Ref: 2
    Order size: -100
    Order Price: 1.38856
    Order Ref: 3
    Order size: -100
    Order Price: 1.39856
    IN A POSITION - Closing Orders
    2016-03-01, SELL EXECUTED, 1.39360
    2016-03-01, Close, 1.39186
    Orders open: 2
    Order Ref: 2
    Order size: -100
    Order Price: 1.38856
    Order Ref: 3
    Order size: -100
    Order Price: 1.39856
    

    Is the method a valid use case or do I have to alter my approach between Oanda broker and Cerebro broker? I try to keep one script for both back testing and live to avoid maintaining two for each strategy.

    This is the next() method of the simple script if interested.

    def next(self):
            # Simply log the closing price of the series from the reference
            self.log('Close, %.5f' % self.dataclose[0])
            open_order = self.broker.get_orders_open()
            print('Orders open: {}'.format(len(open_order)))
            for order in open_order:
                print('Order Ref: {}'.format(order.ref))
                print('Order size: {}'.format(order.size))
                print('Order Price: {}'.format(order.created.price))
            #print(open_order)
            if self.dataclose[0] > self.sma:
                if not self.position:
                    print('SMA Long Signal Received')
                    print('Price: {} vs SMA: {}'.format(self.dataclose[0], self.sma[0]))
                    size = 100
                    stop_size = 100
    
                    stop_price = self.dataclose - 0.01
                    print('Stop Price = {}'.format(stop_price))
    
                    self.buy_bracket(exectype=bt.Order.Market, stopprice=stop_price, size=size)
    
    
            elif self.dataclose[0] < self.sma:
                if self.position:
                    print('IN A POSITION - Closing Orders')
                    self.close()
    

  • administrators

    The problem is that simulating a bracket order ends up creating a 3rd simulated order with buy_bracket, even if you have not given a price, which is then understood as the current price (the close)

    And the manual creation runs into the problem that the 3rd simulated and expected has not been created by you and the code breaks.

    You only need one broker in the system.



  • @backtrader

    Yes I only have one broker in the system at a time. I just wanted to check whether the same code could be used to switch between on or the other depending on whether I want to backtest or go live.

    It looks as though I need to handle setting creating a new position with a stop but without a take profit slightly differently depending on whether I run the script in backtest mode or live through Oanda.

    Thanks again for all the pointers on this topic.


  • administrators

    Upon further examination, the reason not to pass **kwargs with Oanda, unlike with Interactive Brokers, is:

    • With Interactive Brokers the order object gives a hint as to where the key in **kwargs is actually present in the order and can therefore be applied

    • With Oanda the order is simply a dictionary of values to be filled. The end user could actually be sending anything and this would be in the JSON sent to Oanda (whether that's simply digested with no complain by Oanda is something untested)


Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.