2
Suivre
319
Abonnés

Une brève discussion sur les stratégies de création de marché de monnaies numériques : la conception de l'architecture de la stratégie d'impact et la mise en œuvre de la plateforme FMZ

Créé le: 2025-07-24 11:03:59, Mis à jour le: 2025-07-28 13:06:13
comments   0
hits   622

⚠️ Avis important

Cet article présente une stratégie d’augmentation du volume des transactions pour l’apprentissage du cadre de trading, fondamentalement différente de la stratégie traditionnelle d’arbitrage. L’objectif principal de cette stratégie est d’augmenter le volume des transactions en achetant et en vendant au même prix et en obtenant des remises de change ou des remises de niveau, plutôt que de réaliser des profits par arbitrage des différences de prix.

Le code fourni dans cet article n’est qu’un cadre de référence et ne repose sur aucune expérience opérationnelle en temps réel. La stratégie mise en œuvre dans cet article est uniquement destinée à l’apprentissage technique et à la recherche et n’a pas été entièrement vérifiée en situation réelle de marché. Lorsque les lecteurs se réfèrent au contenu de cet article, ils doivent procéder à des vérifications a posteriori et à une évaluation des risques suffisante, et ne doivent pas l’utiliser directement pour le trading en temps réel.


Sur le marché des devises numériques, les stratégies de market making ne sont pas seulement un outil permettant d’améliorer la liquidité du marché et de favoriser les transactions, mais aussi un élément clé de nombreuses stratégies de trading quantitatif. Les market makers réalisent des profits dans différents environnements de marché en publiant des cotations d’achat et de vente et en fournissant de la liquidité. L’implémentation du code des market makers professionnels est souvent extrêmement complexe, impliquant des fonctions avancées telles que l’optimisation des délais à haute fréquence, des systèmes complexes de contrôle des risques et l’arbitrage multi-bourses. Cette fois, nous étudierons les principes fondamentaux de la stratégie de trading de contrepartie en volume et la mise en œuvre d’un cadre d’apprentissage simplifié sur la plateforme Inventor Quantitative (FMZ).

Le corps de cet article est tiré de « L’idée et la méthode d’écriture de la stratégie de market making » de l’auteur original, Zinan. Certaines parties ont été optimisées et reproduites sur la plateforme fmz. Si certaines méthodes d’écriture peuvent paraître obsolètes aujourd’hui, il est néanmoins inspirant pour chacun de comprendre la structure du code et les principes fondamentaux du trading de contrepartie :

Le concept de stratégie de market making

La stratégie de tenue de marché consiste pour les traders (teneurs de marché) à placer simultanément des ordres d’achat et de vente sur le marché, fournissant ainsi de la liquidité pour maintenir la stabilité du marché. Cette stratégie contribue non seulement à maintenir la profondeur du marché, mais fournit également des contreparties aux autres traders. En fournissant des cotations d’achat et de vente dans différentes fourchettes de prix, les teneurs de marché tirent profit des fluctuations de prix.

Le rôle des teneurs de marché est crucial pour le marché des cryptomonnaies, en particulier sur les marchés à faible volume d’échange et à forte volatilité. En fournissant de la liquidité, les teneurs de marché contribuent à réduire les dérapages et offrent aux traders des prix plus faciles à négocier.

Le principe fondamental d’une stratégie traditionnelle de market making est de gagner le spread acheteur-vendeur en fournissant de la liquidité. Le prix de l’ordre d’achat affiché par le market maker est inférieur au prix de l’ordre de vente, et les profits sont réalisés grâce au spread de transaction. Par exemple, lorsque le prix spot augmente, le market maker vend à un prix plus élevé et achète à un prix plus bas, gagnant ainsi le spread. Les principales sources de revenus sont :

  • PropagéLes teneurs de marché traditionnels réalisent des bénéfices en passant des ordres d’achat et de vente et en profitant des différences de prix.
  • Chiffre d’affaires du volume des transactionsLes revenus des teneurs de marché sont étroitement liés au volume de transactions qu’ils génèrent. Un volume de transactions plus important se traduit non seulement par une fréquence de transactions plus élevée et de meilleures opportunités de profit, mais apporte également les avantages suivants :
    • Remise des fraisAfin d’encourager les teneurs de marché à fournir des liquidités, de nombreuses bourses accordent un certain pourcentage de remboursement de frais, voire des frais négatifs (c’est-à-dire que la bourse paie des frais aux teneurs de marché).
    • Avantages du niveau VIPLorsque le volume des transactions atteint un certain seuil, les teneurs de marché peuvent obtenir des frais de traitement inférieurs et réduire les coûts de transaction.
    • Programme d’incitation pour les teneurs de marchéCertaines bourses ont des programmes d’incitation dédiés aux teneurs de marché, offrant des récompenses supplémentaires en fonction de la qualité de la liquidité fournie

Cependant, les teneurs de marché sont également confrontés au risque de volatilité, notamment dans le contexte très volatile du marché des monnaies numériques. Les fortes fluctuations du marché peuvent entraîner des écarts importants entre les ordres d’achat et de vente passés par les teneurs de marché et le prix réel, entraînant des pertes.

Types de stratégies de tenue de marché

Sur le marché des cryptomonnaies, les teneurs de marché choisissent généralement différentes stratégies de tenue de marché en fonction des conditions du marché, du volume des transactions, de la volatilité, etc. Les types courants de stratégies de tenue de marché incluent :

  • Stratégies de tenue de marché passiveLes teneurs de marché placent des ordres d’achat et de vente en fonction de la profondeur du marché, de la volatilité historique et d’autres facteurs, et attendent les transactions. Cette stratégie se caractérise par une faible fréquence et une grande robustesse, et les teneurs de marché s’appuient sur les fluctuations du marché pour réaliser des profits.

  • Stratégie de tenue de marché active:Dans le cadre de cette stratégie, les teneurs de marché ajustent le prix et le volume des ordres d’achat et de vente en temps réel en fonction des conditions du marché afin d’augmenter la probabilité de transaction. Les teneurs de marché augmentent généralement le nombre d’ordres lorsque le prix est proche du cours actuel afin de mieux tirer parti des fluctuations du marché.

  • Stratégie d’augmentation du volume:Le type de stratégie sur lequel cet article se concentre.Stratégie d’augmentation du volumeIl s’agit d’une stratégie visant à augmenter le volume des transactions en achetant et en vendant au même prix. Contrairement aux stratégies traditionnelles de market making, l’objectif principal de cette stratégie n’est pas de générer des différences de prix, mais d’obtenir des remises de change, des remises de niveau ou des récompenses de minage de liquidité grâce à un grand nombre de transactions.

Dans la stratégie de « volume-washing », les teneurs de marché placent des ordres d’achat et de vente au même prix. Une fois les ordres exécutés, bien qu’il n’y ait pas de profit sur la différence de prix, le volume de transactions peut s’accumuler rapidement. Le modèle de profit de cette stratégie repose entièrement sur le mécanisme d’incitation de la bourse, plutôt que sur l’arbitrage de marché.

Caractéristiques principales :

  • Commerce au même prix:Contrairement aux stratégies traditionnelles de création de marché, le prix d’achat et le prix de vente sont les mêmes et aucun profit de différence de prix n’est généré.

  • Orienté vers le volume:L’objectif principal de la stratégie est d’accumuler rapidement du volume de transactions plutôt que de procéder à un arbitrage de prix.

  • Dépendance aux incitations:Les bénéfices dépendent entièrement de la politique de remise de la bourse, des remises de niveau VIP ou des programmes d’incitation des teneurs de marché.

Différences importantes : Comparées aux stratégies traditionnelles de tenue de marché, les stratégies de wash trading ne génèrent pas de profits en fournissant une liquidité réelle au marché, mais en créant artificiellement un volume de transactions pour obtenir des récompenses réglementaires de la part de la bourse. Cette stratégie peut présenter des risques de conformité dans certaines juridictions et doit être soigneusement évaluée dans sa mise en œuvre.

Logique de profit de la stratégie de contre-trading basée sur le volume

En analysant le code, nous pouvons constater que le prix d’achat et le prix de vente dans cette stratégie sont exactement les mêmes :

def make_duiqiao_dict(self, trade_amount):
    mid_price = self.mid_price  # 中间价
    trade_price = round(mid_price, self.price_precision)  # 精准交易价格
    trade_dict = {
        'trade_price': trade_price,  # 买卖都使用同一个价格
        'amount': trade_amount
    }
    return trade_dict

Logique du profit réel

1. Stratégie de volume de transactions

  • L’objectif principal de cette stratégie est d’augmenter le volume des transactions grâce à un grand nombre de transactions.
  • Bénéficiez de remises sur les échanges, de réductions par niveau ou de récompenses d’extraction de liquidités
  • Applicable aux bourses avec des programmes d’incitation pour les teneurs de marché

2. Mécanisme de remboursement des frais

  • Cela dépend de la politique de frais négatifs de la bourse (les taux Maker sont négatifs)
  • Obtenez des réductions de frais en fournissant des liquidités
  • Besoin d’échanges pour soutenir les taux préférentiels des teneurs de marché

Scénarios et risques applicables

✅ Scénarios applicables

  • La bourse a des politiques préférentielles claires pour les teneurs de marché
  • Convient aux mises à niveau de niveau VIP avec des exigences de volume de transactions plus élevées
  • Plateformes avec activités d’extraction de liquidités ou de rabais

❌ Non applicable

  • Échanges sans mécanisme de remise
  • Plateformes avec des frais de transaction plus élevés
  • Bourses avec des restrictions claires sur le wash trading

⚠️ Rappel des risques

  • Si les ordres d’achat et de vente sont exécutés en même temps, il y a généralement une perte après déduction des frais de traitement.
  • Si la politique d’échange change, la stratégie peut devenir invalide
  • Nécessite une surveillance continue des coûts des honoraires
  • Peut être confronté à des risques de non-conformité (certaines régions ont des restrictions sur le comportement de brossage)

Analyse du cadre stratégique de répercussion

Cet article s’appuie sur le framework de code de M. Zinan pour présenter une implémentation simple d’une stratégie d’augmentation du volume, axée sur la manière d’accumuler du volume d’échanges grâce à une stratégie d’achat et de vente à prix constant en bourse. Ce framework stratégique se compose de deux classes principales :MidClass et MarketMakerCes deux classes sont responsables de l’interaction de la couche intermédiaire de l’échange et de l’exécution spécifique de la stratégie de knock-on.

Cette architecture stratégique adopte une conception en couches, séparant l’interface d’échange et la stratégie de tenue de marché afin de garantir une évolutivité et une flexibilité optimales du système. Les principaux composants de cette architecture sont les suivants :

  1. MidClass:La couche intermédiaire d’échange est chargée d’interagir avec l’interface d’échange pour obtenir des données de marché, des informations de compte, l’état des commandes, etc. Cette couche encapsule toutes les interactions avec les échanges externes pour garantir que la logique de trading et l’interface d’échange sont découplées.
  2. MarketMaker: Classe de stratégie de création de marché, chargée d’exécuter la stratégie de knock-to-trade, de générer des ordres en attente, de vérifier l’état des ordres, de mettre à jour l’état de la stratégie, etc. Elle interagit avec la couche intermédiaire de l’échange pour fournir des opérations spécifiques de création de marché et de knock-to-trade.

MidClass

MidClassEn tant que couche intermédiaire de l’échange, sa principale responsabilité est de gérer l’interaction avec l’échange, d’encapsuler tous les appels d’API externes et de fournir une interface concise pourMarketMakerUtilisation. Son architecture comprend les fonctions clés suivantes :

  1. Acquisition de données de marché

    • Obtenez des données en temps réel du marché, telles que le prix du marché, la profondeur, la ligne K, etc. Il doit actualiser régulièrement les données du marché pour garantir que les données sont à jour lorsque la stratégie est exécutée.
  2. Gestion des informations de compte

    • Obtenez des informations sur le compte, telles que le solde du compte, l’état de la position, etc. Ceci est essentiel pour la gestion des fonds et le contrôle des risques.
  3. Gestion des commandes

    • Fournit des interfaces pour créer, interroger et annuler des ordres. Ces interfaces constituent la base de l’exécution des stratégies de market making et garantissent la génération et la gestion des ordres en attente sur le marché.
  4. Stockage et mise à jour des données

    • Maintenir des connexions avec les échanges pour mettre à jour en permanence les données à utiliser par les stratégies.

En encapsulant ces fonctions dansMidClassDans l’exemple ci-dessus, vous pouvez vous assurer que la classe de stratégie de trading (telle queMarketMaker) Concentrez-vous sur l’exécution de vos stratégies de trading sans vous soucier de l’interaction avec les plateformes d’échange. Cette structure améliore la maintenabilité et l’évolutivité du système, facilitant ainsi l’ajout de supports pour différentes plateformes d’échange ou l’optimisation des fonctions existantes.

MarketMaker

MarketMakerIl s’agit de la classe centrale de la stratégie de cross-trading, responsable de l’exécution des opérations de tenue de marché et des transactions de cross-trading. Son architecture comprend les modules principaux suivants :

  1. initialisation

    • Initialiser la couche intermédiaire d’échangeMidClass, obtenez des informations de base sur la bourse, telles que les paires de trading, la précision, la profondeur du marché, etc.
    • Initialisez la stratégie de création de marché et définissez les paramètres nécessaires, tels que le nombre d’ordres en attente, l’écart acheteur-vendeur, etc. Ces paramètres affecteront la méthode d’exécution et l’effet de la stratégie.
  2. Actualisation des données

    • Actualisez régulièrement les données du marché, y compris les informations de compte en temps réel, le prix du marché, la profondeur, la ligne K, etc. Ces données fournissent des informations de base pour l’exécution des stratégies.
    • La fréquence de rafraîchissement peut être ajustée en fonction de la volatilité du marché pour garantir une réponse en temps réel aux changements du marché.
  3. Exécution de la stratégie de frappe

    • Génération de commandes en attenteGénérer un dictionnaire d’ordres en attente basé sur la profondeur du marché et les fluctuations de prix actuelles. Ce dictionnaire contient le prix et le nombre d’ordres d’achat et de vente, généralement calculés automatiquement en fonction des paramètres de la stratégie.
    • Exécution de transactions à effet domino:Une fois la commande en attente générée,MarketMakerIl sera soumis au marché et les ordres d’achat et de vente seront exécutés simultanément. L’objectif est d’accumuler rapidement du volume d’échange en achetant et en vendant au même prix.
    • Vérification de l’état de la commande:Lors de l’exécution d’une transaction de type knock-on,MarketMakerLe système vérifie en permanence l’état de la commande en attente afin de garantir son traitement à temps. En cas d’échec, le prix ou la quantité de la commande en attente est ajusté jusqu’à son exécution.
  4. Mise à jour du statut

    • Mise à jour de l’état de la politique: Mettez à jour régulièrement l’état de la stratégie, y compris le volume des transactions, les commandes terminées, les frais, etc. Grâce à ces informations d’état, les utilisateurs peuvent surveiller les performances de la stratégie en temps réel.
    • Gestion des risques:La stratégie de frappe doit être exécutée dans différents environnements de marché.MarketMakerLa méthode d’exécution de la stratégie sera ajustée de manière dynamique pour s’adapter aux différents environnements de marché.

Reproduction de la logique de la stratégie de frappe

La mise en œuvre de la stratégie de cross-trading dépend de données de marché précises et d’une exécution rapide.MarketMakerEn surveillant la situation du marché en temps réel et en utilisant la méthode du contre-ordre (achat et vente au même prix), les objectifs stratégiques peuvent être atteints en accumulant rapidement le volume des transactions.

initialisation

exister MarketMakerDans la méthode d’initialisation de classe, obtenez d’abord les informations de précision de l’échange et initialisez les paramètres de stratégie, tels que la précision du volume des transactions, la précision des prix, etc.

self.precision_info = self.exchange_mid.get_precision()  # 获取精度信息
self.price_precision = self.precision_info['price_precision']  # 价格精度
self.amount_precision = self.precision_info['amount_precision']  # 交易量精度

Générer un dictionnaire des commandes à impact

Le cœur de la stratégie de cross-trading consiste à générer un dictionnaire d’ordres de cross-trading, incluant les prix d’achat et de vente, ainsi que les quantités. Le code génère ce dictionnaire en calculant le prix médian.

def make_duiqiao_dict(self, trade_amount):
    mid_price = self.mid_price  # 中间价
    trade_price = round(mid_price, self.price_precision)  # 精准交易价格
    trade_dict = {
        'trade_price': trade_price,
        'amount': trade_amount
    }
    return trade_dict

Exécuter une transaction en cascade

Selon le dictionnaire généré des ordres de cross-trading, la transaction de cross-trading est exécutée.create_orderMéthode, passez des ordres d’achat et des ordres de vente en même temps.

def make_trade_by_dict(self, trade_dict):
    if self.position_amount > trade_dict['amount'] and self.can_buy_amount > trade_dict['amount']:
        buy_id = self.exchange_mid.create_order('buy', trade_dict['trade_price'], trade_dict['amount'])  # 挂买单
        sell_id = self.exchange_mid.create_order('sell', trade_dict['trade_price'], trade_dict['amount'])  # 挂卖单
        self.traded_pairs['dui_qiao'].append({
            'buy_id': buy_id, 'sell_id': sell_id, 'init_time': time.time(), 'amount': trade_dict['amount']
        })

Vérifier l’état de la commande

Vérifiez régulièrement l’état des commandes et traitez les commandes non terminées.GetOrderMéthode permettant d’obtenir le statut de la commande et de décider de l’annuler ou non en fonction de ce statut. La logique de traitement d’une commande à élimination directe comprend principalement les étapes suivantes :

  1. Obtenir l’état de la commande

    • Obtenez le statut des ordres d’achat et de vente via l’interface d’échange.
    • Si l’obtention du statut de la commande échoue (par exemple, la commande n’existe pas ou il y a un problème de réseau), la commande est annulée et supprimée de l’enregistrement.
  2. Statut de l’ordonnance jugement

    • En fonction du statut de la commande, déterminez si la commande est terminée, partiellement terminée ou inachevée.
    • Les statuts de commande peuvent inclure :
      • 0(ORDER_STATE_PENDING):Non terminé (commande en attente).
      • 1(ORDER_STATE_CLOSED):Terminé (entièrement commercialisé).
      • 2(ORDER_STATE_CANCELED):Révoqué.
      • 3(ORDER_STATE_UNKNOWN): Le statut est inconnu.
  3. Traitement du statut de la commande

    • Les deux commandes ne sont pas terminées
      • Si les ordres d’achat et de vente ne sont pas terminés (le statut est0), puis selon les horaires de sondage (current_time % 5 == 0) décide d’annuler ou non la commande.
      • Après avoir annulé la commande, mettez à jour le nombre de commandes en attente et supprimez la commande de l’enregistrement.
    • Une commande terminée, une autre non terminée
      • Si une commande est terminée (le statut est1), et une autre commande n’est pas terminée (le statut est0), puis décidez d’annuler ou non la commande inachevée en fonction des heures d’interrogation.
      • Après avoir annulé une commande ouverte, mettez à jour le volume et la liste des commandes ouvertes et supprimez la commande de l’enregistrement.
    • Les deux commandes ont été exécutées
      • Si les ordres d’achat et de vente sont tous deux terminés (le statut est1), le volume de transaction est mis à jour et la commande est supprimée de l’enregistrement.
    • Statut de la commande inconnu
      • Si le statut de la commande n’est ni0Pas vraiment1, il est enregistré comme statut inconnu et enregistré.
  4. Mettre à jour l’enregistrement

    • En fonction des résultats du traitement du statut des commandes, mettez à jour le volume des transactions, la liste des commandes non terminées et le nombre de commandes en attente.

Perspectives stratégiques ultérieures

La stratégie d’augmentation du volume présentée dans cet article sert principalement à comprendre l’architecture du cadre de trading, et sa valeur pratique est limitée. Si les lecteurs s’intéressent aux stratégies de market making, nous présenterons ultérieurement des stratégies plus pratiques :

1. Stratégie de tenue de marché

  • Véritable stratégie d’arbitrage basée sur le spread bid-ask
  • Passez un ordre entre le prix d’achat et le prix de vente pour gagner la différence de prix
  • Plus conforme au modèle de profit des teneurs de marché traditionnels

2. Stratégie de création de marché dynamique

  • Ajuster dynamiquement les prix des commandes en attente en fonction de la volatilité du marché
  • Introduction de mécanismes de gestion des stocks et de contrôle des risques
  • Création de marché adaptative pour s’adapter à différents environnements de marché

3. Stratégie de création de marché à plusieurs niveaux

  • Passer des commandes à plusieurs niveaux de prix simultanément
  • Améliorer la stabilité globale du rendement en diversifiant les risques
  • Plus proche du fonctionnement réel des teneurs de marché professionnels

Ces stratégies se concentreront davantage sur la logique de profit réelle et la gestion des risques, fournissant ainsi une référence plus précieuse aux traders quantitatifs.

Perspectives stratégiques

La stratégie de trading fictif repose sur la politique d’incitation de la bourse. Tout changement de politique peut invalider la stratégie. Par conséquent, elle doit pouvoir s’adapter aux changements de politique, comme la surveillance dynamique du taux de commission ou l’introduction de plusieurs modèles de profit afin de réduire le risque de dépendance unique. De plus, la stratégie de trading fictif peut être considérée comme une manipulation de marché et comporter des risques réglementaires. Dans la pratique, les traders doivent être attentifs aux lois et réglementations en vigueur afin de garantir la conformité de la stratégie et d’éviter les pertes dues aux problèmes réglementaires.

J’espère que les lecteurs pourront optimiser et améliorer leurs stratégies en s’appuyant sur leurs propres concepts de trading et leur compréhension du marché, grâce à leur compréhension du cadre de base. Le charme du trading quantitatif réside dans l’apprentissage, la pratique et l’amélioration continus. Je vous souhaite à tous de continuer à progresser sur la voie du trading quantitatif !

Code de stratégie

import time, json

class MidClass:
    def __init__(self, exchange_instance):
        '''
        初始化交易所中间层
        
        Args:
            exchange_instance: FMZ的交易所结构
        '''
        self.init_timestamp = time.time()  # 记录初始化时间
        self.exchange = exchange_instance  # 保存交易所对象
        self.exchange_name = self.exchange.GetName()  # 获取交易所名称
        self.trading_pair = self.exchange.GetCurrency()  # 获取交易对名称(如 BTC_USDT)

    def get_precision(self):
        '''
        获取交易对的精度信息
        
        Returns:
            返回包含精度信息的字典,失败时返回 None
        '''
        symbol_code = self.exchange.GetCurrency()
        ticker = self.exchange.GetTicker(symbol_code)  # 回测系统需要
        exchange_info = self.exchange.GetMarkets()
        data = exchange_info.get(symbol_code)

        if not data:
            Log("获取市场信息失败", GetLastError())
            return None

        # 获取该交易对的精度信息
        self.precision_info = {
            'tick_size': data['TickSize'],                  # 价格精度
            'amount_size': data['AmountSize'],              # 数量精度
            'price_precision': data['PricePrecision'],      # 价格小数位精度
            'amount_precision': data['AmountPrecision'],    # 数量小数位精度
            'min_qty': data['MinQty'],                      # 最小下单数量
            'max_qty': data['MaxQty']                       # 最大下单数量
        }

        return self.precision_info

    def get_account(self):
        '''
        获取账户信息
        
        Returns:
            获取信息成功返回 True,获取信息失败返回 False
        '''

        self.balance = '---'  # 账户余额
        self.amount = '---'  # 账户持仓量
        self.frozen_balance = '---'  # 冻结余额
        self.frozen_stocks = '---'  # 冻结持仓量
        self.init_balance = None
        self.init_stocks = None
        self.init_equity = None

        try:
            account_info = self.exchange.GetAccount()  # 获取账户信息
            self.balance = account_info['Balance']  # 更新账户余额
            self.amount = account_info['Stocks']  # 更新持仓量
            self.frozen_balance = account_info['FrozenBalance']  # 更新冻结余额
            self.frozen_stocks = account_info['FrozenStocks']  # 更新冻结持仓量
            self.equity = self.balance + self.frozen_balance + (self.amount + self.frozen_stocks) * self.last_price
            
            if not self.init_balance or not self.init_stocks or not self.init_equity:
                if _G("init_balance") and _G("init_balance") > 0 and _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('获取初始eqity', self.init_equity)

            self.profit = self.equity - self.init_equity
            self.profitratio = round((self.equity - self.init_equity)/self.init_equity, 4) * 100

            return True
        except:
            return False  # 获取账户信息失败

    def get_ticker(self):
        '''
        获取市价信息(如买一价、卖一价、最高价、最低价等)
        
        Returns:
            获取信息成功返回 True,获取信息失败返回 False
        '''
        self.high_price = '---'  # 最高价
        self.low_price = '---'  # 最低价
        self.sell_price = '---'  # 卖一价
        self.buy_price = '---'  # 买一价
        self.last_price = '---'  # 最新成交价
        self.volume = '---'  # 成交量
        
        try:
            ticker_info = self.exchange.GetTicker()  # 获取市价信息
        
            self.high_price = ticker_info['High']  # 更新最高价
            self.low_price = ticker_info['Low']  # 更新最低价
            self.sell_price = ticker_info['Sell']  # 更新卖一价
            self.buy_price = ticker_info['Buy']  # 更新买一价
            self.last_price = ticker_info['Last']  # 更新最新成交价
            self.volume = ticker_info['Volume']  # 更新成交量
            return True
        except:
            return False  # 获取市价信息失败
        
    def get_depth(self):
        '''
        获取深度信息(买卖盘的挂单列表)
        
        Returns:
            获取信息成功返回 True,获取信息失败返回 False
        '''
        self.ask_orders = '---'  # 卖盘挂单列表
        self.bid_orders = '---'  # 买盘挂单列表
        
        try:
            depth_info = self.exchange.GetDepth()  # 获取深度信息
            self.ask_orders = depth_info['Asks']  # 更新卖盘挂单列表
            self.bid_orders = depth_info['Bids']  # 更新买盘挂单列表
            return True
        except:
            return False  # 获取深度信息失败
        
    def get_ohlc_data(self, period=PERIOD_M5):
        '''
        获取K线信息
        
        Args:
            period: K线周期,PERIOD_M1 指1分钟, PERIOD_M5 指5分钟, PERIOD_M15 指15分钟,
            PERIOD_M30 指30分钟, PERIOD_H1 指1小时, PERIOD_D1 指一天。
        '''
        self.ohlc_data = self.exchange.GetRecords(period)  # 获取K线数据
        
    def create_order(self, order_type, price, amount):
        '''
        提交一个挂单信息
        
        Args:
            order_type:挂单类型,'buy'指挂买单,'sell'指挂卖单
            price:挂单价格
            amount:挂单数量
            
        Returns:
            挂单Id号,可用以取消挂单
        '''
        if order_type == 'buy':
            try:
                order_id = self.exchange.Buy(price, amount)  # 提交买单
            except:
                return False  # 买单提交失败
            
        elif order_type == 'sell':
            try:
                order_id = self.exchange.Sell(price, amount)  # 提交卖单
            except:
                return False  # 卖单提交失败
        
        return order_id  # 返回订单ID
    
    def get_orders(self):
        '''
        获取未完成的订单列表
        
        Returns:
            未完成的订单列表
        '''
        self.open_orders = self.exchange.GetOrders()  # 获取未完成订单
        return self.open_orders
    
    def cancel_order(self, order_id):
        '''
        取消一个挂单信息
        
        Args:
            order_id:希望取消的挂单ID号
            
        Returns:
            取消挂单成功返回 True,取消挂单失败返回 False
        '''
        return self.exchange.CancelOrder(order_id)  # 取消订单
        
    def refresh_data(self):
        '''
        刷新信息(账户、市价、深度、K线)
        
        Returns:
            刷新信息成功返回 'refresh_data_finish!' 否则返回相应刷新失败的信息提示
        '''

        if not self.get_ticker():  # 刷新市价信息
            return 'false_get_ticker'

        if not self.get_account():  # 刷新账户信息
            return 'false_get_account'
        
        if not self.get_depth():  # 刷新深度信息
            return 'false_get_depth'
        
        try:
            self.get_ohlc_data()  # 刷新K线信息
        except:
            return 'false_get_K_line_info'
        
        return 'refresh_data_finish!'  # 刷新成功

class MarketMaker:
    def __init__(self, mid_class):
        '''
        初始化做市策略
        
        Args:
            mid_class: 交易所中间层对象
        '''
        self.exchange_mid = mid_class  # 交易所中间层对象
        self.precision_info = self.exchange_mid.get_precision()  # 获取精度信息

        self.done_amount = {'dui_qiao': 0}  # 已完成交易量
        self.price_precision = self.precision_info['price_precision']  # 价格精度
        self.amount_precision = self.precision_info['amount_precision']  # 交易量精度
        
        self.traded_pairs = {'dui_qiao': []}  # 已挂单的交易对
        self.pending_orders = []  # 未完成的订单状态
        self.pending_order_count = 0  # 挂单次数

        self.buy_amount = 0
        self.sell_amount = 0

        self.fee = 0
        self.fee_rate = 0.08 / 100

        self.chart = {
            "__isStock": True,
            "tooltip": {"xDateFormat": "%Y-%m-%d %H:%M:%S, %A"},
            "title": {"text": "挂单数量"},
            "xAxis": {"type": "datetime"},
            "yAxis": {
                "title": {"text": "挂单数量"},
                "opposite": False
            },
            "series": [
                {"name": "挂单买量", "id": "挂单买量", "data": []},
                {"name": "挂单卖量", "id": "挂单卖量", "dashStyle": "shortdash", "data": []}
            ]
        }
    
    def refresh_data(self):
        '''
        刷新数据(账户、市价、深度、K线)
        '''
        self.exchange_mid.refresh_data()  # 刷新交易所数据
        self.position_amount = 0 if isinstance(self.exchange_mid.amount, str) else self.exchange_mid.amount  # 持仓量
        self.available_balance = 0 if isinstance(self.exchange_mid.balance, str) else self.exchange_mid.balance  # 账户余额
        Log('检查ticker', self.exchange_mid.buy_price)
        self.can_buy_amount = self.available_balance / float(self.exchange_mid.buy_price)  # 可买的数量
        self.mid_price = (self.exchange_mid.sell_price + self.exchange_mid.buy_price) / 2  # 中间价

    def make_duiqiao_dict(self, trade_amount):
        
        '''
        生成对敲挂单字典
        
        Args:
            trade_amount: 每次交易量
        
        Returns:
            对敲挂单字典列表
        '''
        Log('3制作对敲挂单字典')

        mid_price = self.mid_price  # 中间价

        trade_price = round(mid_price, self.price_precision)  # 精准交易价格

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

        Log('返回盘口挂单字典:', trade_dict)
        return trade_dict
    
    def make_trade_by_dict(self, trade_dict):
        '''
        根据交易字典执行交易
        
        Args:
            trade_dict: 交易字典
        '''
        Log('4按照字典开始交易')
        self.refresh_data()  # 刷新数据
        
        if trade_dict:
            Log('当前账户资金: 币数余额: ', self.position_amount, '资金余额: ', self.can_buy_amount)
            Log('检查开仓: 币数限制: ', self.position_amount > trade_dict['amount'], '资金限制: ', self.can_buy_amount > trade_dict['amount'])
            if self.position_amount > trade_dict['amount'] and self.can_buy_amount > trade_dict['amount']:
                buy_id = self.exchange_mid.create_order('buy', trade_dict['trade_price'], trade_dict['amount'])  # 挂买单
                sell_id = self.exchange_mid.create_order('sell', trade_dict['trade_price'], trade_dict['amount'])  # 挂卖单
                
                self.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()  # 更新上次交易时间
        
    def handle_pending_orders(self):
        '''
        处理未完成的订单
        '''
        pending_orders = self.exchange_mid.get_orders()  # 获取未完成订单
        if len(pending_orders) > 0:
            for order in pending_orders:
                self.exchange_mid.cancel_order(order['Id'])  # 取消未完成订单

    def check_order_status(self, current_time):
        '''
        检查订单状态
        current_time: 轮询检查次数
        '''
        Log('1开始订单信息检查')
        Log(self.traded_pairs['dui_qiao'])
        self.buy_pending = 0
        self.sell_pending = 0
        for traded_pair in self.traded_pairs['dui_qiao'].copy():
            Log('检查订单:', traded_pair['buy_id'], traded_pair['sell_id'])

            try:
                buy_order_status = self.exchange_mid.exchange.GetOrder(traded_pair['buy_id'])  # 获取买单状态
                sell_order_status = self.exchange_mid.exchange.GetOrder(traded_pair['sell_id'])  # 获取卖单状态
            except:
                Log(traded_pair, '取消')
                self.exchange_mid.cancel_order(traded_pair['buy_id'])  # 取消买单
                self.exchange_mid.cancel_order(traded_pair['sell_id'])  # 取消卖单
                self.traded_pairs['dui_qiao'].remove(traded_pair)  # 移除订单
                return

            Log('检查订单:', 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 += 1
                self.sell_pending += 1
                if current_time % 5 == 0:
                    Log('检查挂单,取消挂单(两未完)', buy_order_status['Status'], sell_order_status['Status'], current_time % 5)
                    self.exchange_mid.cancel_order(traded_pair['buy_id'])  # 取消买单
                    self.exchange_mid.cancel_order(traded_pair['sell_id'])  # 取消卖单
                    self.pending_order_count += 1  # 挂单次数加1
                    self.traded_pairs['dui_qiao'].remove(traded_pair)  # 移除订单

            elif {sell_order_status['Status'], buy_order_status['Status']} == {1, 0}:
                if buy_order_status['Status'] == ORDER_STATE_PENDING:
                    self.buy_pending += 1
                if sell_order_status['Status'] == ORDER_STATE_PENDING:
                    self.sell_pending += 1
                if current_time % 5 == 0:
                    Log('检查挂单,取消挂单(一未完)', buy_order_status['Status'], sell_order_status['Status'])
                    self.done_amount['dui_qiao'] += traded_pair['amount']  # 更新交易量
                    if 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('取消该买订单,增加未完成买列表', traded_pair['buy_id'])
                        self.exchange_mid.cancel_order(traded_pair['buy_id'])  # 取消买单
                        self.pending_orders.append(['buy', buy_order_status['Status']])  # 记录未完成订单
                        Log('清除前:', self.traded_pairs['dui_qiao'])
                        Log('清除id:', traded_pair)
                        self.traded_pairs['dui_qiao'].remove(traded_pair)  # 移除订单
                        Log('清除后:', 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('取消该卖订单,增加未完成卖列表', traded_pair['sell_id'])
                        self.exchange_mid.cancel_order(traded_pair['sell_id'])  # 取消卖单
                        self.pending_orders.append(['sell', sell_order_status['Status']])  # 记录未完成订单
                        Log('清除前:', self.traded_pairs['dui_qiao'])
                        Log('清除id:', traded_pair)
                        self.traded_pairs['dui_qiao'].remove(traded_pair)  # 移除订单
                        Log('清除后:', self.traded_pairs['dui_qiao'])
                
            elif [sell_order_status['Status'], buy_order_status['Status']] == [1, 1]:
                Log('两订单都已完成')
                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('完成状态:', buy_order_status['Status'], sell_order_status['Status'], traded_pair['amount'])
                self.done_amount['dui_qiao'] += 2 * traded_pair['amount']  # 更新交易量
                self.traded_pairs['dui_qiao'].remove(traded_pair)  # 移除订单
            else:
                Log('两订单处于未知状态:', buy_order_status, sell_order_status)
                Log('未知订单状态:', buy_order_status['Status'], sell_order_status['Status'])
                Log('未知订单信息:', traded_pair)
        
    def update_status(self):

        self.exchange_mid.refresh_data()

        table1 = {
            "type": "table",
            "title": "账户信息",
            "cols": [
                "初始资金", "现存资金", "对敲买入数量", "对敲卖出数量", "费率", "总收益", "收益率"
            ],
            "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"初始化时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.exchange_mid.init_timestamp))}\n",
            f"`{json.dumps(table1)}`\n",
            f"最后执行时间: {_D()}\n"
        )

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

    def plot_pending(self):
        
        Log('对敲挂单数量:', self.buy_pending, self.sell_pending)
        self.obj_chart = Chart(self.chart)
        now_time = int(time.time() * 1000)
        # 更新挂单买量数据
        self.obj_chart.add(0, [now_time, self.buy_pending])
        # 更新挂单卖量数据
        self.obj_chart.add(1, [now_time, self.sell_pending])


def main():
    '''
    主函数,运行做市策略
    '''
    exchange.IO('simulate', True)
    exchange.IO("trade_super_margin")
    
    target_amount = 1  # 目标交易量
    trade_amount = 0.01  # 每次交易量
    trade_dict = {}  # 初始化交易字典
    
    exchange_mid = MidClass(exchange)  # 初始化交易所中间层
    Log(exchange_mid.refresh_data())  # 刷新数据
    market_maker = MarketMaker(exchange_mid)  # 初始化做市策略

    check_times = 0
    
    while market_maker.done_amount['dui_qiao'] < target_amount:  # 循环直到完成目标交易量
        Log(market_maker.traded_pairs['dui_qiao'])
        market_maker.check_order_status(check_times)  # 检查订单状态
        Sleep(1000)  # 等待1秒
        market_maker.refresh_data()  # 刷新数据
        
        if len(market_maker.traded_pairs['dui_qiao']) < 1: # 价格移动,盘口挂单撤销,等待至所有挂单完毕,制定新的挂单字典
            
            Log('2盘口交易对数量小于1')
            trade_dict = market_maker.make_duiqiao_dict(trade_amount)  # 生成盘口挂单字典
            Log('新交易字典', trade_dict)
        
        if trade_dict:  # 判断字典是否非空
            market_maker.make_trade_by_dict(trade_dict)  # 执行交易

        Log('盘口做市数量:', market_maker.done_amount['dui_qiao'])  # 记录交易量

        market_maker.plot_pending()
        market_maker.update_status()

        check_times += 1
        
    Log(market_maker.position_amount, market_maker.can_buy_amount)  # 记录持仓量和可买数量
    Log('现存订单:', exchange.GetOrders())  # 记录现存订单

def onexit():
    Log("执行扫尾函数")

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