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.
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.
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.
Wir wissen, dass es viele Kleinanleger auf dem Handelsmarkt gibt, und es gibt auch viele große Investoren, wie:
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
Auch wenn der Preis nicht steigt, gibt es in der Position
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
Was bedeutet die Unterstützung der
Der
Darüber hinaus können einige Leser feststellen, dass die Penny Jump-Strategie Markthandelschancen erfordert, dh der Marktbedarf mindestens zwei
Die zwei
Als nächstes beobachten wir den Unterschied zwischen dem vorherigen
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.
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
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
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
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.
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.
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.