mrp
mrp

Reputation: 711

Making async posts requests using dictionary of items

I've built a trading bot that will buy/sell stocks as soon as the market opens and I'm trying to speed up my trade (post) requests.

I only consider about 350 stocks, but I've noticed that on days when I'm sending in a large number of orders (200-300) my requests can take quite a long time (~1.5min) when using the requests library. I wanted to try to use asyncio and aiohttp in order to send in the requests faster, however I'm getting and error I can't figure out. I'm not super familiar with async methods so I'm coming to SO for a 'lil help.

Below are the async functions for making the trade/post requests. I loosely based it on this SO answer (python async post requests) The error I'm getting is TypeError: 'coroutine' object is not iterable

I'm pretty sure this has to do with trying to iterate through the dictionary, but i'm not sure how to accomplish what I want. Perhaps I need to separate the buy and sell orders so I just loop through a 2 separate lists instead of a one dictionary?

Any suggestions would be helpful.

Notes:

  1. If it matters at all, the code is being executed via GCP Cloud Function.
  2. Passing two lists into the function is intentional, I need to calculate buy/sell statistics prior to making the trades and thought it was easiest/cleanest to combine them in the make_trades_async function.
# Example buy/sell lists:
buySymbolsList = ['MMM', 'CLX']
sellSymbolsList = ['A' 'MS']

async def make_trades_async(buySymbolsList, sellSymbolsList, token):
    
    buyDict = dict.fromkeys(buySymbolsList, "BUY")
    sellDict = dict.fromkeys(sellSymbolsList, "SELL")
    trades_dict = {**sellDict, **buyDict}
    
    url = 'https://api.tdameritrade.com/v1/accounts/{}/orders'.format(config.MARGIN_ACCOUNT)
    async with aiohttp.ClientSession() as session:
        post_tasks = []
        
        # prepare the coroutines that post
        async for ticker, trade_action in trades_dict.items():
            post_tasks.append(do_post(session, url, ticker, trade_action, token))
            
        # now execute them all at once
        await asyncio.gather(*post_tasks)
        


async def do_post(session, url, ticker, trade_action, token):
    async with session.post(url, 
                            json ={"orderType": "MARKET",
                                   "session": "NORMAL",
                                    "duration": "DAY",
                                    "orderStrategyType": "SINGLE",
                                    "orderLegCollection": [{
                                          "instruction": trade_action,
                                          "quantity": 1,
                                          "instrument": {
                                            "symbol": ticker,
                                            "assetType": "EQUITY"
                                          }
                                    }]
                                  },
                            headers= {'Authorization': 'Bearer '+ token}
                           ) as response:
        if response.status != 201:
            print("Failed to make trade for {}".format(ticker))

The make_trades_async function is run by executing:

asyncio.run(make_trades_async(buySymbolsList=buySymbolsList,
                              sellSymbolsList=sellSymbolsList, 
                              token=token))

Edit: Environment / Package information:

Python 3.7

asyncio==3.4.3
aiohttp==3.6.2
async-timeout==3.0.1
attrs==19.3.0
chardet==3.0.4
multidict==4.7.6
yarl==1.5.1

Upvotes: 1

Views: 892

Answers (1)

tclarke13
tclarke13

Reputation: 395

You don't need async in your for loop, you are merely adding tasks to a list synchronously. When you use async for, you are saying that some asynchronous code is running inside the generator that is producing your iterable. In your case, your iterable is just the list of items in the dictionary, so there's no async required.

Upvotes: 1

Related Questions