avatar of ianzeng123 ianzeng123
关注 私信
2
关注
319
关注者

浅谈数字货币做市策略:对敲策略的架构设计与FMZ平台实现

创建于: 2025-07-24 11:03:59, 更新于: 2025-07-28 13:06:13
comments   0
hits   622

浅谈数字货币做市策略:对敲策略的架构设计与FMZ平台实现

⚠️ 重要声明

本文展示的是一种用于学习交易框架架构的刷量型对敲策略,与传统的套利做市策略有本质区别。该策略主要目的是通过同价买卖增加交易量,获取交易所返佣或等级优惠,而非通过价差套利获得收益。

本文所提供的代码仅为参考框架,不具有任何实盘运行经验。文中的策略实现仅用于技术学习和研究目的,未经过实际市场环境的充分验证。读者在参考本文内容时,务必进行充分的回测验证和风险评估,切勿直接用于实盘交易。


在数字货币市场,做市策略不仅是提高市场流动性和促进交易的工具,还是许多量化交易策略中的关键组成部分。做市商通过挂出买卖报价、提供流动性,在不同市场环境下进行利润获取。专业做市商的代码实现往往极其复杂,涉及高频延迟优化、复杂风控系统、多交易所套利等高级功能。本次我们来研究一下刷量型对敲策略的基本思路,以及如何在发明者量化(FMZ)平台上实现一个简化的学习框架。

这篇文章的主体来源自子楠原作者的《做市策略的思路及写法》,对个别地方进行了优化,在fmz平台进行了复现,以现在的眼光来看可能有些写法可能过时,但是对于大家理解代码结构和基本的对敲思路还是有一定启发意义的:

做市策略的概念

做市策略(Market Making Strategy)指的是交易者(做市商)在市场上同时挂出买单和卖单,从而提供流动性以维持市场稳定。这种策略不仅帮助保持市场深度,而且为其他交易者提供交易对手。通过在不同价格区间提供买卖报价,做市商在价格波动中获取收益。

对于数字货币市场而言,做市商的角色至关重要,尤其是在交易量较小、波动性较大的市场中。通过提供流动性,做市商帮助减少市场的滑点,并为交易者提供更容易成交的价格。

传统做市策略的核心原理在于通过提供流动性来赚取买卖价差(Spread)。做市商挂出的买单价格低于卖单价格,通过成交价差获取利润。例如,当市场的现货价格上升时,做市商卖出价格更高、买入价格更低,从中赚取差价。主要收入来源包括:

  • 买卖价差(Spread):传统做市商通过挂单买卖,利用价格差异获取利润。
  • 交易量收益:做市商的收益与其提供的交易量密切相关。更多的交易量不仅意味着更高的成交频率和更多的利润机会,还能带来以下额外收益:
    • 手续费返还(Fee Rebate):许多交易所为鼓励做市商提供流动性,会给予一定比例的手续费返还,甚至提供负手续费(即交易所向做市商支付费用)
    • VIP等级优惠:当交易量达到特定阈值时,做市商可以获得更低的手续费率,降低交易成本
    • 做市商激励计划:部分交易所设有专门的做市商激励计划,根据提供的流动性质量给予额外奖励

然而,做市商也面临着市场波动的风险,特别是在数字货币市场这种高波动性的环境下。市场的剧烈波动可能导致做市商挂出的买卖单被大幅度偏离实际价格,从而产生亏损。

做市策略的类型

在数字货币市场中,做市商通常会根据市场状况、交易量、波动性等因素选择不同的做市策略。常见的做市策略类型包括:

  • 被动做市策略:做市商根据市场的深度、历史波动性等因素,挂出买卖单并等待市场成交。这种策略的特点是低频、稳健,做市商依赖市场波动来实现收益。

  • 主动做市策略:在这种策略下,做市商会根据市场行情实时调整买卖单的价格和数量,以提高成交概率。做市商通常会在价格接近当前市场价格时增加订单,借此更好地利用市场波动。

  • 刷量型对敲策略:本文重点介绍的策略类型。刷量型对敲策略是一种通过同价买卖来增加交易量的策略。与传统的做市策略不同,这种策略的主要目的不是赚取价差,而是通过大量交易获取交易所的返佣、等级优惠或流动性挖矿奖励。

在刷量型对敲策略中,做市商通过在相同价格挂出买单和卖单,当订单成交时,虽然不产生价差收益,但可以快速积累交易量。这种策略的盈利模式完全依赖于交易所的激励机制,而非市场套利。

关键特点:

  • 同价买卖:与传统做市策略不同,买入价和卖出价相同,不产生价差收益。

  • 交易量导向:策略的核心目标是快速积累交易量,而非价格套利。

  • 依赖激励机制:盈利完全依赖于交易所的返佣政策、VIP等级优惠或做市商激励计划。

重要区别说明: 与传统的做市策略相比,刷量型对敲策略并非通过提供真正的市场流动性来盈利,而是通过人为制造交易量来获取交易所的政策性奖励。这种策略在某些司法管辖区可能面临合规风险,实际应用时需要谨慎评估。

刷量型对敲策略的盈利逻辑说明

通过分析代码可以发现,本策略中的买入价和卖出价是完全一致的:

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

实际盈利逻辑

1. 交易量刷量策略 - 本策略的主要目的是通过大量交易增加交易量 - 获得交易所的返佣、等级优惠或流动性挖矿奖励 - 适用于有做市商激励计划的交易所

2. 手续费返还机制 - 依赖交易所的负手续费政策(Maker费率为负) - 通过提供流动性获得手续费返还 - 需要交易所支持做市商费率优惠

适用场景与风险

✅ 适用场景 - 交易所有明确的做市商优惠政策 - 适合交易量要求较高的VIP等级提升 - 存在流动性挖矿或返佣活动的平台

❌ 不适用场景 - 没有返佣机制的交易所 - 手续费率较高的平台 - 对刷量行为有明确限制的交易所

⚠️ 风险提醒 - 如果买卖订单同时成交,扣除手续费后通常是亏损的 - 如果交易所政策变化,策略可能失效 - 需要持续监控手续费成本 - 可能面临合规风险(部分地区对刷量行为有限制)

对敲策略架构分析

本文将参考子楠老师的代码框架,介绍一个简单的刷量型对敲策略实现,重点关注如何在交易所环境中通过同价买卖策略进行交易量积累。策略架构由两个主要类组成:MidClassMarketMaker。这两个类分别负责交易所的中间层交互和对敲策略的具体执行。

本策略架构采用了分层设计,通过将交易所接口和做市策略分离,确保系统具有良好的可扩展性和灵活性。架构中的主要组件包括:

  1. MidClass:交易所中间层,负责与交易所接口交互,获取市场数据、账户信息、订单状态等。这一层封装了与外部交易所的所有交互,确保交易逻辑和交易所的接口解耦。
  2. MarketMaker:做市策略类,负责执行对敲策略,生成挂单、检查订单状态、更新策略状态等。它与交易所中间层交互,提供具体的做市和对敲交易操作。

MidClass

MidClass 作为交易所中间层,主要职责是处理与交易所的交互,封装所有外部 API 调用,提供一个简洁的接口供 MarketMaker 使用。它的架构包括以下几个关键功能:

  1. 市场数据获取

    • 获取市场的实时数据,如市价、深度、K线等。它需要定期刷新市场数据,确保策略执行时数据是最新的。
  2. 账户信息管理

    • 获取账户信息,如账户余额、仓位情况等。这对于资金管理和风险控制至关重要。
  3. 订单管理

    • 提供接口创建、查询、撤销订单。这是执行做市策略的基础,确保能够在市场中生成并管理挂单。
  4. 数据存储与更新

    • 维护与交易所的连接,持续更新数据以供策略使用。

通过将这些功能封装在 MidClass 中,可以确保交易策略类(如 MarketMaker)专注于交易策略的执行,而无需关心如何与交易所进行交互。这种结构提升了系统的可维护性和可扩展性,便于后续增加对不同交易所的支持或优化现有功能。

MarketMaker

MarketMaker 是对敲策略的核心类,负责执行做市操作和对敲交易。其架构包括以下几个主要模块:

  1. 初始化

    • 初始化交易所中间层 MidClass,获取交易所的基本信息,如交易对、精度、市场深度等。
    • 初始化做市策略,设置必要的参数,如挂单数量、买卖价差等。这些参数将影响策略的执行方式和效果。
  2. 数据刷新

    • 定期刷新市场数据,包括实时的账户信息、市价、深度、K线等。这些数据为执行策略提供基础信息。
    • 刷新频率可以根据市场波动性进行调整,确保实时反应市场变化。
  3. 对敲策略执行

    • 挂单生成:根据市场深度和当前的价格波动,生成挂单字典。挂单字典包含买单和卖单的价格和数量,通常是根据策略参数自动计算出的。
    • 对敲交易执行:一旦挂单生成,MarketMaker 会将其提交到市场中,同时执行买单和卖单。目标是通过同价买卖来快速积累交易量。
    • 订单状态检查:在执行对敲交易时,MarketMaker 会不断检查订单的状态,确保挂出的订单能够被及时处理。如果订单未能成交,它会调整挂单价格或数量,直至订单完成。
  4. 状态更新

    • 策略状态更新:定期更新策略的状态,包括交易量、已完成的订单、费用等。通过这些状态信息,用户可以实时监控策略的表现。
    • 风险管理:对敲策略需要在不同市场环境下执行,MarketMaker 会动态调整策略执行方式,以适应不同市场环境。

对敲策略逻辑复现

对敲策略的实现依赖于精确的市场数据和快速的执行力。MarketMaker 通过监控市场的即时情况,利用对敲挂单(同价买卖)的方式,通过快速积累交易量来达到策略目标。

初始化

MarketMaker 类的初始化方法中,首先获取交易所的精度信息,并初始化策略参数,如交易量精度、价格精度等。

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

生成对敲挂单字典

对敲策略的核心是生成对敲挂单字典,包含买卖价格和数量。代码中通过计算中间价,生成对敲挂单字典。

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

执行对敲交易

根据生成的对敲挂单字典,执行对敲交易。代码中通过调用交易所中间层的 create_order 方法,同时挂出买单和卖单。

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']
        })

检查订单状态

定期检查订单状态,处理未完成的订单。代码中通过调用交易所中间层的 GetOrder 方法,获取订单状态,并根据订单状态决定是否取消订单,对敲订单的处理逻辑主要包括以下几个步骤:

  1. 获取订单状态

    • 通过交易所接口获取买单和卖单的状态。
    • 如果获取订单状态失败(例如订单不存在或网络问题),则取消订单并从记录中移除。
  2. 订单状态判断

    • 根据订单状态,判断订单是否完成、部分完成或未完成。
    • 订单状态可能包括:
      • 0(ORDER_STATE_PENDING):未完成(挂单中)。
      • 1(ORDER_STATE_CLOSED):已完成(完全成交)。
      • 2(ORDER_STATE_CANCELED):已撤销。
      • 3(ORDER_STATE_UNKNOWN):状态未知。
  3. 订单状态处理

    • 两订单都未完成
      • 如果买单和卖单都未完成(状态为 0),则根据轮询次数(current_time % 5 == 0)决定是否取消订单。
      • 取消订单后,更新挂单次数,并从记录中移除订单。
    • 一个订单完成,另一个未完成
      • 如果一个订单已完成(状态为 1),另一个订单未完成(状态为 0),则根据轮询次数决定是否取消未完成的订单。
      • 取消未完成的订单后,更新交易量和未完成订单列表,并从记录中移除订单。
    • 两订单都已完成
      • 如果买单和卖单都已完成(状态为 1),则更新交易量,并从记录中移除订单。
    • 订单状态未知
      • 如果订单状态既不是 0 也不是 1,则记录为未知状态,并进行日志记录。
  4. 更新记录

    • 根据订单状态处理的结果,更新交易量、未完成订单列表和挂单次数。

后续策略展望

本文展示的刷量型对敲策略主要用于学习交易框架的架构设计,实际应用价值有限。如果读者对真正的做市策略感兴趣,后续我们会推出更具实用价值的策略内容:

1. 盘口做市策略 - 基于买卖价差的真正套利策略 - 在买一价和卖一价之间挂单,赚取价差收益 - 更符合传统做市商的盈利模式

2. 动态做市策略 - 根据市场波动率动态调整挂单价格 - 引入库存管理和风险控制机制 - 适应不同市场环境的自适应做市

3. 多层级做市策略 - 在多个价格层级同时挂单 - 通过分散风险提高整体收益稳定性 - 更接近专业做市商的实际操作

这些策略将更注重实际的盈利逻辑和风险管理,为量化交易者提供更有价值的参考。

策略展望

刷量型对敲策略依赖于交易所的激励政策,如果政策发生变化,可能会导致策略失效。因此,策略需要具备应对政策变化的能力,例如通过动态监控手续费率或引入多重盈利模式来降低单一依赖的风险。此外,刷量型对敲策略可能被视为操纵市场行为,面临监管风险。在实际应用中,交易者需要密切关注相关法律法规,确保策略的合规性,避免因监管问题导致损失。

希望读者在理解基本框架的基础上,能够根据自己的交易理念和市场理解,对策略进行进一步的优化和完善。量化交易的魅力在于不断地学习、实践和改进,祝大家在量化交易的道路上不断进步!

策略代码

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)
相关推荐