← Back to Blog
STRATEGY

Crypto Market Making Bot in Python: Earning the Spread on LMEX

June 10, 2026 · 9 min read · LMEX.AI

Market making represents one of the most consistent profit strategies in crypto derivatives trading, yet it remains underutilized by retail algorithmic traders. By continuously placing buy and sell orders around the current market price, market makers capture the bid-ask spread while providing liquidity to the exchange. On LMEX, this strategy becomes particularly attractive due to competitive maker rebates and deep liquidity across major perpetual contracts like BTC-PERP and ETH-PERP.


This guide demonstrates how to build a production-ready market making bot in Python, complete with risk management, inventory controls, and dynamic spread adjustment based on market volatility.


Understanding Market Making Mechanics on LMEX


Market making profits from the spread between bid and ask prices. Your bot places limit orders on both sides of the order book, earning the difference when both orders fill. The key challenges involve managing inventory risk, adjusting spreads based on volatility, and handling rapid price movements that could leave you with stale orders.


LMEX's maker-taker fee structure incentivizes market making with rebates for providing liquidity. The exchange's low latency and robust API make it ideal for high-frequency market making strategies. Success depends on maintaining tight spreads while avoiding excessive inventory accumulation in any single direction.


Core Market Making Bot Architecture


The foundation of any market making bot requires several critical components: real-time price monitoring, order placement and cancellation logic, inventory tracking, and risk management controls. Here's the core structure:


import asyncio
import aiohttp
import json
import hmac
import hashlib
import time
from decimal import Decimal, ROUND_HALF_UP
from dataclasses import dataclass
from typing import Dict, List, Optional

@dataclass
class MarketMakerConfig:
    symbol: str
    spread_bps: int = 20  # 20 basis points = 0.2%
    max_position_size: float = 1000.0
    order_size: float = 10.0
    max_orders_per_side: int = 3
    price_precision: int = 2
    size_precision: int = 4

class LMEXMarketMaker:
    def __init__(self, api_key: str, api_secret: str, config: MarketMakerConfig):
        self.api_key = api_key
        self.api_secret = api_secret
        self.config = config
        self.base_url = "https://api.lmex.exchange"
        self.session = None
        
        # State tracking
        self.current_position = 0.0
        self.active_orders = {}
        self.last_price = None
        self.bid_orders = []
        self.ask_orders = []
        
    async def initialize(self):
        """Initialize the trading session and fetch current position"""
        self.session = aiohttp.ClientSession()
        await self.fetch_current_position()
        
    def generate_signature(self, timestamp: str, method: str, path: str, body: str = "") -> str:
        """Generate LMEX API signature"""
        message = timestamp + method + path + body
        return hmac.new(
            self.api_secret.encode(),
            message.encode(),
            hashlib.sha256
        ).hexdigest()
    
    async def make_request(self, method: str, endpoint: str, data: dict = None) -> dict:
        """Make authenticated request to LMEX API"""
        timestamp = str(int(time.time() * 1000))
        path = f"/v1/{endpoint}"
        body = json.dumps(data) if data else ""
        
        headers = {
            'LMEX-API-KEY': self.api_key,
            'LMEX-TIMESTAMP': timestamp,
            'LMEX-SIGNATURE': self.generate_signature(timestamp, method, path, body),
            'Content-Type': 'application/json'
        }
        
        async with self.session.request(method, self.base_url + path, 
                                      headers=headers, data=body) as response:
            return await response.json()
    
    async def fetch_current_position(self):
        """Get current position for the trading symbol"""
        response = await self.make_request('GET', f'positions/{self.config.symbol}')
        if response.get('success'):
            self.current_position = float(response['data']['size'])
    
    def calculate_order_prices(self, mid_price: float) -> tuple:
        """Calculate bid and ask prices based on spread configuration"""
        spread = mid_price * (self.config.spread_bps / 10000.0)
        
        bid_price = Decimal(str(mid_price - spread)).quantize(
            Decimal('0.' + '0' * (self.config.price_precision - 1) + '1'),
            rounding=ROUND_HALF_UP
        )
        
        ask_price = Decimal(str(mid_price + spread)).quantize(
            Decimal('0.' + '0' * (self.config.price_precision - 1) + '1'),
            rounding=ROUND_HALF_UP
        )
        
        return float(bid_price), float(ask_price)

This architecture establishes the essential framework for market making operations. The configuration system allows easy adjustment of spread parameters, position limits, and order sizing. The authentication system handles LMEX's signature requirements, while the position tracking maintains awareness of current inventory levels.


Dynamic Spread Adjustment and Order Management


Effective market making requires dynamic spread adjustment based on market conditions. During high volatility periods, wider spreads protect against adverse selection, while tight spreads maximize capture rates during stable conditions:


import numpy as np
from collections import deque

class VolatilityBasedMarketMaker(LMEXMarketMaker):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.price_history = deque(maxlen=100)
        self.volatility_window = 20
        self.base_spread_bps = self.config.spread_bps
        self.volatility_multiplier = 2.0
        
    async def update_market_data(self):
        """Fetch latest market data and update volatility metrics"""
        response = await self.make_request('GET', f'ticker/{self.config.symbol}')
        
        if response.get('success'):
            ticker_data = response['data']
            current_price = float(ticker_data['last_price'])
            self.last_price = current_price
            self.price_history.append(current_price)
            
            return {
                'price': current_price,
                'bid': float(ticker_data['best_bid']),
                'ask': float(ticker_data['best_ask']),
                'volume': float(ticker_data['volume_24h'])
            }
        return None
    
    def calculate_volatility(self) -> float:
        """Calculate rolling volatility from price history"""
        if len(self.price_history) < self.volatility_window:
            return 0.01  # Default 1% volatility
            
        prices = np.array(list(self.price_history)[-self.volatility_window:])
        returns = np.diff(np.log(prices))
        return float(np.std(returns) * np.sqrt(1440))  # Annualized to minutes
    
    def adjust_spread_for_volatility(self, base_spread_bps: int, volatility: float) -> int:
        """Dynamically adjust spread based on market volatility"""
        volatility_adjustment = max(1.0, volatility * self.volatility_multiplier)
        adjusted_spread = int(base_spread_bps * volatility_adjustment)
        return min(adjusted_spread, base_spread_bps * 5)  # Cap at 5x base spread
    
    def should_place_orders(self, market_data: dict) -> bool:
        """Determine if conditions are suitable for placing orders"""
        # Avoid trading during extreme inventory imbalances
        inventory_ratio = abs(self.current_position) / self.config.max_position_size
        if inventory_ratio > 0.8:
← All ArticlesBuild a Bot →