avatar of ianzeng123 ianzeng123
집중하다 사신
2
집중하다
319
수행원

디지털 화폐 시장 조성 전략에 대한 간략한 논의: 노크온 전략의 아키텍처 설계 및 FMZ 플랫폼 구현

만든 날짜: 2025-07-24 11:03:59, 업데이트 날짜: 2025-07-28 13:06:13
comments   0
hits   622

⚠️ 중요 공지

이 글에서는 거래 프레임워크를 배우기 위한 거래량 증대 전략을 제시합니다. 이는 기존의 차익거래 시장 조성 전략과는 근본적으로 다릅니다. 이 전략의 주요 목적은 가격 차익거래를 통해 수익을 얻는 것이 아니라, 동일 가격에 매수 및 매도를 통해 거래량을 늘리고 거래소 리베이트 또는 레벨 할인을 받는 것입니다.

본 문서에 제공된 코드는 참고용 프레임워크일 뿐이며, 실시간 운용 경험이 없습니다. 본 문서의 전략 구현은 기술적 학습 및 연구 목적으로만 사용되었으며, 실제 시장 환경에서 완전히 검증되지 않았습니다. 본 문서의 내용을 참조할 때는 충분한 백테스팅 검증 및 위험 평가를 수행해야 하며, 실시간 거래에 직접 사용해서는 안 됩니다.


디지털 통화 시장에서 마켓 메이킹 전략은 시장 유동성을 개선하고 거래를 촉진하는 도구일 뿐만 아니라, 다양한 퀀트 거래 전략의 핵심 요소입니다. 마켓 메이킹 전략은 매수 및 매도 호가를 게시하고 유동성을 공급함으로써 다양한 시장 환경에서 수익을 창출합니다. 전문 마켓 메이킹 전략의 코드 구현은 고주파 지연 최적화, 복잡한 위험 관리 시스템, 다중 거래소 차익거래와 같은 고급 기능을 포함하여 매우 복잡한 경우가 많습니다. 이번 시간에는 브러시 볼륨 역매매 전략의 기본 개념과 Inventor Quantitative(FMZ) 플랫폼에서 간소화된 학습 프레임워크를 구현하는 방법을 살펴보겠습니다.

이 글의 본문은 원저자 Zinan의 “마켓 메이킹 전략의 아이디어와 작성법”에서 발췌했습니다. 일부 내용은 fmz 플랫폼에 최적화되어 재작성되었습니다. 오늘날의 관점에서 보면 일부 작성법은 시대에 뒤떨어질 수 있지만, 역매매의 코드 구조와 기본 개념을 이해하는 것은 여전히 모두에게 영감을 줄 것입니다.

시장 조성 전략의 개념

마켓 메이킹 전략은 트레이더(마켓 메이커)가 시장에 매수 및 매도 주문을 동시에 제출하여 시장 안정성을 유지하는 유동성을 확보하는 것을 의미합니다. 이 전략은 시장 심도 유지에 도움이 될 뿐만 아니라 다른 트레이더에게 거래 상대방을 제공합니다. 마켓 메이커는 다양한 가격대의 매수 및 매도 호가를 제공함으로써 가격 변동을 통해 수익을 창출합니다.

암호화폐 시장, 특히 거래량이 적고 변동성이 높은 시장에서 마켓메이커의 역할은 매우 중요합니다. 마켓메이커는 유동성을 제공함으로써 시장 슬리피지(slippage)를 줄이고 트레이더에게 더 쉬운 가격 거래를 제공합니다.

전통적인 시장 조성 전략의 핵심 원칙은 유동성을 공급하여 매수-매도 스프레드를 확보하는 것입니다. 시장 조성자가 제시하는 매수 주문 가격은 매도 주문 가격보다 낮으며, 거래 스프레드를 통해 수익을 얻습니다. 예를 들어, 시장의 현물 가격이 상승하면 시장 조성자는 더 높은 가격에 매도하고 더 낮은 가격에 매수하여 스프레드를 확보합니다. 주요 수입원은 다음과 같습니다.

  • 확산:전통적인 시장 조성자는 매수 및 매도 주문을 내고 가격 차이를 이용하여 수익을 냅니다.
  • 거래량 수익:마켓메이커의 수익은 거래량과 밀접한 관련이 있습니다. 거래량이 많을수록 거래 빈도가 높아지고 수익 기회가 늘어날 뿐만 아니라 다음과 같은 추가적인 이점도 있습니다.
    • 수수료 할인:마켓메이커가 유동성을 제공하도록 장려하기 위해 많은 거래소는 일정 비율의 수수료를 환불하거나 심지어 마이너스 수수료를 제공하기도 합니다(즉, 거래소가 마켓메이커에게 수수료를 지불합니다).
    • 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. 수수료 환불 메커니즘

  • 거래소의 마이너스 수수료 정책에 따라 다릅니다. (메이커 수수료는 마이너스입니다.)
  • 유동성을 제공하여 수수료 할인을 받으세요
  • 우선 시장 조성자 요금을 지원하기 위한 거래소가 필요합니다.

적용 가능한 시나리오 및 위험

✅ 적용 가능한 시나리오

  • 거래소는 시장 조성자를 위한 명확한 우대 정책을 가지고 있습니다.
  • 더 높은 거래량 요구 사항이 있는 VIP 레벨 업그레이드에 적합
  • 유동성 채굴 또는 리베이트 활동이 있는 플랫폼

❌ 해당 없음

  • 리베이트 메커니즘이 없는 거래소
  • 거래 수수료가 높은 플랫폼
  • 세탁거래에 대한 명확한 제한이 있는 거래소

⚠️ 위험 알림

  • 매수 주문과 매도 주문이 동시에 실행되면 일반적으로 처리 수수료를 차감한 후 손실이 발생합니다.
  • 교환 정책이 변경되면 전략이 무효화될 수 있습니다.
  • 수수료 비용에 대한 지속적인 모니터링이 필요합니다.
  • 규정 준수 위험에 직면할 수 있음(일부 지역에서는 칫솔질 행동에 제한이 있음)

노크온 전략 프레임워크 분석

이 글에서는 지나 씨의 코드 프레임워크를 참고하여 거래량 증대 전략의 간단한 구현을 소개하고, 거래소 환경에서 동일 가격 매수 및 매도 전략을 통해 거래량을 축적하는 방법에 중점을 둡니다. 전략 프레임워크는 두 가지 주요 클래스로 구성됩니다.MidClass 그리고 MarketMaker이 두 계층은 거래소의 중간 계층 상호작용과 연쇄 전략의 구체적인 실행을 담당합니다.

이 전략 아키텍처는 거래소 인터페이스와 마켓 메이킹 전략을 분리하는 계층적 설계를 채택하여 시스템의 확장성과 유연성을 보장합니다. 아키텍처의 주요 구성 요소는 다음과 같습니다.

  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)