Home / Books / An Overview of Market Making Strategies in Crypto: Architecture Design and FMZ Implementation of the Self-Matching Trading Strategy

An Overview of Market Making Strategies in Crypto: Architecture Design and FMZ Implementation of the Self-Matching Trading Strategy

⚠️ Important Disclaimer

This article demonstrates a volume-boosting self-matching trading strategy intended solely for learning purposes related to trading system architecture. It fundamentally differs from traditional arbitrage or market-making strategies. The core idea of this strategy is to simulate volume through same-price buy-sell matching to obtain exchange rebates or tier-based trading fee discounts, rather than profiting from market inefficiencies.

The provided code is only a reference framework and has not been tested in live trading environments. The strategy implementation is purely for technical study and research, and has not been thoroughly validated under real market conditions. Readers must perform extensive backtesting and risk evaluation before considering any real-world application. Do not use this strategy for live trading without proper due diligence.


In the cryptocurrency market, market-making strategies serve not only to improve liquidity and facilitate trading, but also form a core component of many quantitative trading systems. Market makers quote both buy and sell prices, provide liquidity, and capture profits under various market conditions. Professional market-making systems are often highly sophisticated, involving ultra-low-latency optimization, advanced risk management modules, and cross-exchange arbitrage mechanisms. In this article, we explore the basic concept behind a volume-boosting self-matching trading strategy and demonstrate how to implement a simplified educational framework on the FMZ Quant Trading Platform.

The main content is adapted from the original work “Market Making Strategy: Concepts and Implementation” by Zinan, with some optimizations and adjustments. While some coding practices may now appear outdated, this version recreated on the FMZ platform still offers valuable insights into the structure of trading algorithms and the fundamentals of self-matching trading logic.

Concept of Market Making Strategies

A Market Making Strategy refers to a trading approach where a trader (market maker) places both buy and sell orders in the market simultaneously, thereby providing liquidity and contributing to market stability. This strategy not only helps maintain market depth but also offers counterparties for other traders. By quoting buy and sell prices across various price levels, market makers aim to profit from market fluctuations.

In the context of cryptocurrency markets, market makers play a critical role—especially in markets with low trading volume or high volatility. By offering liquidity, market makers help reduce slippage and make it easier for other traders to execute orders at favorable prices.

The core principle of traditional market-making strategies lies in capturing the bid-ask spread by providing liquidity. Market makers post buy orders at lower prices and sell orders at higher prices, profiting from the difference when orders are filled. For example, when the spot price in the market rises, market makers sell at a higher price and buy at a lower price, earning the difference. The primary sources of income include:

  • Bid-ask spread: Traditional market makers earn profits by placing limit buy and sell orders and capturing the price difference between the bid and ask.
  • Volume-based incentives: The profitability of market makers is closely tied to the trading volume they provide. Higher volume not only leads to more frequent order fills and profit opportunities but also unlocks additional benefits:
    1). Fee rebates: Many exchanges offer fee rebates to incentivize liquidity provision. In some cases, makers receive negative fees—meaning the exchange pays the market maker for executed trades.
    2). VIP tier discounts: Reaching specific volume thresholds may qualify market makers for lower trading fees, reducing operational costs.
    3). Market maker incentive programs: Some exchanges run dedicated incentive programs that reward market makers based on the quality and consistency of their liquidity provision.

However, market makers also face significant market risk, especially in highly volatile environments such as the cryptocurrency space. Rapid price swings may cause market makers’ quoted prices to deviate significantly from actual market conditions, potentially resulting in losses.

Types of Market Making Strategies

In the cryptocurrency market, market makers typically choose different strategies based on market conditions, trading volume, and volatility. Common types of market-making strategies include:

  • Passive market making: In this approach, the market maker places buy and sell limit orders based on market depth, historical volatility, and other indicators, then waits for the market to fill those orders. This strategy is characterized by low frequency and a conservative risk profile, relying on natural market movements to generate profit.
  • Active market making: Active market makers dynamically adjust order prices and sizes in real time based on market conditions to improve execution probability. They often place orders close to the current mid-price, aiming to better capture opportunities from short-term volatility.
  • Volume-boosting self-matching strategy: The focus of this article. A volume-boosting self-matching strategy involves placing simultaneous buy and sell orders at the same price to artificially increase trading volume. Unlike traditional market-making, this strategy is not designed to profit from bid-ask spreads. Instead, it seeks to benefit from exchange incentives such as fee rebates, VIP tier discounts, or liquidity mining rewards.

In a volume-boosting self-matching strategy, the market maker posts buy and sell orders at the same price level. While these trades do not generate profits from price differentials, they quickly accumulate trading volume. Profitability is entirely dependent on the exchange’s incentive mechanisms rather than market inefficiencies or arbitrage.

Key characteristics:

  • Same-price orders: Unlike traditional market-making, buy and sell orders are placed at the same price, eliminating spread-based profits.
  • Volume-oriented execution: The primary objective is to accumulate trade volume rapidly, not to exploit price arbitrage.
  • Incentive-driven profitability: Returns are fully reliant on exchange incentives such as fee rebates, VIP tier benefits, or dedicated market maker reward programs.

Important distinction:
Compared to traditional market-making strategies, volume-boosting self-matching strategies do not generate profit by providing genuine market liquidity. Instead, they create trading volume artificially to capitalize on policy-driven rewards offered by exchanges. This type of strategy may carry regulatory or compliance risks in certain jurisdictions and must be carefully evaluated before any live trading deployment.

Profit Logic Behind Volume-Boosting Self-Matching Strategies

Upon analyzing the code implementation, it becomes evident that the buy and sell prices are exactly the same in this strategy:

defmake_duiqiao_dict(self, trade_amount):
    mid_price = self.mid_price  # Med price
    trade_price = round(mid_price, self.price_precision)  # Accurate transaction price
    trade_dict = {
        'trade_price': trade_price,  # The same price is used for both buying and selling'amount': trade_amount
    }
    return trade_dict

Actual Profit Mechanism

1. Volume-boosting via self-matching

  • The core objective of this strategy is to increase trading volume rapidly through high-frequency self-matching.
  • Profit is derived from exchange incentives such as fee rebates, VIP tier upgrades, or liquidity mining rewards.
  • This approach is applicable on exchanges that offer formal market maker incentive programs.

2. Fee rebate mechanism

  • The strategy relies on exchanges offering negative maker fees (maker rates are negative)
  • By providing liquidity, the strategy earns rebates on filled orders.
  • It requires the exchange to support market maker fee discounts or rebate structures.

Suitable Scenarios & Associated Risks

✅ Suitable scenarios

  • Exchanges with clear market maker rebate or incentive policies.
  • Traders aiming to meet high trading volume requirements for VIP tier upgrades.
  • Platforms that run liquidity mining or commission rebate campaigns.

❌ Unsuitable scenarios

  • Exchanges that do not offer fee rebates or incentives.
  • Platforms with high transaction fees, where self-matching leads to net losses.
  • Exchanges that explicitly prohibit wash trading or enforce restrictions on artificial volume.

⚠️ Risk warnings

  • If both buy and sell orders are filled simultaneously, the strategy may incur a net loss after fees.
  • Changes in exchange policies may render the strategy unprofitable or non-viable.
  • Continuous monitoring of fee structures and trading costs is required.
  • The strategy may face compliance risks in jurisdictions where volume-boosting trading is regulated or restricted.

Self-Matching Strategy Architecture Analysis

This section presents a simplified implementation of a volume-boosting self-matching strategy, inspired by the framework developed by Zinan. The focus is on how to accumulate trading volume through same-price buy and sell orders within a live exchange environment. The strategy architecture is structured around two main classes: MidClass and MarketMaker. These components are responsible for handling exchange interactions and executing the self-matching logic, respectively.

The architecture follows a layered design, separating the exchange interface logic from the trading strategy logic. This ensures modularity, extensibility, and clean separation of concerns. The main components are:

  1. MidClass: The exchange middle layer is responsible for interacting with the exchange interface to obtain market data, account information, order status, etc. This layer encapsulates all interactions with external exchanges to ensure that the trading logic and the exchange interface are decoupled.
  2. MarketMaker: The market making strategy class is responsible for executing the cross-trading strategy, generating pending orders, checking order status, updating strategy status, etc. It interacts with the exchange middle layer to provide specific market making and self-matching trading operations.

MidClass

MidClass is the middle layer of the exchange. Its main responsibility is to handle the interaction with the exchange, encapsulate all external API calls, and provide a simple interface for MarketMaker to use. Its architecture includes the following key functions:

1. Market data retrieval:
Fetches real-time market data such as tickers, order book depth, and candlestick data (K-lines). Regular updates are essential to ensure the strategy operates on up-to-date information.

2. Account information management:
Retrieves account data, including balances, available margin, and open positions. This is critical for capital allocation and risk control.

3. Order management:
Provides functionality to place, query, and cancel orders. This is the foundation of executing a market-making or self-matching strategy and ensures robust control over open orders.

4. Data Synchronization:
Maintains persistent connections with the exchange and updates internal state for use by the strategy layer.

By encapsulating these features in MidClass, the strategy logic within MarketMaker remains focused on execution rather than infrastructure. This structure improves the maintainability and scalability of the system, making it easier to add support for different exchanges or optimize existing functions.

MarketMaker

MarketMaker is the core class of the self-matching strategy, responsible for executing market-making operations and handling self-matching trades. Its architecture typically includes the following key modules:

1. Initialization:

  • Initialize the exchange middleware (MidClass) to retrieve exchange metadata such as trading pair details, precision, tick size, and order book depth.
  • Initialize the market-making strategy, set up key parameters like order quantity, price spread, and execution intervals. These parameters directly affect how the strategy behaves and performs in the market.

2. Data refresh:

  • Periodic market data updates, including real-time account information, market price, depth, K-line, etc. These data provide basic information for executing strategies.
  • The frequency of updates can be dynamically adjusted based on market volatility to ensure timely responses to market changes.

3. Self-matching execution logic:

  • Order book construction: Based on current market depth and price dynamics, construct a dictionary of orders (both bids and asks) with specified price and size. This is typically calculated using predefined strategy parameters.
  • Self-matching execution: Once the order structure is ready, MarketMaker submits both buy and sell orders at the same price level to the market. The goal is to accumulate trade volume quickly via same-price order matching.
  • Order status monitoring: During execution, MarketMaker will check the status of the order constantly to ensure that the pending order can be processed in time. If the order fails to be executed, it will adjust the pending order price or quantity until the order is completed.

4. Strategy state update:

  • Strategy status update: Regularly update key performance indicators such as cumulative trading volume, filled order count, and total fees. These metrics allow real-time tracking of the strategy’s performance.
  • Dynamic risk management: The strategy adapts its behavior based on current market conditions. MarketMaker can modify execution logic in real time to reduce risk and maintain operational efficiency across varying market environments.

Self-Matching Strategy Logic Reconstruction

The implementation of a self-matching strategy relies on precise market data and fast execution. The MarketMaker class monitors real-time market conditions and leverages same-price buy and sell orders (self-matching) to rapidly accumulate trading volume, which is the core objective of this strategy.

Initialization

In the MarketMaker class’s initialization method, the first step is to retrieve the exchange’s precision settings, followed by initializing key strategy parameters such as quantity precision and price precision.

self.precision_info = self.exchange_mid.get_precision()  # Get precision informationself.price_precision = self.precision_info['price_precision']  # Price precisionself.amount_precision = self.precision_info['amount_precision']  # Trading volume precision

Generating the Self-Matching Order Dictionary

At the heart of the self-matching strategy is the construction of an order dictionary containing both buy and sell orders at the same price level, along with their respective quantities. The code generates the dictionary of self-matching trading orders by calculating the middle price.

defmake_duiqiao_dict(self, trade_amount):
    mid_price = self.mid_price  # Mid Price
    trade_price = round(mid_price, self.price_precision)  # Accurate transaction price
    trade_dict = {
        'trade_price': trade_price,
        'amount': trade_amount
    }
    return trade_dict

Executing Self-Matching Trades

According to the generated dictionary of self-matching trading orders, the self-matching trading transaction is executed. In the code, the create_order method of the exchange middle layer is called to place buy orders and sell orders at the same time.

defmake_trade_by_dict(self, trade_dict):
    ifself.position_amount > trade_dict['amount'] andself.can_buy_amount > trade_dict['amount']:
        buy_id = self.exchange_mid.create_order('buy', trade_dict['trade_price'], trade_dict['amount'])  # Pending buy order
        sell_id = self.exchange_mid.create_order('sell', trade_dict['trade_price'], trade_dict['amount'])  # Pending sell orderself.traded_pairs['dui_qiao'].append({
            'buy_id': buy_id, 'sell_id': sell_id, 'init_time': time.time(), 'amount': trade_dict['amount']
        })

Order Status Monitoring

The strategy periodically checks the status of active orders and handles any unfilled or partially filled ones. In the code, this is done by calling the GetOrder method from the exchange middleware (MidClass). Based on the returned order status, the strategy decides whether to cancel the orders. The self-matching order management logic includes the following key steps:

1. Fetching order status:

  • The strategy retrieves the current status of both the buy and sell orders through the exchange API.
  • If the status retrieval fails (e.g., due to a missing order or network issue), the strategy cancels the corresponding order and removes it from the active tracking list.

2. Evaluating order status:

  • The status returned is used to determine whether the order is filled, partially filled, or still open.
  • Typical order status include:
    0(ORDER_STATE_PENDING): Order is open and waiting to be filled.
    1(ORDER_STATE_CLOSED): Order has been completely filled.
    2(ORDER_STATE_CANCELED): Order has been canceled.
    3(ORDER_STATE_UNKNOWN): Order status is unknown or undefined.

3. Handling order status:

  • Both orders unfilled:
    If both buy and sell orders remain unfilled (status 0), the strategy checks the polling interval (e.g., current_time % 5 == 0) to decide whether to cancel them.
    After cancellation, the strategy updates the order count and removes the orders from the internal record.
  • One order filled, the other unfilled:
    If one side of the self-matching order pair is filled (status == 1) and the other remains unfilled (status == 0), the strategy uses the polling interval condition to decide whether to cancel the unfilled order.
    After cancelling an open order, update the volume and the list of open orders and remove the order from the record.
  • Both orders filled:
    If both the buy and sell orders are fully executed (status == 1), the strategy updates the trade volume counter, and the order pair is removed from the internal tracking list.
  • Unknown order status:
    If the order status is neither 0 nor 1, it is recorded as unknown status and logged.

4. Updating internal records:
After processing the order statuses, the strategy updates the total accumulated trade volume, the list of unfilled or partially filled orders, the order submission and cancellation counters.

Future Strategy Outlook

The self-matching strategy presented in this article is primarily intended as an educational example for understanding the architectural design of trading frameworks. Its practical application in live trading is limited. For readers interested in real market-making strategies, we plan to introduce more advanced and practical models in future content:

1. Order book market-making strategy

  • A true arbitrage-based approach that captures the bid-ask spread.
  • Places limit orders between the best bid and ask to earn the spread profit.
  • Closely aligns with the traditional profit model of professional market makers.

2. Dynamic market-making strategy

  • Adapts quote prices in real-time based on market volatility.
  • Integrates inventory management and risk control mechanisms.
  • Suitable for adaptive execution across varying market conditions.

3. Multi-level market-making strategy

  • Places orders at multiple price levels simultaneously.
  • Diversifies execution risk and enhances overall return stability.
  • Closer to how professional market-making systems operate in production.

These upcoming strategies will emphasize realistic profit logic and robust risk management, providing quantitative traders with more actionable and valuable insights for developing production-ready systems.

Strategy Outlook

Self-matching strategies that rely on exchange incentive policies, such as fee rebates, VIP tier upgrades, or liquidity mining rewards — are inherently vulnerable to changes in those policies. If an exchange adjusts its fee structure or removes such incentives, the strategy may become ineffective or even result in net losses. To mitigate this, the strategy must incorporate adaptability to policy changes, such as dynamic monitoring of fee rates and trading incentives, multiple profit sources to reduce over-reliance on a single incentive. fallback mechanisms or automatic shutdown triggers if profitability thresholds are not met. Moreover, self-matching strategies may raise regulatory red flags, as they can be interpreted as attempts to manipulate market volume. In many jurisdictions, such behavior may violate market integrity laws.

Therefore, traders must stay updated on local legal and compliance requirements, consult with legal professionals when deploying volume-based strategies, avoid practices that could be construed as deceptive or manipulative.

We hope readers use this strategy framework as a foundation to build more robust, compliant, and innovative trading systems. The true value of quantitative trading lies in continuous learning, experimentation, and refinement. May your journey in quant trading be insightful, adaptive, and rewarding!

Strategy Code

import time, json

classMidClass:
    def__init__(self, exchange_instance):
        '''
        Initialize the exchange middle layer
        
        Args:
            exchange_instance: FMZ's exchange structure
        '''self.init_timestamp = time.time()  # Record initialization timeself.exchange = exchange_instance  # Save the exchange objectself.exchange_name = self.exchange.GetName()  # Get the exchange nameself.trading_pair = self.exchange.GetCurrency()  # Get the trading pair name (such as BTC_USDT)defget_precision(self):
        '''
        Get the accuracy information of the trading pair
        
        Returns:
            Returns a dictionary containing precision information, or None on failure.
        '''
        symbol_code = self.exchange.GetCurrency()
        ticker = self.exchange.GetTicker(symbol_code)  # Backtesting system needs
        exchange_info = self.exchange.GetMarkets()
        data = exchange_info.get(symbol_code)

        ifnot data:
            Log("Failed to obtain market information", GetLastError())
            returnNone# Get the accuracy information of the trading pairself.precision_info = {
            'tick_size': data['TickSize'],                  # Price accuracy'amount_size': data['AmountSize'],              # Quantity accuracy'price_precision': data['PricePrecision'],      # Price decimal places precision'amount_precision': data['AmountPrecision'],    # Number of decimal places of precision'min_qty': data['MinQty'],                      # Minimum order quantity'max_qty': data['MaxQty']                       # Maximum order quantity
        }

        returnself.precision_info

    defget_account(self):
        '''
        Get account information
        
        Returns:
            Returns True if the information is successfully obtained, and returns False if the information is failed.
        '''self.balance = '---'# Account balanceself.amount = '---'# Account holdingsself.frozen_balance = '---'# Freeze balanceself.frozen_stocks = '---'# Freeze positionsself.init_balance = Noneself.init_stocks = Noneself.init_equity = Nonetry:
            account_info = self.exchange.GetAccount()  # Get account informationself.balance = account_info['Balance']  # Update account balanceself.amount = account_info['Stocks']  # Update the holdingsself.frozen_balance = account_info['FrozenBalance']  # Update frozen balanceself.frozen_stocks = account_info['FrozenStocks']  # Update frozen positionsself.equity = self.balance + self.frozen_balance + (self.amount + self.frozen_stocks) * self.last_price
            
            ifnotself.init_balance ornotself.init_stocks ornotself.init_equity:
                if _G("init_balance") and _G("init_balance") > 0and _G("init_stocks") and _G("init_stocks") > 0:
                    self.init_balance = round(_G("init_balance"), 2)
                    self.init_stocks = round(_G("init_stocks"), 2)
                    self.init_equity = round(_G("init_equity"), 2)
                else:
                    self.init_balance = round(self.balance + self.frozen_balance, 2)
                    self.init_stocks = self.amount + self.frozen_stocks
                    self.init_equity = round(self.init_balance + (self.init_stocks * self.last_price), 2)
                    _G("init_balance", self.init_balance)
                    _G("init_stocks", self.init_stocks)
                    _G("init_equity", self.init_equity)

                    Log('Obtaining initial equity', self.init_equity)

            self.profit = self.equity - self.init_equity
            self.profitratio = round((self.equity - self.init_equity)/self.init_equity, 4) * 100returnTrueexcept:
            returnFalse# Failed to obtain account informationdefget_ticker(self):
        '''
        Get market price information (such as bid price, ask price, highest price, lowest price, etc.)
        
        Returns:
            Returns True if the information is successfully obtained, and returns False if the information is failed.
        '''self.high_price = '---'# The highest priceself.low_price = '---'# The lowest priceself.sell_price = '---'# Ask priceself.buy_price = '---'# Bid priceself.last_price = '---'# Latest transaction priceself.volume = '---'# Trading volumetry:
            ticker_info = self.exchange.GetTicker()  # Get market price informationself.high_price = ticker_info['High']  # Update highest priceself.low_price = ticker_info['Low']  # Update lowest priceself.sell_price = ticker_info['Sell']  # Update ask priceself.buy_price = ticker_info['Buy']  # Update bid priceself.last_price = ticker_info['Last']  # Update the latest transaction priceself.volume = ticker_info['Volume']  # Update trading volumereturnTrueexcept:
            returnFalse# Failed to obtain market price informationdefget_depth(self):
        '''
        Get depth information (list of pending orders for buy and sell orders)
        
        Returns:
            Returns True if the information is successfully obtained, and returns False if the information is failed.
        '''self.ask_orders = '---'# Ask order listself.bid_orders = '---'# Bid order listtry:
            depth_info = self.exchange.GetDepth()  # Get depth informationself.ask_orders = depth_info['Asks']  # Update the sell order listself.bid_orders = depth_info['Bids']  # Update buy order listreturnTrueexcept:
            returnFalse# Failed to obtain depth informationdefget_ohlc_data(self, period=PERIOD_M5):
        '''
        Get K-line information
        
        Args:
            period: K-line period, PERIOD_M1 refers to 1 minute, PERIOD_M5 refers to 5 minutes, PERIOD_M15 refers to 15 minutes,
            PERIOD_M30 means 30 minutes, PERIOD_H1 means 1 hour, PERIOD_D1 means one day.
        '''self.ohlc_data = self.exchange.GetRecords(period)  # Get K-line datadefcreate_order(self, order_type, price, amount):
        '''
        Submit an order
        
        Args:
            order_type: Order type, 'buy' refers to a buy order, 'sell' refers to a sell order
            price: Order price
            amount: Order amount 
            
        Returns:
            Order ID number, which can be used to cancel the order
        '''if order_type == 'buy':
            try:
                order_id = self.exchange.Buy(price, amount)  # Submit a buy orderexcept:
                returnFalse# Buy order submission failedelif order_type == 'sell':
            try:
                order_id = self.exchange.Sell(price, amount)  # Submit a sell orderexcept:
                returnFalse# Sell order submission failedreturn order_id  # Returns the order IDdefget_orders(self):
        '''
        Get a list of uncompleted orders
        
        Returns:
            List of uncompleted orders
        '''self.open_orders = self.exchange.GetOrders()  # Get uncompleted ordersreturnself.open_orders
    
    defcancel_order(self, order_id):
        '''
        Cancel a pending order
        
        Args:
            order_id: The ID number of the pending order you wish to cancel
            
        Returns:
            Returns True if the pending order is successfully cancelled, and returns False if the pending order is failed.
        '''returnself.exchange.CancelOrder(order_id)  # Cancel the orderdefrefresh_data(self):
        '''
        Refresh information (account, market price, depth, K-line)
        
        Returns:
            If the refresh information is successfully returned, 'refresh_data_finish!' will be returned. Otherwise, the corresponding refresh failure information prompt will be returned.
        '''ifnotself.get_ticker():  # Refresh market price informationreturn'false_get_ticker'ifnotself.get_account():  # Refresh account informationreturn'false_get_account'ifnotself.get_depth():  # Refresh depth informationreturn'false_get_depth'try:
            self.get_ohlc_data()  # Refresh K-line informationexcept:
            return'false_get_K_line_info'return'refresh_data_finish!'# Refresh successfullyclassMarketMaker:
    def__init__(self, mid_class):
        '''
        Initialize market making strategy
        
        Args:
            mid_class: Exchange middle layer object
        '''self.exchange_mid = mid_class  # Exchange middle layer objectself.precision_info = self.exchange_mid.get_precision()  # Get accuracy informationself.done_amount = {'dui_qiao': 0}  # Completed transactionsself.price_precision = self.precision_info['price_precision']  # Price precisionself.amount_precision = self.precision_info['amount_precision']  # Trading volume precisionself.traded_pairs = {'dui_qiao': []}  # Trading pairs with pending ordersself.pending_orders = []  # Uncompleted order statusself.pending_order_count = 0# Number of pending ordersself.buy_amount = 0self.sell_amount = 0self.fee = 0self.fee_rate = 0.08 / 100self.chart = {
            "__isStock": True,
            "tooltip": {"xDateFormat": "%Y-%m-%d %H:%M:%S, %A"},
            "title": {"text": "Number of pending orders"},
            "xAxis": {"type": "datetime"},
            "yAxis": {
                "title": {"text": "Number of pending orders"},
                "opposite": False
            },
            "series": [
                {"name": "Buy order quantity", "id": "Buy order quantity", "data": []},
                {"name": "Sell order quantity", "id": "Sell order quantity", "dashStyle": "shortdash", "data": []}
            ]
        }
    
    defrefresh_data(self):
        '''
        Refresh data (account, market price, depth, K-line)
        '''self.exchange_mid.refresh_data()  # Refresh exchange dataself.position_amount = 0ifisinstance(self.exchange_mid.amount, str) elseself.exchange_mid.amount  # Holding positionsself.available_balance = 0ifisinstance(self.exchange_mid.balance, str) elseself.exchange_mid.balance  # Account balance
        Log('Check ticker', self.exchange_mid.buy_price)
        self.can_buy_amount = self.available_balance / float(self.exchange_mid.buy_price)  # Quantity available for purchaseself.mid_price = (self.exchange_mid.sell_price + self.exchange_mid.buy_price) / 2# Mid Pricedefmake_duiqiao_dict(self, trade_amount):
        
        '''
        Generate a dictionary of self-matching orders
        
        Args:
            trade_amount: Volume per transaction
        
        Returns:
            Dictionary list of self-matching orders
        '''
        Log('3 Create a dictionary for self-matching orders')

        mid_price = self.mid_price  # Mid price

        trade_price = round(mid_price, self.price_precision)  # Accurate transaction price

        trade_dict = {
            'trade_price': trade_price,
            'amount': trade_amount
        }

        Log('Returns the market order dictionary:', trade_dict)
        return trade_dict
    
    defmake_trade_by_dict(self, trade_dict):
        '''
        Execute transactions according to the transaction dictionary
        
        Args:
            trade_dict: transaction dictionary
        '''
        Log('4 Start trading by dictionary')
        self.refresh_data()  # Refresh dataif trade_dict:
            Log('Current account funds: Coin balance: ', self.position_amount, 'Funds balance: ', self.can_buy_amount)
            Log('Check open positions: Coin limit: ', self.position_amount > trade_dict['amount'], 'Funding restrictions: ', self.can_buy_amount > trade_dict['amount'])
            ifself.position_amount > trade_dict['amount'] andself.can_buy_amount > trade_dict['amount']:
                buy_id = self.exchange_mid.create_order('buy', trade_dict['trade_price'], trade_dict['amount'])  # Pending buy order
                sell_id = self.exchange_mid.create_order('sell', trade_dict['trade_price'], trade_dict['amount'])  # Pending sell orderself.traded_pairs['dui_qiao'].append({
                    'buy_id': buy_id, 'sell_id': sell_id, 'init_time': time.time(), 'amount': trade_dict['amount']
                })
                    
                self.last_time = time.time()  # Update last transaction timedefhandle_pending_orders(self):
        '''
        Processing unfulfilled orders
        '''
        pending_orders = self.exchange_mid.get_orders()  # Get uncompleted ordersiflen(pending_orders) > 0:
            for order in pending_orders:
                self.exchange_mid.cancel_order(order['Id'])  # Cancel uncompleted ordersdefcheck_order_status(self, current_time):
        '''
        Check order status
        current_time: Polling check times
        '''
        Log('1 Start order information check')
        Log(self.traded_pairs['dui_qiao'])
        self.buy_pending = 0self.sell_pending = 0for traded_pair inself.traded_pairs['dui_qiao'].copy():
            Log('Check the order:', traded_pair['buy_id'], traded_pair['sell_id'])

            try:
                buy_order_status = self.exchange_mid.exchange.GetOrder(traded_pair['buy_id'])  # Get buy order status
                sell_order_status = self.exchange_mid.exchange.GetOrder(traded_pair['sell_id'])  # Get sell order statusexcept:
                Log(traded_pair, 'cancel')
                self.exchange_mid.cancel_order(traded_pair['buy_id'])  # Cancel buy orderself.exchange_mid.cancel_order(traded_pair['sell_id'])  # Cancel sell orderself.traded_pairs['dui_qiao'].remove(traded_pair)  # Remove orderreturn

            Log('Check the order:', traded_pair['buy_id'], buy_order_status, traded_pair['sell_id'], sell_order_status, [sell_order_status['Status'], buy_order_status['Status']])
            if [sell_order_status['Status'], buy_order_status['Status']] == [0, 0]:
                self.buy_pending += 1self.sell_pending += 1if current_time % 5 == 0:
                    Log('Check pending orders and cancel pending orders (two unfinished)', buy_order_status['Status'], sell_order_status['Status'], current_time % 5)
                    self.exchange_mid.cancel_order(traded_pair['buy_id'])  # Cancel buy orderself.exchange_mid.cancel_order(traded_pair['sell_id'])  # Cancel sell orderself.pending_order_count += 1# The number of pending orders increases by 1self.traded_pairs['dui_qiao'].remove(traded_pair)  # Remove orderelif {sell_order_status['Status'], buy_order_status['Status']} == {1, 0}:
                if buy_order_status['Status'] == ORDER_STATE_PENDING:
                    self.buy_pending += 1if sell_order_status['Status'] == ORDER_STATE_PENDING:
                    self.sell_pending += 1if current_time % 5 == 0:
                    Log('Check pending orders and cancel pending orders (part one is not yet completed)', buy_order_status['Status'], sell_order_status['Status'])
                    self.done_amount['dui_qiao'] += traded_pair['amount']  # Update transaction volumeif buy_order_status['Status'] == ORDER_STATE_PENDING:
                        self.sell_amount += traded_pair['amount']
                        self.fee += sell_order_status['Amount'] * self.fee_rate * sell_order_status['Price']
                        Log('Cancel the buy order and add the unfinished buy list', traded_pair['buy_id'])
                        self.exchange_mid.cancel_order(traded_pair['buy_id'])  # Cancel buy orderself.pending_orders.append(['buy', buy_order_status['Status']])  # Record uncompleted orders
                        Log('Before clearing:', self.traded_pairs['dui_qiao'])
                        Log('Clear id:', traded_pair)
                        self.traded_pairs['dui_qiao'].remove(traded_pair)  # Remove order
                        Log('After clearing:', self.traded_pairs['dui_qiao'])
                    elif sell_order_status['Status'] == ORDER_STATE_PENDING:
                        self.buy_amount += traded_pair['amount']
                        self.fee += buy_order_status['Amount'] * self.fee_rate * buy_order_status['Price']
                        Log('Cancel the sell order and add it to the unfinished sell list', traded_pair['sell_id'])
                        self.exchange_mid.cancel_order(traded_pair['sell_id'])  # Cancel sell orderself.pending_orders.append(['sell', sell_order_status['Status']])  # Record uncompleted orders
                        Log('Before clearing:', self.traded_pairs['dui_qiao'])
                        Log('Clear id:', traded_pair)
                        self.traded_pairs['dui_qiao'].remove(traded_pair)  # Remove order
                        Log('After clearing:', self.traded_pairs['dui_qiao'])
                
            elif [sell_order_status['Status'], buy_order_status['Status']] == [1, 1]:
                Log('Both orders have been completed')
                self.buy_amount += traded_pair['amount']
                self.sell_amount += traded_pair['amount']
                self.fee += buy_order_status['Amount'] * self.fee_rate * buy_order_status['Price']
                self.fee += sell_order_status['Amount'] * self.fee_rate * sell_order_status['Price']
                Log('Completion status:', buy_order_status['Status'], sell_order_status['Status'], traded_pair['amount'])
                self.done_amount['dui_qiao'] += 2 * traded_pair['amount']  # Update transaction volumeself.traded_pairs['dui_qiao'].remove(traded_pair)  # Remove orderelse:
                Log('Two orders are in unknown status:', buy_order_status, sell_order_status)
                Log('Unknown order status:', buy_order_status['Status'], sell_order_status['Status'])
                Log('Unknown order information:', traded_pair)
        
    defupdate_status(self):

        self.exchange_mid.refresh_data()

        table1 = {
            "type": "table",
            "title": "Account information",
            "cols": [
                "Initial funds", "Existing funds", "Self-matching buy amount", "Self-matching sell  amount", "Fee rate", "Total return", "Rate of return"
            ],
            "rows": [
                [   
                    self.exchange_mid.init_equity,
                    self.exchange_mid.equity,
                    round(self.buy_amount, 4),
                    round(self.sell_amount, 4),
                    round(self.fee, 2),
                    self.exchange_mid.profit,
                    str(self.exchange_mid.profitratio) + "%"
                ],
            ],
        }

        LogStatus(
            f"Initialization time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.exchange_mid.init_timestamp))}\n",
            f"`{json.dumps(table1)}`\n",
            f"Last execution time: {_D()}\n"
        )

        LogProfit(round(self.exchange_mid.profit, 3), '&')

    defplot_pending(self):
        
        Log('Number of knock-on orders:', self.buy_pending, self.sell_pending)
        self.obj_chart = Chart(self.chart)
        now_time = int(time.time() * 1000)
        # Update pending order buy volume dataself.obj_chart.add(0, [now_time, self.buy_pending])
        # Update pending order selling volume dataself.obj_chart.add(1, [now_time, self.sell_pending])


defmain():
    '''
    Main function, running market making strategy
    '''
    exchange.IO('simulate', True)
    exchange.IO("trade_super_margin")
    
    target_amount = 1# Target transaction volume
    trade_amount = 0.01# Volume per transaction
    trade_dict = {}  # Initialize transaction dictionary
    
    exchange_mid = MidClass(exchange)  # Initialize the exchange middle layer
    Log(exchange_mid.refresh_data())  # Refresh data
    market_maker = MarketMaker(exchange_mid)  # Initialize market making strategy

    check_times = 0while market_maker.done_amount['dui_qiao'] < target_amount:  # Loop until the target transaction volume is reached
        Log(market_maker.traded_pairs['dui_qiao'])
        market_maker.check_order_status(check_times)  # Check order status
        Sleep(1000)  # Wait 1 second
        market_maker.refresh_data()  # Refresh dataiflen(market_maker.traded_pairs['dui_qiao']) < 1: # Price moves, market orders are cancelled, wait until all orders are completed, and create a new order dictionary
            
            Log('2 The number of trading pairs on the market is less than 1')
            trade_dict = market_maker.make_duiqiao_dict(trade_amount)  # Generate a dictionary of pending orders
            Log('New trading dictionary', trade_dict)
        
        if trade_dict:  # Check if the dictionary is not empty
            market_maker.make_trade_by_dict(trade_dict)  # Execute a trade

        Log('Market making quantity:', market_maker.done_amount['dui_qiao'])  # Record transaction volume

        market_maker.plot_pending()
        market_maker.update_status()

        check_times += 1
        
    Log(market_maker.position_amount, market_maker.can_buy_amount)  # Record holdings and available purchase quantities
    Log('Existing orders:', exchange.GetOrders())  # Record existing ordersdefonexit():
    Log("Execute the sweep function")

    _G("init_balance", None)
    _G("init_stocks", None)
    _G("init_equity", None)

From: An Overview of Market Making Strategies in Crypto: Architecture Design and FMZ Implementation of the Self-Matching Trading Strategy

Leave a Reply

Your email address will not be published. Required fields are marked *