Scaling a Forex WebSocket with Python Proxy
30 November 2023
In this tutorial, you'll develop a WebSocket proxy server using Python. The server will authenticate client requests with a specific user key, maintain a connection to a source WebSocket, and broadcast messages from the source to authenticated clients.
Prerequisites
Ensure you have Python 3.6 or newer installed, as WebSockets require this version or later. You’ll also need to install the WebSockets library, which you can do using pip:
pip install websockets
Step 1: Set Up Your Project
Create a new directory for your project and a Python file named websocket_proxy_server.py. This file will contain all your server code.
Step 2: Implement the WebSocket Server
Start by importing the necessary modules and setting up your basic server framework using the Web Sockets library:
import asyncio import websockets import json class WebSocketProxy: def init(self, source_url, symbols): self.source_url = source_url self.clients = set() self.symbols = symbols self.valid_user_key = "yourValidUserKey" # Single valid user key for authentication async def on_open(self, ws): print("Connected to source") symbols_str = ",".join(self.symbols.keys()) init_message = f"{{"userKey":"your_api_key", "symbol":"{symbols_str}"}}" await ws.send(init_message)
Step 3: Handle Client Connections and Authentication
Extend your server to handle client connections. Implement authentication by checking the user key provided by the client upon connection:
async def client_handler(self, websocket, path): try: # Wait for a message that should contain the authentication key auth_message = await asyncio.wait_for(websocket.recv(), timeout=10) auth_data = json.loads(auth_message) user_key = auth_data.get("userKey") if user_key == self.valid_user_key: self.clients.add(websocket) print(f"Client authenticated with key: {user_key}") try: await websocket.wait_closed() finally: self.clients.remove(websocket) else: print("Authentication failed") await websocket.close(reason="Authentication failed") except (asyncio.TimeoutError, json.JSONDecodeError, KeyError): print("Failed to authenticate") await websocket.close(reason="Failed to authenticate")
Step 4: Connect to the Source WebSocket and Broadcast Messages
Set up a method to maintain a connection to the source WebSocket and broadcast any received messages to all authenticated clients:
async def source_handler(self): async with websockets.connect(self.source_url) as websocket: await self.on_open(websocket) async for message in websocket: await self.broadcast(message) async def broadcast(self, message): if self.clients: await asyncio.gather(*(client.send(message) for client in self.clients))
Step 5: Running the Server
Complete your server setup by adding a function to start the server and a block to run it:
def run(self, host="localhost", port=8765): start_server = websockets.serve(self.client_handler, host, port) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_until_complete(self.source_handler()) asyncio.get_event_loop().run_forever() if name == "main": symbols = {"EURUSD": {}, "GBPUSD": {}, "USDJPY": {}, "AUDUSD": {}, "USDCAD": {}} source_url = "ws://example.com/source" proxy = WebSocketProxy(source_url, symbols) proxy.run()
Conclusion
You've created a functional WebSocket proxy server in Python. This server authenticates clients, maintains a connection to the data feed, and broadcasts messages to authenticated clients using the WebSocket protocol. This is particularly useful for applications that require secure, real-time data distribution from a central source to multiple clients.
Testing and Deployment
Test the server locally to ensure it handles connections and broadcasts as expected. Implement load balancing and customize your connection headers. You can also use reverse proxy servers. Once verified, you can deploy the server to a more permanent environment for handling WebSocket connections, such as a cloud service supporting long-lived network connections.