I am a freelancer specializing in difficult, mathy problems.
We develop a stock trading platform and strategies.
Small team, lean principles.
All back-end code is Python 3.5.
We also like Docker, ReactJS, Redis, Kafka, PostgreSQL, TensorFlow, …
This talk
What is a trading platform?
Useful tricks & tools for async Python and performance.
A trading platform is a
low-latency event-driven multi-agent system
Low-latency
Newly arrived data should be processed in less than 10 milliseconds
Decisions should be made in less than 100 milliseconds
Event-driven
A lot of code is just waiting for things to happen:
Data from stock exchanges
Our computations
User input
Multi-agent
Many decoupled components.
Functionality is spread across different modules …
… or even processes
asyncio 101
Parallel programs are easy to reason about.
Coroutines use await to suspend themselves until they receive input.
Everything runs in one thread by default.
class InputDataProcessor:
async def run(self):
while True:
data = await self._reader.read() # Read from socket
publish(parse(data)) # Send to internal pubsub
class Model:
async def run(self):
while True:
incremental_update = await consume() # Read from pubsub
state_snapshot = self._process(incremental_update)
publish(state_snapshot) # Send state to internal pubsub
class UserInterfaceServer:
async def run(self):
await asyncio.wait([self._handle_input(), self._render()])
async def _handle_input(self):
while True:
data = await self._websocket.recv() # Read from WebSocket
publish(parse(data)) # Send input to internal pubsub
async def _render(self):
while True:
state = get_model_state()
await self._websocket.send(encode(state))
await asyncio.sleep(0.25)
import statsd
c = statsd.StatsClient('localhost', 8125)
c.incr('foo') # Increment the 'foo' counter.
c.timing('stats.timed', 320) # Record a 320ms 'stats.timed'.
Small library for monitoring and testing asyncio programs.
Always on in production.
Log warnings when callbacks block the event loop for longer than 50ms
aiodebug.log_slow_callbacks.enable(0.05)
Executing <Task pending coro=<foo() running at /home/.../foo.py:37> wait_for=<Future pending cb=[Task._wakeup()]>> took 0.069 seconds
Speed up or slow down time in the event loop
loop = aiodebug.testing.time_dilated_loop.TimeDilatedLoop()
asyncio.set_event_loop(loop)
loop.time_dilation = 3
await asyncio.sleep(1) # Takes 0.333s of real time
loop.time_dilation = 0.1
await asyncio.sleep(1) # Takes 10s of real time
The standard logging module is feature-rich but slow.
We replaced it with a bare-bones logging library with almost no features.
import logwood
from logwood.handlers.stderr import ColoredStderrHandler
logwood.basic_config(
level = logwood.INFO,
handlers = [ColoredStderrHandler()]
)
logger = logwood.get_logger('LoggerName')
logger.info('Just FYI')
logger.warning('Six times seven is {:d}.', 42)
[1477469095.001102][hostname][script.py][LoggerName][INFO] Just FYI [1477469095.001147][hostname][script.py][LoggerName][WARNING]
Six times seven is 42.