Concurrent Scalping Algo Using Async Python

Hitoshi Harada
Oct 7, 2019 · 7 min read
Image for post
Image for post
Photo by Alvaro Postigo on Unsplash

Automating My Manual Scalping Trading Strategy

One of the advantages of running automatic trading strategies is that you can quickly and consistently act on price action. Even if you have enough time to trade the same idea manually, you need to watch the market movement very closely and keep paying attention to multiple monitors. With algorithmic trading, you can automate this.

Image for post
Image for post

If You Want to Fast Forward to Full Code

Scalping Strategy

What is Scalping Strategy?

There are many types of trading strategies, but today I pick up one of them called “scalping” strategy.

Why Scalping Strategy for Algorithmic Trading?

When I look back at the intraday chart at the end of the day, I can see different missed opportunities, but I am usually working on something else in the office while the market is open and I’m unable to act on them. Also, I feel like there could be even more opportunities if I could monitor a dozen stocks independently versus just looking at one stock in a day.

Image for post
Image for post
TradingView chart for SPY with 20 minute simple moving average on 10/04/09 (This is an example and is for illustrative purposes only. Past performance is not indicative of future results.)

Automate This!

Python’s asyncio to Manage Algo Flow

What if I can have many of me watching each stock. I started coding based on the idea and found Python’s asyncio is pretty useful to manage algorithm flow for multiple stocks, each of which I found to be pretty simple.

Starting with a Single Stock

To begin with a single stock, I wrote a class that manages simple state and event handler for a single stock using Alpaca API (simplified below).

class ScalpAlgo:
def __init__(self, symbol):
self._symbol = symbol
self._bars = []
self._state = 'TO_BUY'
def on_bar(self, bar):
'''called when new bar arrives'''
self._bars = self._bars.append(bar)
if self._state == 'TO_BUY':
if self._check_signal():
self._submit_buy()
self._state = 'BUY_SUBMITTED'
def on_order_event(self, event):
'''called if order status changes'''
if event == 'fill':
if self._state == 'BUY_SUBMITTED':
self._submit_sell(self._get_last_price() + 0.01)
self._state = 'SELL_SUBMITTED'
elif self._state == 'SELL_SUBMITTED':
self._state = 'TO_BUY'
  def _calc_buy_signal(self):
mavg = self._bars.rolling(20).mean().close.values
closes = self._bars.close.values
if closes[-2] < mavg[-2] and closes[-1] > mavg[-1]:
return True
else:
return False

Subscribing Websockets

Since it is important to take action as quickly as the signal triggers, we subscribe to the real-time bar updates from Polygon websockets as well as Alpaca’s order event websockets. Everything is event-driven.

def main():
stream = alpaca.StreamConn()
algo = ScalpAlgo('SPY')
@stream.on('AM.SPY')
async def on_bar(conn, channel, data):
algo.on_bar(data)
@stream.on('trade_updates')
async def on_order_event(conn, channel, data):
algo.on_order_event(data.event)
stream.run(['AM.SPY', 'trade_updates'])

Focus on the State Transitions

Once the structure is built, all you need to do is to focus on the state transitions in a couple of different cases. Beyond the simplified sample code above, you may want to handle cancel/rejection event for your buy order. If you want to forget about it since you don’t have the position, but want to get in next time the same signal triggers, then you will set the state to TO_BUY so you can reset the state. You may also want to consider setting those orders to cancel orders if they don’t fill within the reasonable timeframe after submission. Or those working orders may be canceled from dashboard.

Concurrent algorithm

Scaling This to Multiple Stocks

To scale this idea to many stocks you want to watch, there is actually not much more to do. The ScalpAlgo already takes the stock symbol as parameter, and manages state for this symbol only, which means you just need to create more of this class instance with different symbols. As an example:

def main():
stream = alpaca.StreamConn()
fleet = {}
symbols = ('SPY', 'QQQ', 'DIA')
for symbol in symbols:
fleet[symbol] = ScalpAlgo(symbol)
@stream.on('AM.*')
async def on_bar(conn, channel, data):
if data.symbol in fleet:
fleet[data.symbl].on_bar(data)
@stream.on('trade_updates')
async def on_order_event(conn, channel, data):
order = data.order
if order['symbol']:
fleet[order['symbol']).on_order_event(data.event)
stream.run(['AM.' + symbol for symbols] + ['trade_updates'])

Putting Together

You Can Read the Full Code Here

$ python main.py SPY FB TLT
2019-10-04 18:49:04,250:main.py:119:INFO:SPY:received bar start = 2019-10-04 14:48:00-04:00, close = 293.71, len(bars) = 319
2019-10-04 18:49:04,254:main.py:106:INFO:SPY:closes[-2:] = [293.64 293.71], mavg[-2:] = [293.618215 293.62921 ]
2019-10-04 18:49:04,281:main.py:119:INFO:FB:received bar start = 2019-10-04 14:48:00-04:00, close = 180.69, len(bars) = 319
2019-10-04 18:49:04,286:main.py:106:INFO:FB:closes[-2:] = [180.609 180.69 ], mavg[-2:] = [180.488985 180.511485]
2019-10-04 18:49:04,437:main.py:119:INFO:AAPL:received bar start = 2019-10-04 14:48:00-04:00, close = 227.33, len(bars) = 319
2019-10-04 18:49:04,441:main.py:106:INFO:AAPL:closes[-2:] = [227.3088 227.33 ], mavg[-2:] = [227.114355 227.13985 ]
2019-10-04 18:49:04,455:main.py:119:INFO:TLT:received bar start = 2019-10-04 14:48:00-04:00, close = 145.65, len(bars) = 319
2019-10-04 18:49:04,458:main.py:101:INFO:TLT:buy signal: closes[-2] 145.6 < mavg[-2] 145.60542 closes[-1] 145.65 > mavg[-1] 145.60992000000002
2019-10-04 18:49:04,676:main.py:180:INFO:TLT:submitted buy Order({ 'asset_class': 'us_equity',
'asset_id': '1f8bfbdf-083a-4ebe-ad36-eaf6b6f0cb9d',
'canceled_at': None,
'client_order_id': 'f3c7a968-baba-454b-9691-a90810a389b4',
'created_at': '2019-10-04T18:49:04.658502572Z',
'expired_at': None,
'extended_hours': False,
'failed_at': None,
'filled_at': None,
'filled_avg_price': None,
'filled_qty': '0',
'id': '1aeeb723-34bc-4561-8016-8f916538509c',
'limit_price': '145.65',
'order_type': 'limit',
'qty': '13',
'replaced_at': None,
'replaced_by': None,
'replaces': None,
'side': 'buy',
'status': 'new',
'stop_price': None,
'submitted_at': '2019-10-04T18:49:04.635271258Z',
'symbol': 'TLT',
'time_in_force': 'day',
'type': 'limit',
'updated_at': '2019-10-04T18:49:04.662862495Z'})
Image for post
Image for post

Automation Generation

News and thought leadership on the changing landscape of…

Hitoshi Harada

Written by

CTO/CoFounder @AlpacaHQ (YC W19) the API-first brokerage for algorithmic trading and developers (https://alpaca.markets)

Automation Generation

News and thought leadership on the changing landscape of automated investing. Changing the market one algorithm at a time.

Hitoshi Harada

Written by

CTO/CoFounder @AlpacaHQ (YC W19) the API-first brokerage for algorithmic trading and developers (https://alpaca.markets)

Automation Generation

News and thought leadership on the changing landscape of automated investing. Changing the market one algorithm at a time.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store