Closed windowshopr closed 11 months ago
After some digging and tweaking, and randomly the help of this post #271 , I've been able to further reduce the code to highlight the issue I'm facing. Forget the above code, and see this updated code with comments describing the problem I'm facing re: ib.qualifyContracts(contract)
"hanging" during the request:
import nest_asyncio
from flask import Flask, request, jsonify
from flask_cors import CORS
from ib_insync import *
util.patchAsyncio()
# Global variables
IB_GATEWAY_SERVER_IP_ADDRESS = '127.0.0.1'
IB_GATEWAY_PORT = 4002
IB_GATEWAY_CLIENT_ID = 10
app = Flask(__name__)
CORS(app, supports_credentials=True)
# Initialize IB object
ib = IB()
# Route for establishing a connection to the IB Gateway API
@app.route('/connect-to-ib-gateway', methods=['POST'])
def connect_to_ib_gateway():
try:
ib.connect(IB_GATEWAY_SERVER_IP_ADDRESS, IB_GATEWAY_PORT, clientId=IB_GATEWAY_CLIENT_ID, readonly=False)
return jsonify({'message': 'Connected to IB Gateway'})
except Exception as e:
return jsonify({'message': 'Error connecting to IB Gateway', 'error': str(e)})
# Route for accepting POST requests to open a new position
@app.route('/post-order', methods=['POST'])
async def post_order():
# Create a contract object
contract = Forex("EURUSD", "IDEALPRO", "EUR", "USD")
print(contract) # To ensure endpoint is being hit
# This causes the request to hang/get stuck on "requesting"
# and am not able to CTRL + C to stop the server terminal.
await ib.qualifyContractsAsync(contract)
# If we take the "await" out, the request sends the success message
# below back, however it results in a "ib.qualifyContractsAsync(contract)
# was never awaited" error.
# By making the post_order() function NON-async, and using
# ib.qualifyContracts(contract) instead of the Async version,
# the same "hanging" issue occurs, although you ARE able to CTRL+C
# out of the server terminal to shut it down.
# Inspect the contract object again after qualifying
print(contract)
return jsonify({'message': 'Order placed successfully'})
if __name__ == '__main__':
app.run(port=5000)
I can suggest one of three things.
Have a look at these as well: https://github.com/erdewit/ib_insync/issues/585, https://github.com/erdewit/ib_insync/issues/266
From my own experiences (stemming from inexperience) trying to scale my use case for ib_insync
, I've decided that splitting things up into multiple scripts instantiated using containers gets me around a lot of the nuanced asyncio
stuff that caused me so many headaches. It makes the code base more complex, sure, but it works and it helps keeps things cleaner and more modular which works for my brain.
Ask ChatGPT. It may not provide a direct answer, but it certainly knows a million different ways to do a thing, and maybe one of those ways will give you an idea that could help.
Haha it’s funny because I was asking chatGPT for HOURS trying to get it to help me figure the issue out but even it says things like “it seems to be related to ib_insync’s asynchronous programming, try this” and of course you go down the rabbit holes of its “my apologies, there seemed to be an error in my previous code, try this” a million times but still to no avail.
I will review the posts you suggested to see if I can find a solution there for now. If anyone can get the (second) code sample I’ve provided working in the mean time I would be extremely grateful so I can see what the fix is as I’m new to handling ib_insync in a flask app. Thanks!
After some quick reading from a few different threads in there, it looks like flask it not the ideal framework to use as it has its own blocking/threading functionality and isn’t built for asyncio integration, BUT something like FastAPI is and might work! When I get some time this week, I’ll rewrite using FastAPI (never used yet, but always wanted to so this is good) and see if I can come up with a solution 👍
Ok thanks to @jlixfeld , I refactored the code to use FastAPI for the backend instead of Flask and VOILA! Thanks :)
import nest_asyncio
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
from ib_insync import *
util.patchAsyncio()
# Global variables
IB_GATEWAY_SERVER_IP_ADDRESS = '127.0.0.1'
IB_GATEWAY_PORT = 4002
IB_GATEWAY_CLIENT_ID = 10
app = FastAPI()
origins = ["*"] # Adjust this list to restrict origins if needed
app.add_middleware(CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
# Initialize IB object
ib = IB()
# Route for establishing a connection to the IB Gateway API
@app.post('/connect-to-ib-gateway')
async def connect_to_ib_gateway():
try:
ib.connect(IB_GATEWAY_SERVER_IP_ADDRESS, IB_GATEWAY_PORT, clientId=IB_GATEWAY_CLIENT_ID, readonly=False)
return {'message': 'Connected to IB Gateway'}
except Exception as e:
raise HTTPException(status_code=500, detail='Error connecting to IB Gateway')
# Route for accepting POST requests to open a new position
@app.post('/post-order')
async def post_order():
try:
# Create a contract object
contract = Forex("EURUSD", "IDEALPRO", "EUR", "USD")
print(contract) # To ensure endpoint is being hit
# Qualify the contract asynchronously
await ib.qualifyContractsAsync(contract)
# Inspect the contract object again after qualifying
print(contract)
return {'message': 'Order placed successfully'}
except Exception as e:
raise HTTPException(status_code=500, detail='Error placing order')
if __name__ == '__main__':
nest_asyncio.apply()
uvicorn.run(app, host='127.0.0.1', port=5000)
# uvicorn main:app --port 5000
I am setting up a simple flask backend server that is to be responsible for connecting to IB Gateway, and accepting order parameters to submit new orders.
I have created a minimal reproducible example below of the error I'm getting. Ensure that you have
Flask
installed and updated, save this code into aserver.py
file, and run it with the commandflask --app server run
.With your IB Gateway running and your global variables changed at the top, you can use Postman to create an empty POST request to the
http://127.0.0.1:5000/connect-to-ib-gateway
endpoint, and you'll see in your Gateway a new API client connects successfully.Now, when I submit a JSON POST request to the
http://127.0.0.1:5000/post-order
endpoint, I get the below error traceback. For ease, I'm testing with thisraw
JSON Body:(I also added this header just to cover all my bases)
I'm new to asynchronous programming/how to handle IB across multiple methods, but I think the issue lies somewhere in methods being run in different threads, or maybe it's something else entirely, I don't know. I'm just not sure how to interpret the traceback.
Here is the code: