Strategie für den Hochfrequenzhandel mit Rohstofffutures, geschrieben in C++

Schriftsteller:Gutes, Erstellt: 2020-05-22 15:28:11, Aktualisiert: 2023-11-02 19:54:28

img

Penny Jump Commodity Futures High Frequency Trading Strategy geschrieben in C++

Zusammenfassung

Der Markt ist das Schlachtfeld, der Käufer und der Verkäufer sind immer im Spiel, was auch das ewige Thema des Handelsgeschäfts ist.

Klassifizierung der Hochfrequenzstrategie

Im Hochfrequenzhandel gibt es zwei Haupttypen von Strategien. Die Käuferseite Strategie und die Verkäuferseite Strategie. Die Verkäuferseite Strategie ist in der Regel eine Market-Making-Strategie, und die diese beiden Seiten der Strategien sind Gegner. Zum Beispiel die Hochfrequenz-Arbitrage Käuferstrategie der Glättung aller unzumutbaren Phänomene auf dem Markt mit der schnellsten Geschwindigkeit, die Initiative zu ergreifen, um den Preis schnell anzugreifen, oder den falschen Preis anderer Market Maker zu essen.

Es gibt auch eine Möglichkeit, die historischen Daten oder die Bestellregeln des Marktes zu analysieren, die ausstehenden Aufträge zu einem unzumutbaren Preis im Voraus zu senden und die Rücknahmeaufträge mit der schnellen Veränderung des Marktpreises zu senden.

Was ist die Penny-Jump-Strategie?

Penny Jump bedeutet in englischer Sprache Mikropreissteigerung. Das Prinzip ist es, den Kaufpreis und den Verkaufspreis des Marktes zu verfolgen. Dann ist es nach dem Marktpreis plus oder minus der Mikropreissteigerung des Nachverfolgungspreises offensichtlich, dass dies eine passive Handelsstrategie ist, sie gehört zur Verkäuferseite Marktgestaltung Strategie. Ihr Geschäftsmodell und Logik ist es, bilaterale Transaktionen auf börsennotierten Limitordern durchzuführen, um Liquidität zu bieten.

Die Market-Making-Strategie erfordert eine gewisse Menge an Inventar in der Hand und handelt dann sowohl auf der Käufer- als auch auf der Verkäuferseite. Das Hauptentgelt dieser Strategie ist die Provisionsgebühr, die von der Börse bereitgestellt wird, sowie der Preisunterschied, der durch Kauf niedrig und Verkauf hoch erzielt wird. Aber für viele Hochfrequenzhändler, die Market-Making machen möchten, ist ein Bid-Ask-Spread eine gute Sache, aber es ist kein absolutes Mittel zum Gewinn.

Das Prinzip der Penny Jump Strategie

Wir wissen, dass es viele Kleinanleger auf dem Handelsmarkt gibt, und es gibt auch viele große Investoren, wie: hot money, öffentliche Fonds, private Fonds und so weiter. Kleinanleger haben normalerweise weniger Mittel, ihre Aufträge haben eine sehr geringe Wirkung auf den Markt, sie können jederzeit leicht ein Handelsziel kaufen und verkaufen. Aber für große Fonds, die am Markt teilnehmen, ist es nicht so einfach.

Wenn ein großer Investor 500 Lose Rohöl kaufen möchte, gibt es nicht so viele Aufträge zum aktuellen Preis zum Verkauf, und der Investor will sie nicht zum höheren Preis kaufen. Wenn er darauf besteht, den Kauf Auftrag zum aktuellen Preis zu senden, wird die Kosten für Schlupfpunkte zu hoch sein. Daher muss er einen ausstehenden Auftrag zum gewünschten Preis senden. Alle Marktteilnehmer werden einen hohen Kauf Auftrag sehen, der auf dem bestimmten Preis angezeigt wird.

Aufgrund dieser riesigen Bestellung sieht es auf dem Markt ungeschickt aus, manchmal nennen wir es "Elefantenbestellungen". Zum Beispiel zeigt der aktuelle Markt:

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

Plötzlich springt dieser umständliche Elefant in den Markt und der Gebotspreis wird zum Preis von 400.1 gesendet.

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

Alle Händler wissen, dass, wenn es eine große Menge an ausstehenden Aufträgen zu einem bestimmten Preis gibt, dieser Preis eine starke Unterstützung (oder Widerstand) bilden wird.

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

Der Preis 400.1 wird Kaufpreis 2 in der Orderbuchtiefe. Wenn der Preis dann auf 400.3 steigt, erzielt der Hochfrequenzhändler einen Gewinn von 0,1

Auch wenn der Preis nicht steigt, gibt es in der Position kaufen 2 immer noch einen elefant, der den Preis hält, und er kann schnell zum Preis 400.1 an den Elefanten zurückverkauft werden. Dies ist die allgemeine Idee der Penny Jump-Strategie. Seine Logik ist so einfach wie diese, indem er den Marktorderstatus überwacht, die Absichten des Gegners spekuliert und dann die Führung übernimmt, um eine günstige Position aufzubauen und schließlich in kurzer Zeit von einem kleinen Spread profitieren zu können. Für diesen elefant, weil er eine riesige Menge Kauforder auf dem Markt hängt, hat er seine Handelsabsichten freigegeben und wurde natürlich zum gejagten Ziel von Hochfrequenzhändlern.

Umsetzung der Penny Jump Strategie

Erstens, beobachten Sie die Handelsmöglichkeiten mit sehr geringer Wahrscheinlichkeit des Marktes und machen Sie entsprechende Strategien gemäß der Handelslogik. Wenn die Logik komplex ist, müssen Sie das vorhandene mathematische Wissen verwenden, das Modell verwenden, um die Natur des irrationalen Phänomens so weit wie möglich zu beschreiben und die Überanpassung zu minimieren. Darüber hinaus muss es durch eine Backtest-Engine überprüft werden, die dem Preis first then Volume first Prinzip entsprechen kann. Glücklicherweise haben wir FMZ Quant-Plattform, die derzeit diese Backtesting-Modi unterstützt.

Was bedeutet die Unterstützung der Price first then Volume first Backtest Engine? Sie können es so verstehen: Sie senden einen ausstehenden Auftrag bei 400.1 zum Kauf, nur wenn der Verkaufspreis in der Orderbuchtiefe 400.1 oder niedriger ist, kann Ihr ausstehender Auftrag geschlossen werden. Es berechnet nur die Preisdaten der ausstehenden Aufträge und berechnet nicht die Volumendaten der ausstehenden Aufträge, die nur der Preispriorität entspricht ((Preis first) in den Austauschorderausgleichsregeln.

Der Volume first ist eine verbesserte Version des Preis first. Er ist sowohl preisprioritisiert als auch zeitprioritisiert. Es kann gesagt werden, dass dieser Matching-Modus genau dem Austauschmodell entspricht. Durch die Berechnung der Höhe der ausstehenden Bestellung wird beurteilt, ob die aktuelle ausstehende Bestellung die Bedingung einer passiven Transaktion erreicht, um die Volumentransaktion zu realisieren, um eine echte Simulation des realen Marktumfelds zu erreichen.

Darüber hinaus können einige Leser feststellen, dass die Penny Jump-Strategie Markthandelschancen erfordert, dh der Marktbedarf mindestens zwei hop Preislücken hat. Unter normalen Umständen ist der Haupthandelsvertrag von Rohstoff-Futures relativ beschäftigt. Der Unterschied zwischen Käufen 1 und Verkaufen 1hop besteht darin, dass es fast keine Handelschancen gibt. Wir setzen also unsere Energie auf den Sub-primären Vertrag, wo der Handel nicht zu aktiv ist. Diese Art von Handelsvertrag hat gelegentlich zwei oder sogar drei hop Möglichkeiten. Zum Beispiel tritt im MA (Methanol Code in chinesischen Rohstoff-Futures) 1909 Vertrag folgende Situation auf:

img

Verkaufe 1 Preis 2225 mit Volumen 551, Kaufe 1 Preis 2223 mit Volumen 565, schau ein paar Sekunden nach unten. Nachdem dies geschehen ist, wird es nach mehreren Ticks verschwinden. In diesem Fall betrachten wir den Markt als Selbstkorrektur. Was wir tun müssen, ist aufzuholen. Bevor der Markt ihn aktiv korrigiert. Wenn wir es manuell tun, wäre es unmöglich, mit Hilfe des automatischen Handels können wir es ermöglichen.

Die zwei hop Preislücke Auftritt Situation passiert sehr oft, aber die drei Hopps sind die sicherste, aber die drei Hopps selten auftreten, was zu einer Handelsfrequenz zu niedrig ist.

Als nächstes beobachten wir den Unterschied zwischen dem vorherigen Selling 1 Buying 1 und dem Buying 1 Selling 1 jetzt. Um die Preislücke zwischen dem Markt zu schließen, wenn die Geschwindigkeit schnell genug ist, kann sie an der Spitze anderer Aufträge platziert werden. Außerdem ist die Positionshaltenzeit sehr kurz, mit dieser Handelslogik, nach der Realisierung der Strategie, nehmen wir den MA909 als Beispiel, empfiehlt der reale Markttest Esunny anstelle der CTP-Schnittstelle, der Mechanismus der Position und des Fondswechsels für Esunny ist durch gedrängte Daten, sehr geeignet für den Hochfrequenzhandel.

Strategiecode

Da die FMZ Quant-Plattform C++-Strategie schreiben verwendet Beispiele sind zu wenige, hier verwenden wir C++ um diese Strategie zu schreiben, die für jeden praktisch zu lernen ist, und die Vielfalt ist Rohstoff-Futures.fmz.com> Login > Dashboard > Strategiebibliothek > Neue Strategie > Klicken Sie auf das Dropdown-Menü in der oberen linken Ecke > Wählen Sie C ++, um mit dem Schreiben der Strategie zu beginnen, achten Sie auf die Kommentare im folgenden Code.

  • Schritt 1: Erstellen Sie zunächst den Rahmen der Strategie, in dem eine HFT-Klasse und eine Hauptfunktion definiert sind. Die erste Zeile in der Hauptfunktion besteht darin, das Protokoll zu löschen. Der Zweck besteht darin, die zuvor ausgeführten Protokollinformationen bei jedem Neustart der Strategie zu löschen. Die zweite Zeile besteht darin, einige Fehlermeldungen zu filtern, die nicht notwendig sind, wie z. B. Netzwerkverzögerungen und einige Tipps, so dass das Protokoll nur wichtige Informationen aufzeichnet und sauberer aussieht; die dritte Zeile besteht darin, die Meldung Init OK zu drucken, was bedeutet, dass das Programm gestartet wurde. Die vierte Zeile besteht darin, ein Objekt nach der HFT-Klasse zu erstellen, und der Objektname ist hft; das fünfte Zeileprogramm tritt die While-Schleife ein und führt immer die Schleife in der hft-Methode aus, so dass man sehen kann, dass die Loop-Methode die Logik dieses Programms ist. Zeile 6 ist eine andere Kernmeldung

Als nächstes schauen wir uns die HFT-Klasse an, die fünf Methoden hat. Die erste Methode ist die Konstruktionsmethode; die zweite Methode besteht darin, den aktuellen Wochentag zu erhalten, um festzustellen, ob es sich um eine neue K-Linie handelt; die dritte Methode besteht hauptsächlich darin, alle unerfüllten Aufträge zu stornieren und detaillierte Positionsinformationen zu erhalten, da sie zuerst den aktuellen Positionsstatus bestimmen muss, bevor die Bestellung platziert wird; die vierte Methode wird hauptsächlich verwendet, um einige Informationen zu drucken, für diese Strategie ist diese Methode nicht die wichtigste; die wichtigste ist die fünfte Methode.

/ / 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
}

Also lassen Sie uns sehen, wie jede der Methoden in dieser HFT-Klasse implementiert wird, und wie die wichtigste Loop-Methode funktioniert. Von oben nach unten werden wir die spezifische Implementierung jeder Methode einzeln implementieren, und Sie werden feststellen, dass die ursprüngliche Hochfrequenzstrategie so einfach ist. Bevor wir über die HFT-Klasse sprechen, haben wir zuerst mehrere globale Variablen für die Speicherung der Ergebnisse der hft-Objektberechnung definiert. Sie sind: Speicherung des Auftragsstatus, Positionsstatus, Halten der Long-Position, Halten der Short-Position, Kaufpreis, Kaufmenge, Verkaufspreis, Verkaufsmenge. Bitte sehen Sie den folgenden Code:

/ / 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;

Mit den oben genannten globalen Variablen können wir die vom hft-Objekt berechneten Ergebnisse separat speichern, was für nachfolgende Anrufe durch das Programm praktisch ist. Als nächstes werden wir über die spezifische Implementierung jeder Methode in der HFT-Klasse sprechen. Erstens ist die erste HFT-Methode ein Konstruktor, der die zweite getTradingWeekDay-Methode aufruft und das Ergebnis in das Protokoll druckt. Die zweite getTradingWeekDay-Methode erhält den aktuellen Wochentag, um festzustellen, ob es sich um eine neue K-Zeile handelt. Es ist auch sehr einfach zu implementieren, den Zeitstempel zu erhalten, die Stunde und Woche zu berechnen und schließlich die Anzahl der Wochen zurückzugeben; die dritte getState-Methode ist etwas lang, ich werde nur die allgemeine Idee beschreiben, für eine spezifische Erklärung können Sie sich die Kommentare im folgenden Codierungsblock ansehen.

Als Nächstes erhalten wir zuerst alle Aufträge, geben das Ergebnis ein normales Array zurück, dann durchqueren wir dieses Array, eines nach dem anderen, um die Reihenfolge zu annullieren, dann erhalten wir die Positionsdaten, geben ein Array zurück, und dann durchqueren wir dieses Array, erhalten detaillierte Positionsinformationen, einschließlich: Richtung, Position, gestrige oder aktuelle Position usw., und geben schließlich das Ergebnis zurück; die vierte Stoppmethode ist, Informationen zu drucken; der Code lautet wie folgt:

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");
    }

Schließlich konzentrieren wir uns darauf, wie die Loop-Funktion die Strategie-Logik und die Reihenfolge steuert. Wenn Sie sorgfältiger sehen möchten, können Sie sich auf die Kommentare im Code beziehen. Zuerst bestimmen Sie, ob die CTP-Transaktion und der Marktserver verbunden sind; dann erhalten Sie den verfügbaren Kontostand und erhalten Sie die Anzahl der Wochen; dann legen Sie den zu handelnden Sortencode fest, indem Sie die offizielle FMZ-Funktion SetQuantContractType anrufen und können diese Funktion verwenden, um die Details der Handelsvariante zurückzugeben; rufen Sie dann die GetDepth-Funktion an, um die Daten der aktuellen Markttiefe zu erhalten. Die Tiefendaten umfassen: Kaufpreis, Kaufvolumen, Verkaufspreis, Verkaufvolumen usw., und wir speichern sie mit Variablen, weil sie später verwendet werden; Dann führen wir diese Daten in die Statusleiste aus, um dem Benutzer den aktuellen Marktstatus anzuzeigen; der Code

// 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);
}

Nach so viel getan, können wir endlich Orders platzieren. Vor dem Handel, zuerst beurteilen wir den aktuellen Halteposition Status des Programms (keine Halteposition, lange Position Aufträge, kurze Position Aufträge), hier haben wir die verwendet, wenn...anders wenn...anders wenn Logik Steuerung. Sie sind sehr einfach, Wenn es keine Halteposition gibt, wird die Position nach der logischen Bedingung geöffnet. Wenn es eine Halteposition gibt, wird die Position nach der logischen Bedingung geschlossen. Um jedem das Verständnis zu erleichtern, verwenden wir drei Absätze, um die Logik zu erklären, Für den Öffnungsposition Teil:

Zuerst deklarieren wir eine booleanische Variable, die wir verwenden, um die Schlussposition zu steuern; als nächstes müssen wir die Leistungsbilanzinformationen erhalten und den Gewinnwert aufzeichnen, dann den Status der Auszahlungsanweisung bestimmen, wenn die Anzahl der Auszahlungen das festgelegte Maximum übersteigt, drucken Sie die entsprechenden Informationen im Protokoll; berechnen Sie dann den absoluten Wert der aktuellen Gebots- und Angebotspreisunterschied, um festzustellen, ob es mehr als 2 Hopps zwischen dem aktuellen Gebotspreis und dem Angebotspreis gibt.

Als nächstes erhalten wir den Kaufpreis 1 und den Verkaufspreis 1, wenn der vorherige Kaufpreis größer ist als der aktuelle Kaufpreis und das aktuelle Verkaufsvolumen kleiner als das Kaufvolumen ist, bedeutet dies, dass der Kaufpreis 1 verschwunden ist. der Eröffnungspreis der Long-Position und die Auftragsmenge werden festgelegt; andernfalls, wenn der vorherige Verkaufspreis kleiner ist als der aktuelle Verkaufspreis und das aktuelle Kaufvolumen kleiner ist als Das Verkaufvolumen beweist, dass der Verkaufspreis 1 verschwunden ist, werden der Eröffnungspreis der Short-Position und die Auftragsmenge festgelegt; am Ende treten die Long-Position und die Short-Orders gleichzeitig in den Markt ein. Der spezifische Code ist wie folgt:

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
}

Als nächstes werden wir darüber sprechen, wie man eine lange Position schließt, zunächst den Auftragstyp gemäß dem aktuellen Positionsstatus festlegt und dann den Selling 1 Preis erhält. Wenn der aktuelle Selling 1 Preis größer ist als der Kauf-Long-Öffnungspreis, setzt man den Schlusspreis der langen Position. Wenn der aktuelle Selling 1 Preis kleiner ist als der Öffnungspreis der langen Position, setzt man dann die Schlussmengevariable auf wahr zurück und schließt dann alle Long-Positionen.

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;
     }
}

Schließlich schauen wir uns an, wie man eine Short-Position schließt. Das Prinzip ist das Gegenteil der oben genannten Closing-Long-Position. Zuerst, entsprechend dem aktuellen Positionsstatus, setzen Sie den Auftragstyp ein und erhalten dann den Buying 1 Preis, wenn der aktuelle Buying 1 Preis kleiner als der Short-Positionsöffnungspreis ist, wird der Preis der Closing-Short-Position festgelegt. Wenn der aktuelle Buying 1 Preis größer als der Opening-Short-Positionspreis ist, setzen Sie die Schließmenge auf wahr zurück und schließen Sie dann alle Short-Positionen.

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;
     }
}

Die obige Analyse ist eine vollständige Analyse dieser Strategie.https://www.fmz.com/strategy/163427) um den kompletten Strategie-Quellcode zu kopieren, ohne die Backtest-Umgebung auf FMZ Quant zu konfigurieren.

Ergebnisse der Backtests

img

Handelslogik

img

Strategieerklärung

Um die Neugierde des Hochfrequenzhandels zu befriedigen und die Ergebnisse klarer zu sehen, wird diese Strategie-Backtestumgebung Transaktionsgebühr auf 0 gesetzt, was zu einer einfachen Schnellgeschwindigkeitslogik führt. wenn Sie die Transaktionsgebühr decken möchten, um Rentabilität auf dem realen Markt zu erzielen. Mehr Optimierung ist erforderlich. Wie die Verwendung des Auftragsstroms, um kurzfristige Prognosen durchzuführen, um die Gewinnrate zu verbessern, plus die Rückerstattung der Wechselgebühr, Um eine nachhaltige profitable Strategie zu erreichen, gibt es viele Bücher zum Hochfrequenzhandel. Ich hoffe, dass jeder mehr denken und auf den realen Markt gehen kann, anstatt nur am Prinzip zu bleiben.

Über uns

FMZ Quant ist ein rein technologiegetriebenes Team, das einen hocheffizienten verfügbaren Backtest-Mechanismus für Quantitative-Trading-Enthusiasten bietet. Unser Backtest-Mechanismus simuliert einen echten Austausch, anstatt einen einfachen Preis-Match. Wir hoffen, dass Benutzer die Plattform nutzen können, um ihre eigenen Fähigkeiten besser zu spielen.


Verwandt

Mehr