Search code examples
pythonbotstradingalgorithmic-tradingib-api

Trouble getting ibapi to place trades


I am trying my hand at making my first algo-bot and decided to go with IB and use their api. I have followed Jacob Aramral's youtube series about making a bot and pretty much followed along and added tweaks along the way to make the logic performed how I wanted. After trying to run the code, I kept getting errors about not being able to connect. So, I just copied his code and tried to run that (https://github.com/Jake0303/InteractiveBrokersPythonBot/blob/main/InteractiveBrokersPythonBot.py). It connects to TWS (and I assume IB servers) and I can enter a ticker symbol and get real time data, but it wouldn't make trades. I then tweaked Jacob's code to basically make trades at every candle (just because it was reading my paper account and I wanted to see any trades be made), but when the criteria is met (last close higher than the close of the candle before) no trade is made. I'm starting to get a little discouraged, so hopefully someone can help me out. I also tried copying the intro guide here to try and get TWS to make a trade, but still no luck. If anyone can see what I'm doing wrong and can help me get it fixed, I would be grateful.

I have my IBpro account funded with play money and have subscribed to 'US Equity and Options Add-On Streaming Bundle' and the 'US Securities Snapshot and Futures Value Bundle' subscriptions. For the paper account, I use 7497 as the socket port, checked the Active X and socket clients, and disabled read-only API.I believe that's all I needed to enable to allow the API to make trades for me (aside from having a functional code, lol). Here is the code that should work. I would be curious if it work for other and places trades. Also, I have included a snip of the monitor once the code is ran. Any help is appreciated!

#Imports
import ibapi
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.order import *
import ta
import numpy as np
import pandas as pd
import pytz
import math
from datetime import datetime, timedelta
import threading
import time
#Vars
orderId = 1
#Class for Interactive Brokers Connection
class IBApi(EWrapper,EClient):
    def __init__(self):
        EClient.__init__(self, self)
    # Historical Backtest Data
    def historicalData(self, reqId, bar):
        try:
            bot.on_bar_update(reqId,bar,False)
        except Exception as e:
            print(e)
    # On Realtime Bar after historical data finishes
    def historicalDataUpdate(self, reqId, bar):
        try:
            bot.on_bar_update(reqId,bar,True)
        except Exception as e:
            print(e)
    # On Historical Data End
    def historicalDataEnd(self, reqId, start, end):
        print(reqId)
    # Get next order id we can use
    def nextValidId(self, nextorderId):
        global orderId
        orderId = nextorderId
    # Listen for realtime bars
    def realtimeBar(self, reqId, time, open_, high, low, close,volume, wap, count):
        super().realtimeBar(reqId, time, open_, high, low, close, volume, wap, count)
        try:
            bot.on_bar_update(reqId, time, open_, high, low, close, volume, wap, count)
        except Exception as e:
            print(e)
    def error(self, id, errorCode, errorMsg):
        print(errorCode)
        print(errorMsg)
#Bar Object
class Bar:
    open = 0
    low = 0
    high = 0
    close = 0
    volume = 0
    date = datetime.now()
    def __init__(self):
        self.open = 0
        self.low = 0
        self.high = 0
        self.close = 0
        self.volume = 0
        self.date = datetime.now()
#Bot Logic
class Bot:
    ib = None
    barsize = 1
    currentBar = Bar()
    bars = []
    reqId = 1
    global orderId
    smaPeriod = 50
    symbol = ""
    initialbartime = datetime.now().astimezone(pytz.timezone("America/New_York"))
    def __init__(self):
        #Connect to IB on init
        self.ib = IBApi()
        self.ib.connect("127.0.0.1", 7497,221)
        ib_thread = threading.Thread(target=self.run_loop, daemon=True)
        ib_thread.start()
        time.sleep(1)
        currentBar = Bar()
        #Get symbol info
        self.symbol = input("Enter the symbol you want to trade : ")
        #Get bar size
        self.barsize = int(input("Enter the barsize you want to trade in minutes : "))
        mintext = " min"
        if (int(self.barsize) > 1):
            mintext = " mins"
        queryTime = (datetime.now().astimezone(pytz.timezone("America/New_York"))-timedelta(days=1)).replace(hour=16,minute=0,second=0,microsecond=0).strftime("%Y%m%d %H:%M:%S")
        #Create our IB Contract Object
        contract = Contract()
        contract.symbol = self.symbol.upper()
        contract.secType = "STK"
        contract.exchange = "SMART"
        contract.currency = "USD"
        self.ib.reqIds(-1)
        # Request Market Data
        #self.ib.reqRealTimeBars(0, contract, 5, "TRADES", 1, [])
        self.ib.reqHistoricalData(self.reqId,contract,"","2 D",str(self.barsize)+mintext,"TRADES",1,1,True,[])
    #Listen to socket in seperate thread
    def run_loop(self):
        self.ib.run()
    #Bracet Order Setup
    def bracketOrder(self, parentOrderId, action, quantity, profitTarget, stopLoss):
        #Initial Entry
        #Create our IB Contract Object
        contract = Contract()
        contract.symbol = self.symbol.upper()
        contract.secType = "STK"
        contract.exchange = "SMART"
        contract.currency = "USD"
        # Create Parent Order / Initial Entry
        parent = Order()
        parent.orderId = parentOrderId
        parent.orderType = "MKT"
        parent.action = action
        parent.totalQuantity = quantity
        parent.transmit = False
        # Profit Target
        profitTargetOrder = Order()
        profitTargetOrder.orderId = parent.orderId+1
        profitTargetOrder.orderType = "LMT"
        profitTargetOrder.action = "SELL"
        profitTargetOrder.totalQuantity = quantity
        profitTargetOrder.lmtPrice = round(profitTarget,2)
        profitTargetOrder.parentId = parentOrderId
        profitTargetOrder.transmit = False
        # Stop Loss
        stopLossOrder = Order()
        stopLossOrder.orderId = parent.orderId+2
        stopLossOrder.orderType = "STP"
        stopLossOrder.action = "SELL"
        stopLossOrder.totalQuantity = quantity
        stopLossOrder.parentId = parentOrderId
        stopLossOrder.auxPrice = round(stopLoss,2)
        stopLossOrder.transmit = True

        bracketOrders = [parent, profitTargetOrder, stopLossOrder]
        return bracketOrders
    #Pass realtime bar data back to our bot object
    def on_bar_update(self, reqId, bar,realtime):
        global orderId
        #Historical Data to catch up
        if (realtime == False):
            self.bars.append(bar)
        else:
            bartime = datetime.strptime(bar.date,"%Y%m%d %H:%M:%S").astimezone(pytz.timezone("America/New_York"))
            minutes_diff = (bartime-self.initialbartime).total_seconds() / 60.0
            self.currentBar.date = bartime
            lastBar = self.bars[len(self.bars)-1]
            #On Bar Close
            if (minutes_diff > 0 and math.floor(minutes_diff) % self.barsize == 0):
                self.initialbartime = bartime
                #Entry - If we have a higher high, a higher low and we cross the 50 SMA Buy
                #1.) SMA
                closes = []
                for bar in self.bars:
                    closes.append(bar.close)
                self.close_array = pd.Series(np.asarray(closes))
                self.sma = ta.trend.sma(self.close_array,self.smaPeriod,True)
                print("SMA : " + str(self.sma[len(self.sma)-1]))
                #2.) Calculate Higher Highs and Lows
                lastLow = self.bars[len(self.bars)-1].low
                lastHigh = self.bars[len(self.bars)-1].high
                lastClose = self.bars[len(self.bars)-1].close

                # Check Criteria
                if (bar.close > lastHigh
                    and self.currentBar.low > lastLow
                    and bar.close > str(self.sma[len(self.sma)-1])
                    and lastClose < str(self.sma[len(self.sma)-2])):
                    #Bracket Order 2% Profit Target 1% Stop Loss
                    profitTarget = bar.close*1.02
                    stopLoss = bar.close*0.99
                    quantity = 1
                    bracket = self.bracketOrder(orderId,"BUY",quantity, profitTarget, stopLoss)
                    contract = Contract()
                    contract.symbol = self.symbol.upper()
                    contract.secType = "STK"
                    contract.exchange = "SMART"
                    contract.currency = "USD"
                    #Place Bracket Order
                    for o in bracket:
                        o.ocaGroup = "OCA_"+str(orderId)
                        self.ib.placeOrder(o.orderId,contract,o)
                    orderId += 3
                #Bar closed append
                self.currentBar.close = bar.close
                print("New bar!")
                self.bars.append(self.currentBar)
                self.currentBar = Bar()
                self.currentBar.open = bar.open
        #Build  realtime bar
        if (self.currentBar.open == 0):
            self.currentBar.open = bar.open
        if (self.currentBar.high == 0 or bar.high > self.currentBar.high):
            self.currentBar.high = bar.high
        if (self.currentBar.low == 0 or bar.low < self.currentBar.low):
            self.currentBar.low = bar.low

#Start Bot
bot = Bot()

Solution

  • Made a few adjustments, including a message when criteria is not met. Should get you started

    #Imports
    import ibapi
    from ibapi.client import EClient
    from ibapi.wrapper import EWrapper
    from ibapi.contract import Contract
    from ibapi.order import *
    
    import pandas as pd
    import pytz
    import math
    from datetime import datetime, timedelta
    import threading
    import time
    #Vars
    orderId = 1
    
    #Class for Interactive Brokers Connection
    class IBApi(EWrapper,EClient):
        def __init__(self):
            EClient.__init__(self, self)
        # Historical Backtest Data
        def historicalData(self, reqId, bar):
            try:
                myBar = Bar()
                myBar.open = bar.open
                myBar.low = bar.low
                myBar.high = bar.high
                myBar.close = bar.close
                myBar.volume = bar.volume
                myBar.date = pytz.timezone("America/New_York").localize(datetime.strptime(bar.date[:17], "%Y%m%d %H:%M:%S"))
                bot.on_bar_update(reqId,myBar,False)
            except Exception as e:
                print("historicalData Ex: ",e)
    
        # On Historical Data End
        def historicalDataEnd(self, reqId, start, end):
            try:
                print("\nHistorical data recieved: ",start," to ",end)
                print("Waiting for next bar close....")
                contract = Contract()
                contract.symbol = bot.symbol.upper()
                contract.secType = "STK"
                contract.exchange = "SMART"
                contract.currency = "USD"
                bot.ib.reqRealTimeBars(2, contract, 5, "TRADES", False, []) 
            except Exception as e:
                print("historicalDataEnd Ex: ",e)
        # Get next order id we can use
        def nextValidId(self, nextorderId):
            global orderId
            orderId = nextorderId
        # Listen for realtime bars
        def realtimeBar(self, reqId, time, open_, high, low, close,volume, wap, count):
            super().realtimeBar(reqId, time, open_, high, low, close, volume, wap, count)
            
            bar = Bar()
            bar.open = open_
            bar.low = low
            bar.high = high
            bar.close = close
            bar.volume = volume
            bar.date = datetime.fromtimestamp(time, pytz.timezone("America/New_York"))
            bot.on_bar_update(reqId, bar, True)
    
        def error(self, id, errorCode, errorMsg, advOrdRej):  # Depending on API version advOrdRej may not be required
            print(errorCode,": ",errorMsg)
    
    
    #Bar Object
    class Bar:
        open = 0
        low = 0
        high = 0
        close = 0
        volume = 0
        date = datetime.now()
        def __init__(self):
            self.open = 0
            self.low = 0
            self.high = 0
            self.close = 0
            self.volume = 0
            self.date = datetime.now()
    #Bot Logic
    class Bot:
        ib = None
        barsize = 1
        currentBar = Bar()
        bars = []
        reqId = 1
        global orderId
        smaPeriod = 50
        symbol = ""
        nextBarTime = 0
        
        def __init__(self):
            #Connect to IB on init
            self.ib = IBApi()
            self.ib.connect("127.0.0.1", 7497,221)
            ib_thread = threading.Thread(target=self.run_loop, daemon=True)
            ib_thread.start()
            time.sleep(1)
            currentBar = Bar()
            #Get symbol info
            self.symbol = input("Enter the symbol you want to trade : ")
            #Get bar size
            self.barsize = int(input("Enter the barsize you want to trade in minutes : "))
            mintext = " min"
            if (int(self.barsize) > 1):
                mintext = " mins"
                
            secs = 60 * self.barsize * 200 # barcount to retrieve
            queryTime = (datetime.now().astimezone(pytz.timezone("America/New_York"))-timedelta(seconds=secs)).strftime("%Y%m%d %H:%M:%S")
            
            #Create our IB Contract Object
            contract = Contract()
            contract.symbol = self.symbol.upper()
            contract.secType = "STK"
            contract.exchange = "SMART"
            contract.currency = "USD"
            self.ib.reqIds(-1)
            # Request History
            self.ib.reqHistoricalData(1,contract,"",str(secs)+" S",str(self.barsize)+mintext,"TRADES",0,1,False,[])
            
        #Listen to socket in seperate thread
        def run_loop(self):
            self.ib.run()
        #Bracet Order Setup
        def bracketOrder(self, parentOrderId, action, quantity, profitTarget, stopLoss):
            #Initial Entry
            #Create our IB Contract Object
            contract = Contract()
            contract.symbol = self.symbol.upper()
            contract.secType = "STK"
            contract.exchange = "SMART"
            contract.currency = "USD"
            # Create Parent Order / Initial Entry
            parent = Order()
            parent.orderId = parentOrderId
            parent.orderType = "MKT"
            parent.action = action
            parent.totalQuantity = quantity
            parent.transmit = False
            # Profit Target
            profitTargetOrder = Order()
            profitTargetOrder.orderId = parent.orderId+1
            profitTargetOrder.orderType = "LMT"
            profitTargetOrder.action = "SELL"
            profitTargetOrder.totalQuantity = quantity
            profitTargetOrder.lmtPrice = round(profitTarget,2)
            profitTargetOrder.parentId = parentOrderId
            profitTargetOrder.transmit = False
            # Stop Loss
            stopLossOrder = Order()
            stopLossOrder.orderId = parent.orderId+2
            stopLossOrder.orderType = "STP"
            stopLossOrder.action = "SELL"
            stopLossOrder.totalQuantity = quantity
            stopLossOrder.parentId = parentOrderId
            stopLossOrder.auxPrice = round(stopLoss,2)
            stopLossOrder.transmit = True
    
            bracketOrders = [parent, profitTargetOrder, stopLossOrder]
            return bracketOrders
        
    
        #Pass realtime bar data back to our bot object
        def on_bar_update(self, reqId, bar, realtime):
            global orderId,nextBarTime,currentBar
            
            try:
                #Historical Data to catch up
                if (realtime == False):
                    self.bars.append(bar)
                    self.nextBarTime = bar.date+timedelta(seconds=60 * self.barsize)
                else:
                    if (bar.date >= self.nextBarTime):
                        #On Bar Close
                        self.bars.append(self.currentBar)
                        print("Bar Closed ",self.currentBar.date.strftime('%H:%M'),"   o:",self.currentBar.open," h:",self.currentBar.high," l:",self.currentBar.low," c:",self.currentBar.close)
                        
                        # Calc SMA
                        closes = []
                        for bar in self.bars:
                            closes.append(bar.close)
                            
                        numbers_series = pd.Series(closes)
                        windows = numbers_series.rolling(self.smaPeriod)
                        moving_averages = windows.mean()
                        sma = moving_averages.tolist()
    
                        # Calculate Higher Highs and Lows
                        lastLow = self.bars[len(self.bars)-1].low
                        lastHigh = self.bars[len(self.bars)-1].high
                        lastClose = self.bars[len(self.bars)-1].close
                        
                        # Check Criteria
                        if (self.currentBar.close > lastHigh
                            and self.currentBar.low > lastLow
                            and self.currentBar.close > sma[len(sma)-1]
                            and lastClose < sma[len(sma)-2]):
                            
                            print("Placing Order")
                            
                            #Bracket Order 2% Profit Target 1% Stop Loss
                            profitTarget = bar.close*1.02
                            stopLoss = bar.close*0.99
                            quantity = 1
                            bracket = self.bracketOrder(orderId,"BUY",quantity, profitTarget, stopLoss)
                            contract = Contract()
                            contract.symbol = self.symbol.upper()
                            contract.secType = "STK"
                            contract.exchange = "SMART"
                            contract.currency = "USD"
                            #Place Bracket Order
                            for o in bracket:
                                o.ocaGroup = "OCA_"+str(orderId)
                                self.ib.placeOrder(o.orderId,contract,o)
                            orderId += 3
                            
                        else:
                            if(self.currentBar.close <= lastHigh):
                                print("-- Criteria not met: 'self.currentBar.close > lastHigh'    Close:",self.currentBar.close,"   lastHigh:",lastHigh)
                            if(self.currentBar.low <= lastLow):
                                print("-- Criteria not met: 'self.currentBar.low > lastLow'    Low:",self.currentBar.close,"   lastLow:",lastLow)
                            if(self.currentBar.close <= sma[len(sma)-1]):
                                print("-- Criteria not met: 'self.currentBar.close > sma[len(sma)-1]'     Close:",self.currentBar.close,"   sma:",sma[len(sma)-1])
                            if(lastClose >= sma[len(sma)-2]):
                                print("-- Criteria not met: 'lastClose < sma[len(sma)-2]'    lastClose:",lastClose,"   sma:",sma[len(sma)-2])
                        
                        
                        # Create new bar 
                        self.currentBar = Bar()    
                        self.currentBar.open = bar.open
                        self.currentBar.high = bar.high
                        self.currentBar.low = bar.low
                        self.currentBar.close = bar.close              
                        self.currentBar.date = self.nextBarTime #bar.date
                        
                        self.nextBarTime += timedelta(seconds=60 * self.barsize)  
               
                    else:
                        #Build realtime bar
                        if (self.currentBar.open == 0):
                            self.currentBar.open = bar.open
                        if (self.currentBar.high == 0 or bar.high > self.currentBar.high):
                            self.currentBar.high = bar.high
                        if (self.currentBar.low == 0 or bar.low < self.currentBar.low):
                            self.currentBar.low = bar.low
                        self.currentBar.close = bar.close
                        
            except Exception as e:
                print("on_bar_update Ex: ",e)
    
    #Start Bot
    bot = Bot()