最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Placing a Bull Put Spread order with IBKR's TWS API using ibapi - Stack Overflow

programmeradmin2浏览0评论

I'm playing around with IBKR's ibapi library using the TWS API, and trying to make a simple script that will place a Bull Put Spread order.

from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.order import Order
import threading
import time

class IBapi(EWrapper, EClient):
    def __init__(self):
        EClient.__init__(self, self)
        self.nextOrderId = None
        self.position_data = []
        self.order_status = {}
        selfpleted_orders = {}
        self.contract_details = {}
        self.contract_details_end = {}
        
    def nextValidId(self, orderId: int):
        super().nextValidId(orderId)
        self.nextOrderId = orderId
        print(f"Next Valid Order ID: {orderId}")
    
    def orderStatus(self, orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice):
        super().orderStatus(orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice)
        print(f"Order Status - OrderId: {orderId}, Status: {status}, Filled: {filled}, Remaining: {remaining}")
        self.order_status[orderId] = status
    
    def openOrder(self, orderId, contract, order, orderState):
        super().openOrder(orderId, contract, order, orderState)
        print(f"Open Order - OrderId: {orderId}, Symbol: {contract.symbol}, Order Type: {order.orderType}, Action: {order.action}")
    
    def execDetails(self, reqId, contract, execution):
        super().execDetails(reqId, contract, execution)
        print(f"Execution Details - ReqId: {reqId}, Symbol: {contract.symbol}, ExecId: {execution.execId}, Price: {execution.price}")
        
    def contractDetails(self, reqId, contractDetails):
        super().contractDetails(reqId, contractDetails)
        self.contract_details[reqId] = contractDetails
        print(f"Contract Details - ReqId: {reqId}, ConId: {contractDetails.contract.conId}, Symbol: {contractDetails.contract.symbol}")
    
    def contractDetailsEnd(self, reqId):
        super().contractDetailsEnd(reqId)
        self.contract_details_end[reqId] = True
        print(f"Contract Details End - ReqId: {reqId}")

def create_option_contract(symbol, expiration, strike, right):
    contract = Contract()
    contract.symbol = symbol
    contract.secType = "OPT"
    contract.exchange = "SMART"
    contract.currency = "USD"
    contract.lastTradeDateOrContractMonth = expiration  # Format "YYYYMMDD"
    contract.strike = strike
    contract.right = right  # "P" for Put, "C" for Call
    contract.multiplier = "100"
    return contract

def create_order(action, quantity, price=None, order_type="LMT"):
    order = Order()
    order.action = action
    order.totalQuantity = quantity
    order.orderType = order_type
    if price is not None:
        order.lmtPrice = price
    
    # Disable problematic attributes
    order.eTradeOnly = False
    order.firmQuoteOnly = False
    
    # Set additional order properties to ensure proper execution
    order.transmit = True
    order.outsideRth = False  # Execute during regular trading hours only
    
    return order

def run_loop():
    app.run()

# Main code to execute a bull put spread
if __name__ == "__main__":
    from ibapi.contract import ComboLeg
    
    # Connect to IB TWS or IB Gateway
    app = IBapi()
    app.connect('127.0.0.1', 7497, 0)  # 7497 for TWS paper trading, 7496 for IB Gateway paper
    
    # Start the thread for processing IB messages
    api_thread = threading.Thread(target=run_loop, daemon=True)
    api_thread.start()
    
    # Wait for connection and nextValidId
    time.sleep(2)
    
    if app.nextOrderId is None:
        print("Failed to connect to IB API. Check if TWS/IB Gateway is running and API connections are enabled.")
        app.disconnect()
        exit(1)
    
    try:
        symbol = "SPY"
        expiration = "20250411"  # April 11, 2025
        
        higher_strike = 575
        lower_strike = 570
        quantity = 1
        credit_limit = 1.70
        
        # Create contracts for both legs
        sell_put_contract = create_option_contract(symbol, expiration, higher_strike, "P")
        buy_put_contract = create_option_contract(symbol, expiration, lower_strike, "P")
        
        print(f"Requesting contract details for {symbol} puts at strikes {higher_strike} and {lower_strike}...")
        
        # Request contract details to get the conIds
        app.reqContractDetails(1, sell_put_contract)
        app.reqContractDetails(2, buy_put_contract)
        
        # Wait for contract details to be received
        timeout = 10  # seconds
        start_time = time.time()
        while (1 not in app.contract_details_end or 2 not in app.contract_details_end) and (time.time() - start_time < timeout):
            time.sleep(0.1)
        
        if 1 not in app.contract_details or 2 not in app.contract_details:
            print("Failed to receive contract details. Check if the options exist for the specified expiration and strikes.")
            app.disconnect()
            exit(1)
        
        # Get the contract IDs
        sell_put_conid = app.contract_details[1].contract.conId
        buy_put_conid = app.contract_details[2].contract.conId
        
        print(f"Retrieved Contract IDs - Sell {higher_strike} Put: {sell_put_conid}, Buy {lower_strike} Put: {buy_put_conid}")
        
        # Create a combo contract for the bull put spread
        combo = Contract()
        combo.symbol = symbol
        combo.secType = "BAG"
        combo.exchange = "SMART"
        combo.currency = "USD"
        
        # Create the legs
        # For a bull put spread:
        # - SELL the higher strike put (575)
        # - BUY the lower strike put (570)
        leg1 = ComboLeg()
        leg1.conId = sell_put_conid  # ConId for the 575 strike put
        leg1.ratio = 1
        leg1.action = "SELL"
        leg1.exchange = "SMART"
        
        leg2 = ComboLeg()
        leg2.conId = buy_put_conid  # ConId for the 570 strike put
        leg2.ratio = 1
        leg2.action = "BUY"
        leg2.exchange = "SMART"
        
        comboboLegs = [leg1, leg2]
        
        # Create the combo order
        spread_order = create_order("SELL", quantity, credit_limit)
        
        print(f"Placing bull put spread on {symbol} as a combo order:")
        print(f"- SELL {quantity} {symbol} {expiration} {higher_strike} Put (ConId: {sell_put_conid})")
        print(f"- BUY {quantity} {symbol} {expiration} {lower_strike} Put (ConId: {buy_put_conid})")
        print(f"- Credit limit: ${credit_limit}")
        
        # Place the combo order
        app.placeOrder(app.nextOrderId, combo, spread_order)
        combo_order_id = app.nextOrderId
        app.nextOrderId += 1
        
        print(f"Combo order placed - Order ID: {combo_order_id}")
        
        # Monitor order status for a bit
        time.sleep(10)
        
        # Check order status
        print("\nOrder status summary:")
        for order_id, status in app.order_status.items():
            print(f"Order {order_id}: {status}")
        
    except Exception as e:
        print(f"Error: {e}")
    
    finally:
        # Disconnect
        print("\nDisconnecting from IB API...")
        app.disconnect()
        print("Disconnected.")

It seems like it's almost working, except I'm getting an error in TWS that says "Riskless combination orders are not allowed".

In addition, in the Orders tab in TWS, the order looks correct to me:

However, when I go into the Order Ticket, the buy/sell actions for the legs are reversed:

I believe this is the cause of the error, but it's not clear to me what I'm doing wrong in my script. Does anyone know what the issue might be?

I'm playing around with IBKR's ibapi library using the TWS API, and trying to make a simple script that will place a Bull Put Spread order.

from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.order import Order
import threading
import time

class IBapi(EWrapper, EClient):
    def __init__(self):
        EClient.__init__(self, self)
        self.nextOrderId = None
        self.position_data = []
        self.order_status = {}
        selfpleted_orders = {}
        self.contract_details = {}
        self.contract_details_end = {}
        
    def nextValidId(self, orderId: int):
        super().nextValidId(orderId)
        self.nextOrderId = orderId
        print(f"Next Valid Order ID: {orderId}")
    
    def orderStatus(self, orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice):
        super().orderStatus(orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld, mktCapPrice)
        print(f"Order Status - OrderId: {orderId}, Status: {status}, Filled: {filled}, Remaining: {remaining}")
        self.order_status[orderId] = status
    
    def openOrder(self, orderId, contract, order, orderState):
        super().openOrder(orderId, contract, order, orderState)
        print(f"Open Order - OrderId: {orderId}, Symbol: {contract.symbol}, Order Type: {order.orderType}, Action: {order.action}")
    
    def execDetails(self, reqId, contract, execution):
        super().execDetails(reqId, contract, execution)
        print(f"Execution Details - ReqId: {reqId}, Symbol: {contract.symbol}, ExecId: {execution.execId}, Price: {execution.price}")
        
    def contractDetails(self, reqId, contractDetails):
        super().contractDetails(reqId, contractDetails)
        self.contract_details[reqId] = contractDetails
        print(f"Contract Details - ReqId: {reqId}, ConId: {contractDetails.contract.conId}, Symbol: {contractDetails.contract.symbol}")
    
    def contractDetailsEnd(self, reqId):
        super().contractDetailsEnd(reqId)
        self.contract_details_end[reqId] = True
        print(f"Contract Details End - ReqId: {reqId}")

def create_option_contract(symbol, expiration, strike, right):
    contract = Contract()
    contract.symbol = symbol
    contract.secType = "OPT"
    contract.exchange = "SMART"
    contract.currency = "USD"
    contract.lastTradeDateOrContractMonth = expiration  # Format "YYYYMMDD"
    contract.strike = strike
    contract.right = right  # "P" for Put, "C" for Call
    contract.multiplier = "100"
    return contract

def create_order(action, quantity, price=None, order_type="LMT"):
    order = Order()
    order.action = action
    order.totalQuantity = quantity
    order.orderType = order_type
    if price is not None:
        order.lmtPrice = price
    
    # Disable problematic attributes
    order.eTradeOnly = False
    order.firmQuoteOnly = False
    
    # Set additional order properties to ensure proper execution
    order.transmit = True
    order.outsideRth = False  # Execute during regular trading hours only
    
    return order

def run_loop():
    app.run()

# Main code to execute a bull put spread
if __name__ == "__main__":
    from ibapi.contract import ComboLeg
    
    # Connect to IB TWS or IB Gateway
    app = IBapi()
    app.connect('127.0.0.1', 7497, 0)  # 7497 for TWS paper trading, 7496 for IB Gateway paper
    
    # Start the thread for processing IB messages
    api_thread = threading.Thread(target=run_loop, daemon=True)
    api_thread.start()
    
    # Wait for connection and nextValidId
    time.sleep(2)
    
    if app.nextOrderId is None:
        print("Failed to connect to IB API. Check if TWS/IB Gateway is running and API connections are enabled.")
        app.disconnect()
        exit(1)
    
    try:
        symbol = "SPY"
        expiration = "20250411"  # April 11, 2025
        
        higher_strike = 575
        lower_strike = 570
        quantity = 1
        credit_limit = 1.70
        
        # Create contracts for both legs
        sell_put_contract = create_option_contract(symbol, expiration, higher_strike, "P")
        buy_put_contract = create_option_contract(symbol, expiration, lower_strike, "P")
        
        print(f"Requesting contract details for {symbol} puts at strikes {higher_strike} and {lower_strike}...")
        
        # Request contract details to get the conIds
        app.reqContractDetails(1, sell_put_contract)
        app.reqContractDetails(2, buy_put_contract)
        
        # Wait for contract details to be received
        timeout = 10  # seconds
        start_time = time.time()
        while (1 not in app.contract_details_end or 2 not in app.contract_details_end) and (time.time() - start_time < timeout):
            time.sleep(0.1)
        
        if 1 not in app.contract_details or 2 not in app.contract_details:
            print("Failed to receive contract details. Check if the options exist for the specified expiration and strikes.")
            app.disconnect()
            exit(1)
        
        # Get the contract IDs
        sell_put_conid = app.contract_details[1].contract.conId
        buy_put_conid = app.contract_details[2].contract.conId
        
        print(f"Retrieved Contract IDs - Sell {higher_strike} Put: {sell_put_conid}, Buy {lower_strike} Put: {buy_put_conid}")
        
        # Create a combo contract for the bull put spread
        combo = Contract()
        combo.symbol = symbol
        combo.secType = "BAG"
        combo.exchange = "SMART"
        combo.currency = "USD"
        
        # Create the legs
        # For a bull put spread:
        # - SELL the higher strike put (575)
        # - BUY the lower strike put (570)
        leg1 = ComboLeg()
        leg1.conId = sell_put_conid  # ConId for the 575 strike put
        leg1.ratio = 1
        leg1.action = "SELL"
        leg1.exchange = "SMART"
        
        leg2 = ComboLeg()
        leg2.conId = buy_put_conid  # ConId for the 570 strike put
        leg2.ratio = 1
        leg2.action = "BUY"
        leg2.exchange = "SMART"
        
        comboboLegs = [leg1, leg2]
        
        # Create the combo order
        spread_order = create_order("SELL", quantity, credit_limit)
        
        print(f"Placing bull put spread on {symbol} as a combo order:")
        print(f"- SELL {quantity} {symbol} {expiration} {higher_strike} Put (ConId: {sell_put_conid})")
        print(f"- BUY {quantity} {symbol} {expiration} {lower_strike} Put (ConId: {buy_put_conid})")
        print(f"- Credit limit: ${credit_limit}")
        
        # Place the combo order
        app.placeOrder(app.nextOrderId, combo, spread_order)
        combo_order_id = app.nextOrderId
        app.nextOrderId += 1
        
        print(f"Combo order placed - Order ID: {combo_order_id}")
        
        # Monitor order status for a bit
        time.sleep(10)
        
        # Check order status
        print("\nOrder status summary:")
        for order_id, status in app.order_status.items():
            print(f"Order {order_id}: {status}")
        
    except Exception as e:
        print(f"Error: {e}")
    
    finally:
        # Disconnect
        print("\nDisconnecting from IB API...")
        app.disconnect()
        print("Disconnected.")

It seems like it's almost working, except I'm getting an error in TWS that says "Riskless combination orders are not allowed".

In addition, in the Orders tab in TWS, the order looks correct to me:

However, when I go into the Order Ticket, the buy/sell actions for the legs are reversed:

I believe this is the cause of the error, but it's not clear to me what I'm doing wrong in my script. Does anyone know what the issue might be?

Share Improve this question edited Mar 6 at 16:41 Rhys Causey asked Mar 6 at 16:31 Rhys CauseyRhys Causey 7877 silver badges22 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

I believe I've figured it out. When you open a position (whether net debit or credit), it's a buy, and when you close a position, it's a sell.

发布评论

评论列表(0)

  1. 暂无评论