Trouble getting ibapi to place trades

230 views Asked by At

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()
1

There are 1 answers

10
dno On BEST ANSWER

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()