리소스 로딩... 로딩...

C++로 작성된 고주파 거래 전략

저자:선함, 2020-05-22 15:28:11, 업데이트: 2023-11-02 19:54:28

img

페니 점프 C++로 작성된 고주파 거래 전략

요약

시장은 전투 현장이며, 구매자와 판매자는 항상 게임에 있으며, 이는 또한 거래 비즈니스의 영원한 주제입니다. 오늘날 공유되는 페니 점프 전략은 고주파 전략 중 하나이며, 원래 은행 간 외환 시장에서 파생되었으며 주류 팩트 통화 쌍에서 자주 사용됩니다.

고주파 전략 분류

고주파 거래에는 두 가지 주요 유형의 전략이 있습니다. 구매자 측면 전략과 판매자 측면 전략. 판매자 측면 전략은 일반적으로 시장 제작 전략이며, 이 두 측면 전략은 반대입니다. 예를 들어, 고주파 중재 구매자 전략은 시장의 모든 불합리한 현상을 가장 빠른 속도로 부드럽게하고, 가격을 신속하게 공격하거나 다른 시장 제작자의 잘못된 가격을 먹는 것입니다.

또한 역사적인 데이터 또는 시장의 주문 규칙을 분석하고, 부득이한 가격으로 대기 주문을 사전에 보내고, 시장 가격의 급격한 변화와 함께 철수 명령을 보내는 방법이 있습니다. 이러한 전략은 수동 시장 제작에서 일반적입니다. 대기 주문이 실행되면 특정 이익 또는 스톱 로스 조건에 도달한 후 포지션은 닫힐 것입니다. 수동 시장 제작 전략은 일반적으로 너무 많은 속도를 필요로하지 않지만 강력한 전략 논리와 구조가 필요합니다.

페니 점프 전략은 무엇일까요?

페니 점프는 영어로 번역하면 마이크로 가격 상승의 의미입니다. 원칙은 시장의 구매 가격과 판매 가격을 추적하는 것입니다. 그러면 시장 가격에 따라, 추적 가격의 마이크로 가격 상승을 더하거나 빼면 이것은 수동적인 거래 전략이며 판매자 측 시장 제작 전략에 속한다는 것이 분명합니다. 비즈니스 모델과 논리는 유동성을 제공하기 위해 거래소 상장 한도 주문에 대한 양자 거래를 수행하는 것입니다.

시장 제작 전략은 손에 일정 양의 재고를 요구하고 구매자 및 판매자 양쪽에서 거래합니다. 이 전략의 주요 수입은 거래소가 제공하는 수수료 수익이며, 낮은 가격과 높은 가격으로 구매하여 얻은 가격 차이입니다. 그러나 시장 제작을 원하는 많은 고주파 거래자에게 입찰-구매 스프레드를 얻는 것은 좋은 일이지만 절대적인 수익 수단은 아닙니다.

페니 점프 전략 원칙

우리는 거래 시장에 많은 소매 투자자가 있고, 또한 많은 대형 투자자가 있다는 것을 알고 있습니다. 예를 들어: 핫 머니, 공공 자금, 민간 자금 등. 소매 투자자는 일반적으로 더 적은 자금을 보유하고 있으며, 그들의 주문은 시장에 매우 작은 영향을 미치며, 언제든지 거래 목표를 쉽게 사고 팔 수 있습니다. 그러나 큰 자금이 시장에 참여하는 것은 그렇게 간단하지 않습니다.

큰 투자자가 500 롯의 원유를 구입하고 싶다면, 판매를 위해 현재 가격에 많은 주문이 없으며, 투자자는 더 높은 가격으로 구입하기를 원하지 않습니다. 현재 가격에 구매 주문을 보내기를 주장하면, 미끄러짐 포인트의 비용은 너무 높습니다. 따라서 원하는 가격에 미뤄있는 주문을 보내야합니다. 시장의 모든 참가자는 특정 가격에 표시되는 높은 구매 주문을 볼 것입니다.

이 거대한 주문 때문에 시장에서 어색하게 보입니다. 때때로 우리는 그것을 "코끼리 주문"이라고 부릅니다. 예를 들어, 현재 시장은 다음과 같습니다.

Selling Price 400.3, Order volume 50; buying price 400.1, Order volume 10. 

갑자기 이 번거로운 코끼리가 시장에 뛰어들었고, 입찰 가격은 400.1의 가격으로 보내졌습니다. 이 시점에서 시장은:

Selling Price 400.3, Order volume 50; Buying price 400.1, Order volume 510.

트레이더들은 모두 특정 가격에 대기 중인 주문이 엄청나게 많으면 이 가격이 강력한 지원 (또는 저항) 을 형성할 것이라는 것을 알고 있습니다. 또한, 고주파 트레이더들은 또한, 만약 그들이 주문책 깊이에서 Buying 1 가격 이상의 구매 주문을 보내면 시장은 다음과 같습니다.

Selling Price 400.3, Order volume 50; Buying price 400.2, Order volume 1,

가격 400.1은 주문책 깊이에서 구입 2 가격으로 변합니다. 그러면 가격이 400.3로 상승하면 고주파 트레이더는 0.1의 이익을 얻을 것입니다.

가격 상승이 일어나지 않더라도, 2를 구매하는 위치에서는 여전히 코끼리가 가격을 잡고 있으며, 그것은 빠르게 코끼리 400.1 가격으로 다시 판매 될 수 있습니다. 이것은 페니 점프 전략의 일반적인 아이디어입니다. 그것의 논리는 이렇게 간단합니다. 시장 주문 상태를 모니터링하여 상대방의 의도를 추측하고, 우호적인 위치를 구축하는 데 주도권을 잡고, 마침내 짧은 시간 내에 작은 스프레드를 통해 이익을 얻습니다. 이 코끼리에 대해, 그는 시장에서 엄청난 양의 구매 주문을 매달기 때문에, 그는 자신의 거래 의도를 노출하고, 자연스럽게 고주파 거래자의 사냥 대상이되었습니다.

페니 점프 전략의 실행

먼저, 시장의 매우 낮은 확률로 거래 기회를 관찰하고, 거래 논리에 따라 대응 전략을 세워야 합니다. 논리가 복잡하다면, 기존의 수학적 지식을 사용하여 모형을 사용하여 비합리 현상의 본질을 가능한 한 설명하고, 과잉 적합성을 최소화해야 합니다. 또한, 이것은 Price first then Volume first 원칙을 충족할 수 있는 백테스트 엔진에 의해 검증되어야 합니다. 다행히도, 우리는 현재이 백테스팅 모드를 지원하는 FMZ 퀀트 플랫폼을 가지고 있습니다.

값 먼저 다음 볼륨 먼저 백테스트 엔진을 지원하는 의미는 무엇입니까? 400.1에서 구매하기 위해 대기 주문을 보내면 주문 책 깊이의 판매 가격이 400.1 또는 그 이하일 때만 대기 주문이 종료 될 수 있습니다. 대기 주문의 가격 데이터를 계산하고 대기 주문의 볼륨 데이터를 계산하지 않으며 교환 주문 일치 규칙에서 가격 우선 순위를 충족합니다.

자본 먼저가격 먼저의 업그레이드 된 버전이다. 가격 우선 순위와 시간 우선 순위 모두다. 이 매칭 모드는 교환 모델과 정확히 동일하다고 말할 수 있습니다. 미결 주문 금액을 계산함으로써 현재 미결 주문이 실제 시장 환경의 실제 시뮬레이션을 달성하기 위해 양량 거래를 실현하기 위해 수동 거래 조건에 도달하는지 여부를 판단합니다.

또한 일부 독자들은 페니 점프 전략은 시장 거래 기회를 필요로한다는 것을 발견할 수 있습니다. 즉, 시장의 필요성은 적어도 두 개의 hop 가격 격차를 가지고 있습니다. 정상적인 상황에서는 상품 선물의 주요 거래 계약이 상대적으로 바쁘습니다. 1 을 구매하고 1 을 판매하는 차이점은 거래 기회가 거의 없다는 것입니다. 따라서 거래가 너무 활성화되지 않는 하위 주요 계약에 에너지를 투자합니다. 이 유형의 거래 계약은 때때로 두 개 또는 세 개의 hop 기회를 가지고 있습니다. 예를 들어 MA (중국 상품 선물에서 메탄올 코드) 1909 계약에서 다음과 같은 상황이 발생합니다.

img

1 가격 2225을 551량으로 팔고, 1 가격 2223을 565량으로 구매하고, 몇 초 동안 아래로 쳐다보세요. 이 일이 일어난 후, 몇 번의 틱 후에 사라집니다. 이 경우, 우리는 시장을 자기 교정으로 간주합니다. 우리가 해야 할 일은 따라잡는 것입니다. 시장이 적극적으로 수정하기 전에. 우리가 수동으로 하면 불가능하지만 자동 거래의 도움으로 가능 할 수 있습니다.

두 개의 hop 가격 격차가 나타나는 상황은 매우 자주 발생하지만, 세 개의 hop는 가장 안전하지만, 세 개의 hop는 거의 발생하지 않아 거래 빈도가 너무 낮습니다.

다음으로, 우리는 이전 Selling 1 Buying 1Buying 1 Selling 1 사이의 차이를 관찰합니다. 시장 사이의 가격 격차를 메우기 위해, 속도가 충분히 빠르면 다른 주문의 최전선에 배치 될 수 있습니다. 또한, 포지션 보유 시간은 매우 짧으며, 이러한 거래 논리로, 전략을 실현 한 후, MA909을 예로 들 수 있습니다. 실제 시장 테스트는 CTP 인터페이스 대신 Esunny를 권장합니다. Esunny의 위치 및 펀드 상황 변경 메커니즘은 푸시 데이터로 고주파 거래에 매우 적합합니다.

전략 코드

거래 논리를 정리 한 후, 우리는 그것을 달성하기 위해 코드를 사용할 수 있습니다. FMZ 퀀트 플랫폼이 C ++ 작성 전략을 사용하는 예가 너무 적기 때문에, 여기 우리는 C ++를 사용하여 모든 사람이 배우기 편리한 이 전략을 작성합니다.fmz.com> 로그인 > 대시보드 > 전략 라이브러리 > 새로운 전략 > 왼쪽 상단에있는 드롭다운 메뉴를 클릭 > 전략을 작성하기 위해 C ++를 선택하십시오. 아래 코드의 댓글에 주의하십시오.

  • 단계 1: 먼저 HFT 클래스와 메인 함수가 정의된 전략의 프레임워크를 구축한다. 메인 함수의 첫 번째 줄은 로그를 클리어하는 것이다. 이의 목적은 전략이 다시 시작될 때마다 이전에 실행된 로그 정보를 클리어하는 것이다. 두 번째 줄은 네트워크 지연과 일부 팁과 같은 불필요한 오류 메시지를 필터링하여 로그가 중요한 정보를 기록하고 더 깔끔하게 보이도록 하는 것이다. 세 번째 줄은 프로그램 시작을 의미하는 Init OK 메시지를 인쇄하는 것이다. 네 번째 줄은 HFT 클래스에 따라 객체를 생성하고 객체 이름은 hft이다. 다섯 번째 줄의 프로그램은 while 루프에 들어가 hft 메소드에서 항상 루프를 실행한다. 루프 메소드가 이 프로그램의 논리라는 것을 볼 수 있다. 다른 증거는 프린트 메소드이다. 프로그램 실행이 종료된 경우 프로그램 실행이 정상하지 않을 것이다.

다음으로, 다섯 가지 방법이 있는 HFT 클래스를 살펴보자. 첫 번째 방법은 구성 방법이다; 두 번째 방법은 새로운 K 라인인지 여부를 결정하기 위해 현재 주일을 얻는 방법이다; 세 번째 방법은 주로 모든 채우지 않은 오더를 취소하고 자세한 위치 정보를 얻는 방법이다. 왜냐하면 오더가 배치되기 전에 먼저 현재 위치 상태를 결정해야 하기 때문이다. 네 번째 방법은 주로 일부 정보를 인쇄하는 데 사용됩니다. 이 전략에서는 이 방법이 주요한 방법이 아닙니다. 가장 중요한 것은 다섯 번째 방법입니다. 이 방법은 주로 거래 논리와 오더를 배치하는 데 책임이 있습니다.

/ / Define the HFT class
Class HFT {
     Public:
         HFT() {
             // Constructor
         }
        
         Int getTradingWeekDay() {
             // Get the current day of the week to determine if it is a new K line
         }
        
         State getState() {
             / / Get order data
         }

         Void stop() {
             // Print orders and positions
         }
        
         Bool Loop() {
             // Strategy logic and placing orders
         }
};

// main function
Void main() {
     LogReset(); // clear the log
     SetErrorFilter("ready|timeout"); // Filter error messages
     Log("Init OK"); // Print the log
     HFT hft; // Create HFT object
     While (hft.Loop()); // enter loop
     Log("Exit"); // Program exits, prints the log
}

그래서 이 HFT 클래스의 각 방법들이 어떻게 구현되는지, 그리고 가장 핵심적인 루프 방법이 어떻게 작동하는지를 살펴보자. 위에서 아래로, 각 방법의 구체적인 구현을 하나씩 구현할 것이고, 원래의 고주파 전략이 매우 간단하다는 것을 알게 될 것이다. HFT 클래스에 대해 이야기하기 전에, 우리는 먼저 hft 객체 계산 결과를 저장하기 위한 몇 가지 글로벌 변수를 정의했다. 그들은: 주문 상태를 저장, 위치 상태를, 긴 위치를 보유, 짧은 위치를 보유, 구매 가격, 구매 양, 판매 가격, 판매 양입니다. 아래 코드를 참조하십시오:

/ / Define the global enumeration type State
Enum State {
     STATE_NA, // store order status
     STATE_IDLE, // store position status
     STATE_HOLD_LONG, // store long position directions
     STATE_HOLD_SHORT, // store short position direction
};

/ / Define global floating point type variable
Typedef struct {
     Double bidPrice; // store the buying price
     Double bidAmount; // store the buying amount
     Double askPrice; // store the selling price
     Double askAmount; // store the selling amount
} Book;

위의 글로벌 변수들을 사용하면 hft 객체에 의해 계산된 결과를 별도로 저장할 수 있는데, 이는 프로그램에서 후속 호출에 편리하다. 다음으로 우리는 HFT 클래스의 각 메소드의 구체적인 구현에 대해 이야기할 것이다. 첫째, 첫 번째 HFT 메소드는 두 번째 getTradingWeekDay 메소드를 호출하고 결과를 로그에 프린트하는 컨스트럭터이다. 두 번째 getTradingWeekDay 메소드는 새로운 K 라인인지 여부를 결정하기 위해 현재 주일을 얻는다. 또한 구현하는 것은 매우 간단하다. 타임 스탬프를 얻고, 시간과 주를 계산하고, 마지막으로 주 수를 반환한다. 세 번째 getState 메소드는 조금 길어, 나는 단지 일반적인 아이디어를 설명할 것이다. 구체적인 설명에 대해서는 다음 코딩 블록의 댓글을 살펴볼 수 있다.

다음으로, 먼저 모든 순서를 받아서, 결과를 정상 배열로 반환하고, 그 다음이 배열을 가로질러, 하나씩 순서를 취소하고, 위치 데이터를 얻고, 배열을 반환하고, 그 다음이 배열을 가로질러, 방향, 위치, 어제 또는 현재 위치 등을 포함한 자세한 위치 정보를 얻고, 마지막으로 결과를 반환합니다. 네 번째 정지 방법은 정보를 인쇄하는 것입니다. 코드는 다음과 같습니다:

Public:
    // Constructor
    HFT() {
        _tradingDay = getTradingWeekDay();
        Log("current trading weekday", _tradingDay);
    }
    
    // Get the current day of the week to determine if it is a new K line
    Int getTradingWeekDay() {
        Int seconds = Unix() + 28800; // get the timestamp
        Int hour = (seconds/3600)%24; // hour
        Int weekDay = (seconds/(60*60*24))%7+4; // week
        If (hour > 20) {
            weekDay += 1;
        }
        Return weekDay;
    }
    
    / / Get order data
    State getState() {
        Auto orders = exchange.GetOrders(); // Get all orders
        If (!orders.Valid || orders.size() == 2) { // If there is no order or the length of the order data is equal to 2
            Return STATE_NA;
        }
        
        Bool foundCover = false; // Temporary variable used to control the cancellation of all unexecuted orders
        // Traverse the order array and cancel all unexecuted orders
        For (auto &order : orders) {
            If (order.Id == _coverId) {
                If ((order.Type == ORDER_TYPE_BUY && order.Price < _book.bidPrice - _toleratePrice) ||
                    (order.Type == ORDER_TYPE_SELL && order.Price > _book.askPrice + _toleratePrice)) {
                    exchange.CancelOrder(order.Id, "Cancel Cover Order"); // Cancel order based on order ID
                    _countCancel++;
                    _countRetry++;
                } else {
                    foundCover = true;
                }
            } else {
                exchange.CancelOrder(order.Id); // Cancel order based on order ID
                _countCancel++;
            }
        }
        If (foundCover) {
            Return STATE_NA;
        }
        
        // Get position data
        Auto positions = exchange.GetPosition(); // Get position data
        If (!positions.Valid) { // if the position data is empty
            Return STATE_NA;
        }

        // Traverse the position array to get specific position information
        For (auto &pos : positions) {
            If (pos.ContractType == Symbol) {
                _holdPrice = pos.Price;
                _holdAmount = pos.Amount;
                _holdType = pos.Type;
                Return pos.Type == PD_LONG || pos.Type == PD_LONG_YD ? STATE_HOLD_LONG : STATE_HOLD_SHORT;
            }
        }
        Return STATE_IDLE;
    }
    
    // Print orders and positions information
    Void stop() {
        Log(exchange.GetOrders()); // print order
        Log(exchange.GetPosition()); // Print position
        Log("Stop");
    }

마지막으로, 우리는 루프 함수가 전략 논리와 순서를 제어하는 방법에 초점을 맞추고 있습니다. 더 자세히보고 싶다면 코드의 댓글을 참조 할 수 있습니다. 먼저 CTP 거래와 시장 서버가 연결되어 있는지 여부를 결정하십시오. 다음으로 계좌의 사용 가능한 잔액을 얻고 주 수를 얻으십시오. 다음으로 FMZ 공식 SetQuantContractType 함수를 호출하여 거래 할 품종 코드를 설정하고 거래 품종의 세부 정보를 반환하기 위해 이 기능을 사용할 수 있습니다. 다음으로 GetDepth 함수를 호출하여 현재 시장의 깊이 데이터를 얻을 수 있습니다. 깊이 데이터는 포함합니다: 구매 가격, 구매량, 판매 가격, 판매량 등, 그리고 우리는 나중에 사용할 것이기 때문에 변수와 함께 저장합니다. 다음으로이 데이터를 상태 표시줄에 출력하여 사용자가 현재 시장 상태를 쉽게 볼 수 있습니다. 코드는 다음과 같습니다:

// Strategy logic and placing order
Bool Loop() {
    If (exchange.IO("status") == 0) { // If the CTP and the quote server are connected
        LogStatus(_D(), "Server not connect ...."); // Print information to the status bar
        Sleep(1000); // Sleep 1 second
        Return true;
    }
    
    If (_initBalance == 0) {
        _initBalance = _C(exchange.GetAccount).Balance; // Get account balance
    }
    
    Auto day = getTradingWeekDay(); // Get the number of weeks
    If (day != _tradingDay) {
        _tradingDay = day;
        _countCancel = 0;
    }
    
    // Set the futures contract type and get the contract specific information
    If (_ct.is_null()) {
        Log(_D(), "subscribe", Symbol); // Print the log
        _ct = exchange.SetContractType(Symbol); // Set futures contract type
        If (!_ct.is_null()) {
            Auto obj = _ct["Commodity"]["CommodityTickSize"];
            Int volumeMultiple = 1;
            If (obj.is_null()) { // CTP
                Obj = _ct["PriceTick"];
                volumeMultiple = _ct["VolumeMultiple"];
                _exchangeId = _ct["ExchangeID"];
            } else { // Esunny
                volumeMultiple = _ct["Commodity"]["ContractSize"];
                _exchangeId = _ct["Commodity"]["ExchangeNo"];
            }
            If (obj.is_null() || obj <= 0) {
                Panic("PriceTick not found");
            }
            If (_priceTick < 1) {
                exchange.SetPrecision(1, 0); // Set the decimal precision of the price and the quantity of the order.
            }
            _priceTick = double(obj);
            _toleratePrice = _priceTick * TolerateTick;
            _ins = _ct["InstrumentID"];
            Log(_ins, _exchangeId, "PriceTick:", _priceTick, "VolumeMultiple:", volumeMultiple); // print the log
        }
        Sleep(1000); // Sleep 1 second
        Return true;
    }
    
    // Check orders and positions to set status
    Auto depth = exchange.GetDepth(); // Get depth data
    If (!depth.Valid) { // if no depth data is obtained
        LogStatus(_D(), "Market not ready"); // Print status information
        Sleep(1000); // Sleep 1 second
        Return true;
    }
    _countTick++;
    _preBook = _book;
    _book.bidPrice = depth.Bids[0].Price; // "Buying 1" price
    _book.bidAmount = depth.Bids[0].Amount; // "Buying 1" amount
    _book.askPrice = depth.Asks[0].Price; // "Selling 1" price
    _book.askAmount = depth.Asks[0].Amount; // "Selling 1" amount
    // Determine the state of the port data assignment
    If (_preBook.bidAmount == 0) {
        Return true;
    }
    Auto st = getState(); // get the order data
    
    // Print the port data to the status bar
    LogStatus(_D(), _ins, "State:", st,
                "Ask:", depth.Asks[0].Price, depth.Asks[0].Amount,
                "Bid:", depth.Bids[0].Price, depth.Bids[0].Amount,
                "Cancel:", _countCancel,
                "Tick:", _countTick);
}

그렇게 많은 일을 한 후, 우리는 마침내 주문을 할 수 있습니다. 거래 전에, 먼저 우리는 프로그램의 현재 보유 위치 상태를 판단합니다 (유지 위치, 긴 위치 명령, 짧은 위치 명령), 여기 우리는 if...else if...else if 로직 컨트롤을 사용했습니다. 그들은 매우 간단합니다. 만약 보유 위치가 없다면, 로직 조건에 따라 포지션이 열릴 것입니다. 만약 보유 위치가 있다면, 로직 조건에 따라 포지션이 닫힐 것입니다. 모든 사람의 이해를 돕기 위해, 우리는 논리를 설명하기 위해 세 개의 문장을 사용합니다.

먼저 볼변수를 선언하고, 우리는 폐쇄 포지션을 제어하기 위해 그것을 사용합니다. 다음으로 우리는 현금 계좌 정보를 얻고, 이익 가치를 기록해야 합니다. 그 다음 인출 주문의 상태를 결정하고, 인출 수가 설정된 최대치를 초과하면 관련 정보를 로그에 인쇄합니다.

다음으로, 우리는 구매 1 가격과 판매 1 가격을 얻습니다. 이전 구매 가격이 현재 구매 가격보다 높고 현재 판매 부피가 구매 부피보다 적으면, 그것은 구매 1 가격이 사라진다는 것을 의미합니다. 긴 포지션 개척 가격과 주문 양이 설정됩니다. 그렇지 않으면 이전 판매 가격이 현재 판매 가격보다 낮고 현재 구매 부피가 더 적으면 판매 부피는 판매 1 가격이 사라진다는 것을 증명합니다. 짧은 포지션 개척 가격과 주문 양이 설정됩니다. 결국, 긴 포지션과 짧은 포지션 주문이 동시에 시장에 진출합니다. 구체적인 코드는 다음과 같습니다.

Bool forceCover = _countRetry >= _retryMax; // Boolean value used to control the number of closings
If (st == STATE_IDLE) { // if there is no holding position
    If (_holdAmount > 0) {
        If (_countRetry > 0) {
            _countLoss++; // failure count
        } else {
            _countWin++; // success count
        }
        Auto account = exchange.GetAccount(); // Get account information
        If (account.Valid) { // If get account information
            LogProfit(_N(account.Balance+account.FrozenBalance-_initBalance, 2), "Win:", _countWin, "Loss:", _countLoss); // Record profit value
        }
    }
    _countRetry = 0;
    _holdAmount = 0;
    
    // Judging the status of withdrawal
    If (_countCancel > _cancelMax) {
        Log("Cancel Exceed", _countCancel); // Print the log
        Return false;
    }

    Bool canDo = false; // temporary variable
    If (abs(_book.bidPrice - _book.askPrice) > _priceTick * 1) { // If there is more than 2 hops between the current bid and ask price
        canDo = true;
    }
    If (!canDo) {
        Return true;
    }
    
    Auto bidPrice = depth.Bids[0].Price; // Buying 1 price
    Auto askPrice = depth.Asks[0].Price; // Selling 1 price
    Auto bidAmount = 1.0;
    Auto askAmount = 1.0;
    
    If (_preBook.bidPrice > _book.bidPrice && _book.askAmount < _book.bidAmount) { // If the previous buying price is greater than the current buying price and the current selling volume is less than the buying volume
        bidPrice += _priceTick; // Set the opening long position price
        bidAmount = 2; // set the opening long position volume
    } else if (_preBook.askPrice < _book.askPrice && _book.bidAmount < _book.askAmount) { // If the previous selling price is less than the current selling price and the current buying volume is less than the selling volume
        askPrice -= _priceTick; // set the opening short position volume
        askAmount = 2; // set the opening short position volume
    } else {
        Return true;
    }
    Log(_book.bidPrice, _book.bidAmount, _book.askPrice, _book.askAmount); // Print current market data
    exchange.SetDirection("buy"); // Set the order type to buying long
    exchange.Buy(bidPrice, bidAmount); // buying long and open position
    exchange.SetDirection("sell"); // Set the order type to selling short
    exchange.Sell(askPrice, askAmount); // short sell and open position
}

다음으로, 우리는 긴 포지션을 닫는 방법에 대해 이야기 할 것입니다. 먼저 현재 포지션 상태에 따라 주문 유형을 설정하고, 그 다음 판매 1 가격을 얻습니다. 현재 판매 1 가격이 구매 긴 오픈 가격보다 크다면, 폐쇄 긴 포지션 가격을 설정합니다. 현재 판매 1 가격이 긴 포지션 오픈 가격보다 작다면, 닫는 양 변수를 true로 재설정하고, 모든 긴 포지션을 닫습니다. 코딩 부분은 아래와 같습니다:

Else if (st == STATE_HOLD_LONG) { // if holding long position
     exchange.SetDirection((_holdType == PD_LONG && _exchangeId == "SHFE") ? "closebuy_today" : "closebuy"); // Set the order type, and close position
     Auto sellPrice = depth.Asks[0].Price; // Get "Selling 1" price
     If (sellPrice > _holdPrice) { // If the current "selling 1" price is greater than the long position opening price
         Log(_holdPrice, "Hit #ff0000"); // Print long position opening price 
         sellPrice = _holdPrice + ProfitTick; // Set closing long position price
     } else if (sellPrice < _holdPrice) { // If the current "selling 1" price is less than the long position opening price
         forceCover = true;
     }
     If (forceCover) {
         Log("StopLoss");
     }
     _coverId = exchange.Sell(forceCover ? depth.Bids[0].Price : sellPrice, _holdAmount); // close long position
     If (!_coverId.Valid) {
         Return false;
     }
}

마지막으로, 짧은 포지션을 닫는 방법을 보자. 원칙은 위에서 언급한 긴 포지션을 닫는 것과 반대입니다. 먼저, 현재 포지션 상태에 따라 주문 유형을 설정하고, 다음으로 현재 구매 1 가격을 얻으십시오. 현재 구매 1 가격이 짧은 포지션 개척 가격보다 작으면 폐쇄 짧은 포지션 가격이 설정됩니다. 현재 구매 1 가격이 개척 짧은 포지션 가격보다 크면 true로 종료 양 변수를 재설정하고 모든 짧은 포지션을 닫습니다.

Else if (st == STATE_HOLD_SHORT) { // if holding short position
     exchange.SetDirection((_holdType == PD_SHORT && _exchangeId == "SHFE") ? "closesell_today" : "closesell"); // Set the order type, and close position
     Auto buyPrice = depth.Bids[0].Price; // Get "buying 1" price
     If (buyPrice < _holdPrice) { // If the current "buying 1" price is less than the opening short position price
         Log(_holdPrice, "Hit #ff0000"); // Print the log
         buyPrice = _holdPrice - ProfitTick; // Set the close short position price
     } else if (buyPrice > _holdPrice) { // If the current "buying 1" price is greater than the opening short position price
         forceCover = true;
     }
     If (forceCover) {
         Log("StopLoss");
     }
     _coverId = exchange.Buy(forceCover ? depth.Asks[0].Price : buyPrice, _holdAmount); // close short position
     If (!_coverId.Valid) {
         Return false;
     }
}

이 전략에 대한 전체적인 분석은 아래와 같습니다.https://www.fmz.com/strategy/163427) FMZ Quant에서 백테스트 환경을 구성하지 않고 전체 전략 소스 코드를 복사합니다.

백테스트 결과

img

거래 논리

img

전략 선언

높은 주파수 거래의 호기심을 충족시키고 결과를 더 명확하게 보기 위해,이 전략 백테스트 환경 거래 수수료는 0으로 설정되어 간단한 빠른 속도 논리로 이어집니다. 실제 시장에서 수익성을 달성하기 위해 거래 수수료를 커버하려면. 더 많은 최적화가 필요합니다. 예를 들어, 승률을 향상시키기 위해 단기 예측을 수행하기 위해 주문 흐름을 사용하는 것과 교환 수수료 환불, 지속 가능한 수익성있는 전략을 달성하기 위해, 높은 주파수 거래에 대한 많은 책이 있습니다. 모든 사람이 더 많이 생각하고 원칙에 머무르지 않고 실제 시장으로 갈 수 있기를 바랍니다.

우리에 대해

FMZ 퀀트는 정량 거래 애호가들에게 매우 효율적인 백테스트 메커니즘을 제공하는 순수 기술 중심의 팀입니다. 우리의 백테스트 메커니즘은 단순한 가격 매치보다는 실제 교환을 시뮬레이션합니다. 사용자는 자신의 능력을 더 잘 발휘하기 위해 플랫폼을 활용할 수 있기를 바랍니다.


관련

더 많은