You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

IB-Insync对接TradingView Webhook仅能执行一次订单的技术排查求助

Troubleshooting IB-Insync Only Executing First Order from TradingView Webhook

Hey there, let's break down why your TWS API is only processing the first order and ignoring subsequent ones. I've dealt with a few IB API quirks like this before, so here are the most likely culprits and fixes:

1. Event Loop Conflict Between asyncio.run() and ib.run()

Your current code starts two separate event loops: one with asyncio.run(run_periodically(...)) and another with ib.run() at the end. IB-Insync has its own asyncio event loop management, and running both causes silent conflicts that break order placement after the first try.

Fix: Replace the standalone asyncio.run call with IB-Insync's native task scheduling. The IB object's run() method handles the event loop properly, so we'll use that to manage all async tasks.

2. Unreliable Redis PubSub Polling

Using p.get_message() in a 1-second periodic check is hit-or-miss—it might miss messages that arrive between checks, and over time, it doesn't handle Redis's internal message buffer correctly. This leads to orders being logged in Redis/Flask but never processed by TWS.

Fix: Switch to a continuous listener for Redis pubsub messages instead of polling. We'll use a dedicated async task that waits for messages directly, ensuring nothing slips through.

3. Explicit Order ID Management (Just to Be Safe)

While IB-Insync claims to auto-generate order IDs, connection blips can sometimes freeze the internal ID counter. Manually fetching and setting unique order IDs eliminates silent failures from duplicate or invalid IDs.

Fix: Fetch the next valid order ID directly from TWS before placing each order, then assign it explicitly to the order object.

4. Connection Health Checks

TWS API can drop connections silently if there's no activity or if the event loop isn't handling heartbeats. A periodic check to verify the connection is alive (and reconnect if it's not) ensures orders aren't sent to a dead connection.

Modified IB-Insync Code

Here's the revised code incorporating all these fixes:

import redis, json
from ib_insync import *
import asyncio, time

# Initialize IB and Redis
ib = IB()
r = redis.Redis(host='localhost', port=6379, db=0)
p = r.pubsub()
p.subscribe('tradingview')

async def check_ib_connection():
    """Periodically check if IB connection is alive; reconnect if not."""
    while True:
        if not ib.isConnected():
            print(f"{time.time()} - Reconnecting to TWS...")
            try:
                ib.disconnect()
                ib.connect('127.0.0.1', 7497, clientId=1)
                print(f"{time.time()} - Reconnected successfully!")
            except Exception as e:
                print(f"{time.time()} - Reconnection failed: {str(e)}")
        await asyncio.sleep(30)  # Check every 30 seconds

async def listen_for_webhook_messages():
    """Continuously listen for Redis pubsub messages and process orders."""
    print(f"{time.time()} - Listening for TradingView webhook messages...")
    for message in p.listen():
        if message['type'] == 'message':
            print(f"{time.time()} - Received message: {message}")
            try:
                message_data = json.loads(message['data'])
                # Fetch next valid order ID from TWS
                next_order_id = ib.client.getReqId()
                # Create contract and order
                stock = Stock(message_data['ticker'], 'SMART', 'USD')
                order = MarketOrder(
                    message_data['strategy']['order_action'],
                    message_data['strategy']['order_contracts']
                )
                order.orderId = next_order_id  # Explicitly set order ID
                # Place order
                trade = ib.placeOrder(stock, order)
                print(f"{time.time()} - Order placed successfully: {trade}")
            except Exception as e:
                print(f"{time.time()} - Error processing order: {str(e)}")

# Start tasks and run IB event loop
if __name__ == "__main__":
    # Create async tasks
    asyncio.create_task(check_ib_connection())
    asyncio.create_task(listen_for_webhook_messages())
    # Run IB's event loop (this blocks until disconnected)
    ib.run()

Key Changes Explained:

  • Removed conflicting event loops: We now use IB-Insync's event loop exclusively to avoid clashes.
  • Continuous Redis listening: p.listen() waits for messages directly, so no orders are missed.
  • Explicit order IDs: Fetching the next valid ID from TWS ensures every order has a unique identifier.
  • Connection health check: A background task verifies the IB connection every 30 seconds and reconnects if needed.

Additional TWS Settings to Verify:

  • Ensure "Enable Active X and Socket Clients" is checked (Edit > Global Configuration > API > Settings).
  • Uncheck "Read-Only API" (you need write access to place orders).
  • Confirm your clientId matches the allowed client IDs in TWS (if you've restricted access).

Test the Fix:

  1. Restart TWS and confirm the API is enabled.
  2. Run the modified IB-Insync script.
  3. Send multiple test orders via Insomnia or TradingView Webhook—you should see each order processed in the script output and reflected in TWS.

If issues persist, check the TWS API Message Log (Window > API > Message Log) for hidden error messages that might not appear in your script.

内容的提问来源于stack exchange,提问作者Trent

火山引擎 最新活动