Vom quantitativen Handel zum Asset Management - Entwicklung einer CTA-Strategie für eine absolute Rendite

Schriftsteller:FMZ~Lydia, Erstellt: 2023-02-07 09:58:41, aktualisiert: 2023-09-18 20:25:11

, [nowTime, this.basb]]); ObjChart.add (([4, [nowTime, this.sabb]]); ObjChart.update (Chart) (Objektivdiagramm aktualisiert); - Ich weiß.

### 4. In the entry function main(), execute the pre-transaction pre-processing code, which will only run once after the program is started, including:

- ```SetErrorFilter ( )``` to filter the unimportant information in the console
- ```exchange.IO ( )``` to set the digital currency to be traded
- ```ObjChart.reset ( )``` to clear the previous chart drawn before starting the program
- ```LogProfitReset ( )``` to clear the status bar information before starting the program

After the above pre-transaction pre-processing is defined, the next step is to enter the polling mode and execute the onTick() function repeatedly. It also sets the sleep time for Sleep () polling, because the API of some digital currency exchanges has built-in access limit for a certain period of time.

Funktion main() { // Filtern Sie die unwichtigen Informationen in der Konsole SetErrorFilter ((429 zu bekommenRecords: zu bekommenOrders: zu bekommenDepth: zu bekommenAccount zu bekommen: zu kaufenSelltimeout zu bekommenFutures_OP); exchange.IO(Währung, Name + _USDT); // Setzen Sie die zu handelende digitale Währung ein ObjChart.reset(); // Löschen Sie das vorherige Diagramm, das vor dem Start des Programms gezeichnet wurde LogProfitReset ((); // Löschen Sie die Statusleiste vor dem Start des Programms while (true) { // Geben Sie den Umfrage-Modus ein Ausführen der Funktion onTick Schlaf ((500); // Schlaf für 0,5 Sekunden - Ich weiß. - Ich weiß.

## II. Get and calculate data

1. Obtain basic data object, account balance, and boll indicator data for use in the trading logic.

Funktion eingeschaltet var data = new Data ((tradeTypeA, tradeTypeB); // Erstellen eines Basisdatenobjekts var accountStocks = data.accountData.Stocks; // Kontostand Var boll = data.boll ((dataLength, timeCycle); // Erhalten Sie Boll-Indikatordaten wenn (!boll) zurück; // Wenn keine Boll-Daten vorhanden sind, zurück - Ich weiß.

## III. Place an order and handle the follow-up

1. Execute the buying and selling operation according to the above strategic logic. First, judge whether the price and indicator conditions are valid, then judge whether the position conditions are valid, and finally execute the trade () order function.

// Erklärung des Preisunterschieds // basb = (Verkaufe einen Preis von Vertrag A - Kaufe einen Preis von Vertrag B) // sabb = (Einen Preis von Vertrag A kaufen - einen Preis von Vertrag B verkaufen) wenn (data.sabb > boll.middle && data.sabb < boll.up) { // Wenn sabb höher ist als die mittlere Spur wenn (data.mp(tradeTypeA, 0)) { // Überprüfen Sie, ob Vertrag A lange Aufträge hat, bevor Sie eine Bestellung aufgeben Daten.trade(tradeTypeA, closebuy); // Vertrag A schließt eine Longposition - Ich weiß. wenn (data.mp(tradeTypeB, 1)) { // Überprüfen Sie, ob Vertrag B vor der Bestellung Shortorders hat data.trade ((tradeTypeB, closesell); // Vertrag B schließt die Leerposition - Ich weiß. } else if (data.basb < boll.middle && data.basb > boll.down) { // Wenn basb niedriger ist als die mittlere Spur wenn (data.mp(tradeTypeA, 1)) { // Überprüfen Sie, ob Vertrag A vor der Auftragserteilung Short-Orders hat data.trade ((tradeTypeA, closesell); // Vertrag A schließt eine Leerposition - Ich weiß. wenn (data.mp(tradeTypeB, 0)) { // Überprüfen Sie, ob Vertrag B lange Aufträge hat, bevor Sie eine Bestellung aufgeben Daten.trade(tradeTypeB, closebuy); // Vertrag B schließt eine Longposition - Ich weiß. - Ich weiß. wenn (AccountStocks * Math.max(data.askA, data.askB) > 1) { // Wenn ein Guthaben auf dem Konto vorliegt wenn (data.basb < boll.down) { // Wenn der Preisunterschied bei basb niedriger ist als der Preisuntergang wenn (!data.mp(tradeTypeA, 0)) { // Überprüfen Sie, ob Vertrag A lange Aufträge hat, bevor Sie eine Bestellung aufgeben Daten.trade(tradeTypeA, buy); // Vertrag A eröffnet eine Longposition - Ich weiß. wenn (!data.mp(tradeTypeB, 1)) { // Überprüfen Sie, ob Vertrag B vor der Bestellung Short-Orders hat Daten.trade ((tradeTypeB, sell); // Vertrag B eröffnet eine Leerposition - Ich weiß. } else if (data.sabb > boll.up) { // If sabb Preisdifferenz ist höher als die obere Spur wenn (!data.mp(tradeTypeA, 1)) { // Vor der Auftragserteilung prüfen, ob Vertrag A Short-Orders hat Daten.trade(tradeTypeA, sell); // Vertrag A eröffnet eine Leerposition - Ich weiß. wenn (!data.mp(tradeTypeB, 0)) { // Überprüfen Sie, ob Vertrag B lange Aufträge hat, bevor Sie eine Bestellung aufgeben Daten.trade ((tradeTypeB, buy); // Vertrag B eröffnet eine Longposition - Ich weiß. - Ich weiß. - Ich weiß.

2. After the order is placed, it is necessary to deal with the abnormal situations such as the unsettled order and the holding of a single contract. And draw the chart.

Data.cancelOrders(); // Aufträge stornieren Daten.ZeichnungGrafik ((Ball); // Zeichnung Daten.isEven(); // Handling der individuellen Verträge

As above, we have created a simple cross-period arbitrage strategy of digital currency completely through more than 200 lines code. The complete code is as follows:

// Globale Variable // Deklarieren eines Chartobjekts für das Konfigurationsdiagramm Verzeichnis = { __isStock: wahr, Werkzeugtipp: { xDateFormat: %Y-%m-%d %H:%M:%S, %A }, Titel: { Text: Transaktionsgewinn- und -verlustkurve (ausführlich) }, RangeSelector: { Schaltflächen: Typ: Stunde, Anzahl: 1, Text: 1h Wir sind hier. Typ: Stunde, Anzahl: 2, Text: 3h Wir sind hier. Typ: Stunde, Anzahl: 8 Text: 8h Wir sind hier. Typ: alle, Text: Alle Die Kommission ausgewählt: 0, EingabeAktiviert: falsch }, xAchse: { Typ: Datum/Zeit }, yAchse: { Titel: { Text: Preisunterschied }, entgegengesetzt: falsch, }, Reihe: Bezeichnung: Oberbahn, id: Zeile 1, oben , Daten: [] Wir sind hier. Bezeichnung: mittelstrecke, id: Zeile2, Mitte, Daten: [] Wir sind hier. Name: nach unten auf der Strecke, id: Linie 3, nach unten , Daten: [] Wir sind hier. Name: basb, id: zeile 4, basb, Daten: [] Wir sind hier. Name: sabb, ID: Linie 5, sabb, Daten: [] - Ich weiß. ); Var ObjChart = Chart(chart); // Zeichnen Objekt Varbars = []; // Speicherpreisdifferenzreihe var oldTime = 0; // Zeitstempel für historische Daten

// Parameter Var tradeTypeA = this_week; // Arbitrage Ein Vertrag var tradeTypeB = quarter; // Schiedsvertrag B Datenlänge = 10; // Zeitrahmenlänge WarmzeitZyklus = 1; // K-Zeile var name = ETC; // Währungen Var-Einheit = 1; // Bestellmenge

// Grunddaten Funktion Daten ((tradeTypeA, tradeTypeB) { // Übertragen von Arbitrage-A-Kontrakt und Arbitrage-B-Kontrakt this.accountData = _C(exchange.GetAccount); // Erhalten Sie Kontoinformationen this.positionData = _C(exchange.GetPosition); // Positionsinformationen erhalten Var recordsData = _C(exchange.GetRecords); // Erhalten Sie K-Zeilendaten exchange.SetContractType(tradeTypeA); // Abonnieren Sie einen Arbitrage-Vertrag VAR DEEPTHDATA = _C (exchange.GetDepth); // Arbitrage Die Tiefendaten für einen Vertrag exchange.SetContractType(tradeTypeB); // Abonnieren Sie einen Arbitrage-B-Vertrag VAR depthDataB = _C(exchange.GetDepth); // Arbitrage B Vertragstiefe Daten this.time = recordsData[recordsData.length - 1].Zeit; // Zeit, um die neuesten Daten zu erhalten this.askA = depthDataA.Aks[0].Price; // Verkaufen Sie einen Preis von Arbitrage Ein Vertrag this.bidA = depthDataA.Bids[0].Preis; // Kaufen Sie einen Preis von Arbitrage Ein Vertrag this.askB = depthDataB.Aks[0].Preis; // Verkaufen Sie einen Preis des Arbitrage-B-Kontrakts this.bidB = depthDataB.Bids[0].Preis; // Kaufen Sie einen Preis des Arbitrage-B-Kontrakts // Positive Preisunterschiede durch Arbitrage (Verkaufe einen Preis von Vertrag A - Kaufe einen Preis von Vertrag B) this.basb = depthDataA.Aks[0].Preis - depthDataB.Bids[0].Preis; // Negative Preisunterschiede durch Arbitrage (Einen Preis von Vertrag A kaufen - Einen Preis von Vertrag B verkaufen) this.sabb = depthDataA.Bids[0].Preis - depthDataB.Asks[0].Preis; - Ich weiß.

// Position einnehmen Daten.Prototyp.mp = Funktion (TradeType, Typ) { Var positionData = this.positionData; // Positionsinformationen erhalten für (var i = 0; i < positionData.length; i++) { wenn (positionData[i].ContractType == tradeType) { wenn (PositionDaten[i].Typ == Typ) { wenn (PositionDaten[i].Menge > 0) { Rückgabe der PositionDaten[i].Betrag; - Ich weiß. - Ich weiß. - Ich weiß. - Ich weiß. zurückgeben false; - Ich weiß.

// Synthese neuer K-Liniendaten und Boll-Indikatordaten Daten.Prototyp.Boll = Funktion (Nummer, ZeitZyklus) { VAR selbst = {}; // temporäre Objekte // Medianwert der zwischen positiver und negativer Arbitragepreisunterschied Selbst.Schließen = (dieses.basb + dieses.sabb) / 2; wenn (diese.ZeitA == diese.ZeitB) { Selbstzeit = this.time; } // Vergleichen Sie zwei Tiefendatenzeitstempel wenn (this.time - oldTime > timeCycle * 60000) { Barren.Schub ((selbst); oldTime = this.time; } // Geben Sie das Preisdifferenz-Datenobjekt gemäß dem angegebenen Zeitraum in das K-Linien-Array ein wenn (Bar.length > num * 2) { bars.shift(); // Steuerung der Länge des K-Linien-Arrays - Nein. Rückgabe; - Ich weiß. Var Boll = TA.BOLL(Barren, num, 2); // Ruf den Boll-Indikator in der Talib-Bibliothek Rückgabe oben: Boll[0][boll[0].Längen - 1], // Boll-Indikator oberer Gleis Mitte: Boll[1][boll[1].Länge - 1], // Boll-Indikator Mitte der Strecke nach unten: Boll[2][boll[2].Länge - 1] // Boll-Indikator nach unten } // Gibt verarbeitete Boll-Indikatordaten zurück - Ich weiß.

// Bestell etwas Daten.Prototyp.Trade = Funktion (TradeType, Typ) { exchange.SetContractType(tradeType); // Vor der Bestellung einen Vertrag erneut abonnieren Warte, du hast einen Preis gefordert. wenn (TradeType == tradeTypeA) { // Wenn der Auftrag in Vertrag A platziert wird AskPrice = this.askA; // Setzen Sie askPrice Ausgabe der Ausgabe der Ausgabe der Ausgabe } anders wenn (tradeType == tradeTypeB) { // Wenn der Auftrag in Vertrag B platziert wird AskPrice = this.askB; // Setzen Sie askPrice Ausgabe der Ausgabe der Ausgabe - Ich weiß. Schalter (Typ) { // Modus der Auftragsvergabe Fall kaufen: exchange.SetDirection(type); // Setzen Sie den Bestellmodus Rückgabe.Kauf ((AskPreis, Einheit); Fall verkauf: exchange.SetDirection(type); // Setzen Sie den Bestellmodus Rückgabe-Austausch.Verkauf ((Bietpreis, Einheit); Fall closebuy: exchange.SetDirection(type); // Setzen Sie den Bestellmodus Rückgabe-Austausch.Verkauf ((Bietpreis, Einheit); Fall schließender Verkauf: exchange.SetDirection(type); // Setzen Sie den Bestellmodus Rückgabe.Kauf ((AskPreis, Einheit); Standard: zurückgeben false; - Ich weiß. - Ich weiß.

// Bestellungen stornieren Daten.Prototyp.Rückgängig machenOrders = Funktion () { Schlaf(500); // Verzögerung vor der Stornierung, weil einige Austausch, wissen Sie, was ich meine Var-Orders = _C(exchange.GetOrders); // Erhalten Sie ein Array aus unerfüllten Aufträgen wenn (orders.length > 0) { // Wenn es unerfüllte Aufträge gibt für (var i = 0; i < orders.length; i++) { // Iteration durch das Array der unerfüllten Aufträge exchange.CancelOrder(Bestellungen[i].Id); // Unerfüllte Bestellungen einzeln stornieren Schlaf ((500); // Schlaf für 0,5 Sekunden - Ich weiß. return false; // Return false, wenn eine nicht ausgefüllte Bestellung storniert wird - Ich weiß. return true; // Return true, wenn keine unerfüllten Bestellungen vorliegen - Ich weiß.

// Handhabung von Einzelverträgen Daten.Prototyp.isEven = Funktion () { Var positionData = this.positionData; // Positionsinformationen erhalten Var-Typ = null; // Positionrichtung wechseln // Wenn die restlichen 2 der Position Array Länge ist nicht gleich 0 oder die Position Array Länge ist nicht gleich 2 wenn (positionData.length % 2!= 0 für (var i = 0; i < positionData.length; i++) { // Iterieren durch das Positionarray wenn (positionData[i].Type == 0) { // Wenn es sich um eine lange Reihenfolge handelt Typ = 10; // Satz der Reihenfolge Parameter } else if (positionData[i].Type == 1) { // Wenn es sich um eine kurze Ordnung handelt Typ = -10; // Anordnungsparameter festlegen - Ich weiß. // Alle Positionen schließen this.trade(positionDaten[i].VertragArt, Art, PositionDaten[i].Betrag); - Ich weiß. - Ich weiß. - Ich weiß.

// Zeichnen Daten.Prototyp.ZeichnungDiagramm = Funktion (Ball) { var nowTime = neues Datum ().getTime (); ObjChart.add (([0, [nowTime, boll.up]]); ObjChart.add (([1, [nowTime, boll.middle]]); ObjChart.add (([2, [nowTime, boll.down]]); ObjChart.add (([3, [nowTime, this.basb]]); ObjChart.add (([4, [nowTime, this.sabb]]); ObjChart.update (Chart) (Objektivdiagramm aktualisiert); - Ich weiß.

// Handelsbedingungen Funktion eingeschaltet var data = new Data ((tradeTypeA, tradeTypeB); // Erstellen eines Basisdatenobjekts var accountStocks = data.accountData.Stocks; // Kontostand Var boll = data.boll ((dataLength, timeCycle); // Erhalten Sie Boll-Indikatordaten wenn (!boll) zurück; // Wenn keine Boll-Daten vorhanden sind, zurück // Erläuterung der Preisunterschiede // basb = (Verkaufe einen Preis von Vertrag A - Kaufe einen Preis von Vertrag B) // sabb = (Einen Preis von Vertrag A kaufen - einen Preis von Vertrag B verkaufen) wenn (data.sabb > boll.middle && data.sabb < boll.up) { // Wenn sabb höher ist als die mittlere Spur wenn (data.mp(tradeTypeA, 0)) { // Überprüfen Sie, ob Vertrag A lange Aufträge hat, bevor Sie eine Bestellung aufgeben Daten.trade(tradeTypeA, closebuy); // Vertrag A schließt eine Longposition - Ich weiß. wenn (data.mp(tradeTypeB, 1)) { // Überprüfen Sie, ob Vertrag B vor der Bestellung Shortorders hat data.trade ((tradeTypeB, closesell); // Vertrag B schließt die Leerposition - Ich weiß. } else if (data.basb < boll.middle && data.basb > boll.down) { // Wenn basb niedriger ist als die mittlere Spur wenn (data.mp(tradeTypeA, 1)) { // Überprüfen Sie, ob Vertrag A vor der Auftragserteilung Short-Orders hat data.trade ((tradeTypeA, closesell); // Vertrag A schließt eine Leerposition - Ich weiß. wenn (data.mp(tradeTypeB, 0)) { // Überprüfen Sie, ob Vertrag B lange Aufträge hat, bevor Sie eine Bestellung aufgeben Daten.trade(tradeTypeB, closebuy); // Vertrag B schließt eine Longposition - Ich weiß. - Ich weiß. wenn (AccountStocks * Math.max(data.askA, data.askB) > 1) { // Wenn ein Guthaben auf dem Konto vorliegt wenn (data.basb < boll.down) { // Wenn der Preisunterschied bei basb niedriger ist als der Preisuntergang wenn (!data.mp(tradeTypeA, 0)) { // Überprüfen Sie, ob Vertrag A lange Aufträge hat, bevor Sie eine Bestellung aufgeben Daten.trade(tradeTypeA, buy); // Vertrag A eröffnet eine Longposition - Ich weiß. wenn (!data.mp(tradeTypeB, 1)) { // Überprüfen Sie, ob Vertrag B vor der Bestellung Short-Orders hat Daten.trade ((tradeTypeB, sell); // Vertrag B eröffnet eine Leerposition - Ich weiß. } else if (data.sabb > boll.up) { // If sabb Preisdifferenz ist höher als die obere Spur wenn (!data.mp(tradeTypeA, 1)) { // Vor der Auftragserteilung prüfen, ob Vertrag A Short-Orders hat Daten.trade(tradeTypeA, sell); // Vertrag A eröffnet eine Leerposition - Ich weiß. wenn (!data.mp(tradeTypeB, 0)) { // Überprüfen Sie, ob Vertrag B lange Aufträge hat, bevor Sie eine Bestellung aufgeben Daten.trade ((tradeTypeB, buy); // Vertrag B eröffnet eine Longposition - Ich weiß. - Ich weiß. - Ich weiß. Data.cancelOrders(); // Bestellungen stornieren Daten.ZeichnungGrafik ((Ball); // Zeichnung Daten.isEven(); // Verwalten Sie das Halten einzelner Verträge - Ich weiß.

// Eingabefunktion Funktion main() { // Filtern Sie unwichtige Informationen in der Konsole SetErrorFilter ((429 zu erhaltenRecords: zu erhaltenOrders: zu erhaltenDepth: zu erhaltenAccount zu erhalten: zu kaufenSelltimeout zu erhaltenFutures_OP); exchange.IO(Währung, Name + _USDT); //Setzen Sie die zu handelende digitale Währung ein ObjChart.reset(); // Löschen Sie das vorherige Diagramm, das vor dem Start des Programms gezeichnet wurde LogProfitReset ((); // Löschen Sie die Statusleiste vor dem Start des Programms while (true) { // Eingabe des Wahlmodus onTick(); // Ausführen der Funktion onTick Schlaf ((500); // Schlaf für 0,5 Sekunden - Ich weiß. - Ich weiß. ` Der Arbitragehandel entstand aus der Aktienhandelsstrategie von Morgan Stanley. Seine Idee ist, dass die Preisdifferenz von zwei stark korrelierten Sorten dem Popcorn-Prozess entspricht, dh die Preisdifferenz von einer Position, die vom historischen Mittel abweicht, immer wieder zum Mittel zurückkehrt und dann wieder vom Mittel abweicht.

Daher können wir niedrig kaufen und hoch verkaufen auf der Preisdifferenz, um Gewinne zu erzielen. Dann, nach dem Prinzip der Standardabweichung in der Statistik, wird das Bollinger Band durch eine mittlere Spur und die oberen und unteren Spuren, die durch die Standardabweichung berechnet werden, gebildet, die Bildung von drei Mesh-Band, die sehr praktisch in der Preisdifferenz-Arbitrage-Transaktion sind.

Nach dem Testen, das nach dieser Strategie betrieben wird, ist das Gesamteinkommen relativ stabil, obwohl das Einkommen jedes Mal nicht sehr hoch ist, ohne die Handlinggebühr und die Wirkungskosten zu berücksichtigen. Es sollte beachtet werden, dass aufgrund der statistischen Arbitrage das Risiko einer umgekehrten Ausweitung der Preisdifferenz besteht. Wir müssen beim Entwerfen das Stop-Loss-Problem berücksichtigen. Zweitens müssen wir auch auf die Wirkungskosten achten. Wenn die Liquidität der beiden am Geschäft beteiligten Verträge schrumpft, hat dies einen großen Einfluss auf das Einkommen und Anleger sollten es entsprechend vermeiden.

4. Fortgeschrittene Iteration der CTA-Strategieentwicklung

4.1 Vermeiden Sie die Fallstricke der CTA-Futures-Strategie

In den letzten beiden Klassen haben wir eine Trendstrategie in MyLanguage und eine Arbitrage-Strategie in JavaScript geschrieben.

In der Tat ist der Backtest nur eine Simulation der Strategie. Es wird nur verwendet, um die Leistung der Strategie in den historischen Daten zu bewerten. Es ermöglicht Händlern, einige Handelsstrategien schnell zu bewerten und zu verwerfen.

In vielen Fällen können Strategien, die im Backtest gut aussehen, aus verschiedenen Gründen den Backteststandard auf dem realen Markt nicht erfüllen.

Statische und dynamische Daten

Wir sollten zunächst ein Konzept von statischen Daten und dynamischen Daten haben, um sie zu quantifizieren. Im Backtest verwenden wir statische historische Daten. Der Preis des Öffnungshochs und des Schlussschlags mit jeder K-Linie ist vollständig und jedes Transaktionssignal kann zu 100% geschlossen werden. Aber die Daten auf dem realen Markt sind dynamisch. Zum Beispiel, wenn der maximale Preis innerhalb von 1 Stunde nach der Öffnung größer als der maximale Preis ist, kaufen. Aber wenn die aktuelle K-Linie nicht abgeschlossen ist, ist der maximale Preis dynamisch und das Handelssignal kann hin und her flackern. Diese Situation zeigt an, dass die Strategie die zukünftige Funktion verwendet, um die Bedingungen des Kaufs und Verkaufs zu beurteilen.

Zukünftige Funktion

Was ist die zukünftige Funktion? Schauen wir uns zunächst die Erklärung der Baidu Enzyklopädie an: Eine Menge hängt von einer anderen Menge ab, wie z. B. Menge A und Menge B. Wenn sich B ändert, ändert sich A, dann ist A eine Funktion von B. Wenn B eine spätere Menge ist, ist A eine frühere Menge, A ändert sich mit B, und A ist eine zukünftige Funktion von B. Sie können verwirrt sein.

Im Allgemeinen handelt es sich um eine Funktion, bei der zukünftige Daten zitiert werden, z. B. die Prognose des Preises von morgen mit dem Preis von morgen. Wenn ein technischer Indikator eine zukünftige Funktion enthält, ist sein Signal unsicher. Es ist oft das aktuelle Transaktionssignal. Wenn die nächste K-Linie erscheint, verschwindet das Signal oder ändert seine Position.

Der Schlusskurs ist eine Zukunftsfunktion. Der Schlusskurs ändert sich immer, bis die neueste K-Linie ausläuft. Sie müssen warten, bis die K-Linie ausläuft, um den Schlusskurs zu bestimmen. Da der Schlusskurs selbst eine Zukunftsfunktion ist, sind alle technischen Indikatoren, die auf dem Schlusskurs basieren, auch Zukunftsfunktionen.

Wenn daher ein technischer Indikator den bestätigten Schlusskurs als Basisdaten verwendet, ändert sich das Handelssignal unabhängig davon, wie lange es vergangen ist, es kann gesagt werden, dass der technische Indikator sich nicht auf die zukünftige Funktion bezieht.

Vergangene Preise

Die Future-Funktion verwendet den zukünftigen Preis, der auch den vergangenen Preis umgekehrt verwenden kann. Dies ist auch ein Problem, das viele Anfänger neigen, zu ignorieren. Um dieses Problem in der Zukunft besser zu veranschaulichen, nehmen wir ein Beispiel: Wenn der aktuelle Maximalpreis innerhalb von 1 Stunde nach der Eröffnung größer als der Maximalpreis ist, kaufen Sie zum Eröffnungspreis. Offensichtlich gibt es kein Problem mit den Bedingungen des Kauf- und Verkaufssignals, aber der Preis der Bestellung hat den vergangenen Preis verwendet.

Im Backtest ist die Strategie normal, da die auf statischen Daten basierende Backtest-Engine nur dann zu 100% geschlossen werden kann, wenn ein Kaufsignal vorliegt. Wenn jedoch der höchste Preis innerhalb von 1 Stunde nach der Eröffnung größer als der höchste Preis ist, ist es sicher, dass der Auftrag nicht zum vorherigen Eröffnungspreis ausgegeben werden kann.

Preisvakuum

Das sogenannte Preisvakuum bezieht sich auf die auf dem K-Liniendiagramm angezeigten Preise, jedoch auf die Preise, mit denen auf dem realen Markt nicht gehandelt werden kann, die hauptsächlich in folgende Fälle unterteilt sind:

  • 1. Jeder, der den Handel gemacht hat, weiß, dass es schwierig ist, zu kaufen, wenn der Preis steigt und schwierig zu verkaufen, wenn der Preis sinkt.
  • 2. Der Übereinstimmungsmechanismus des Austauschs ist Preispriorität und Zeitpriorität. Einige Varianten haben oft eine große Anzahl von Aufträgen auf dem Markt. Wenn Sie mit Aufträgen auf dem realen Markt handeln, müssen Sie hinter anderen Aufträgen liegen. Sie können nur handeln, nachdem andere Aufträge gehandelt wurden. Noch bevor der Preis gehandelt werden kann, hat sich der Preis geändert. Im Backtest jedoch, wenn Ihre Strategie darin besteht, mit der Bestellung umzugehen, werden Sie rechtzeitig handeln, was sich vom realen Marktumfeld unterscheidet.
  • 3. Wenn Sie eine Arbitrage-Strategie verwenden, ist der Gewinn des Backtests sehr hoch, da davon ausgegangen wird, dass Sie diese Preisunterschiede jedes Mal erfasst haben. In Wirklichkeit können viele Preisunterschiede nicht erfasst werden, oder nur ein Bein kann erfasst werden. Im Allgemeinen muss es das sein, das nicht zu Ihrer Richtung führt. Dann müssen Sie das andere Bein sofort füllen. Zu diesem Zeitpunkt ist der Gleitpunkt nicht mehr 1 oder 2 Punkte, und die Arbitrage-Strategie selbst zielt darauf ab, die Preisunterschiede dieser Punkte zu verdienen. Diese Situation kann im Backtest nicht simuliert werden. Der reale Gewinn ist nicht so gut wie der Backtest.
  • 4. Obwohl das Schwarze Schwan-Ereignis nicht häufig verwendet wird, hat es immer noch einen großen Einfluss auf den quantitativen Handel. Wie in der nachfolgenden Tabelle gezeigt, können bei dem Schwarzen Schwan-Ereignis des Devisenfranken sowohl hohe als auch niedrige Preise aus dem Chart gesehen werden. In der Tat ist der mittlere Preis im extremen Markt des Tages Vakuum, und eine große Anzahl von Stop-Loss-Orders verursacht Stampede-Ereignisse. Die Liquidität ist Null und es ist sehr schwierig, mit ihr umzugehen, aber es kann den Verlust im Backtest stoppen.

From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return

Überanpassung

Überfitting ist ein häufiger Fehler, der von Anfängern des quantitativen Handels gemacht wird. Was ist Überfitting? Um ein einfaches Beispiel zu nennen, verwenden manche Leute eine Menge Übungen, um jede Frage in der Schulprüfung auswendig zu lernen. Er kann es nicht tun, wenn sich das Thema während der Prüfung ein wenig ändert. Weil er die Praxis jeder Frage auf sehr komplexe Weise auswendig gelernt hat, hat er die allgemeinen Regeln nicht abstrahiert.

From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return

Wie das obige Diagramm kann sich ein Modell perfekt an Daten anpassen, solange es komplex genug ist. Dies gilt auch für die Überanpassung beim quantitativen Handel. Wenn Ihre Strategie komplex ist und viele externe Parameter hat, gibt es immer einen oder mehrere Parameter, die perfekt zum historischen Markt passen können.

In der Tat ist die Essenz der quantitativen Handelsstrategieentwicklung der Prozess der Abgleichung lokaler nicht-zufälliger Daten aus einer großen Anzahl scheinbar zufälliger Daten. Daher müssen wir statistische Kenntnisse verwenden, um die Falle zu vermeiden.

Die Kompromißlösung besteht darin, Daten innerhalb der Stichprobe und außerhalb der Stichprobe zu verwenden. Teilen Sie die gesamten Daten in zwei Teile und verwenden Sie die Intra-Stichprobe als Trainingssatz, der für den Daten-Backtest verantwortlich ist. Die Extra-Stichprobe wird als Testsatz verwendet und ist für die Verifizierung verantwortlich. Wenn es wenig historische Daten gibt, können Sie auch die Cross-Test-Methode verwenden.

Wenn Sie feststellen, dass die Daten aus der Stichprobe nicht gut funktionieren, und Sie das Gefühl, dass es zu schlimm ist, das Modell zu verlieren oder Sie sind nicht bereit zuzugeben, dass Ihr Modell nicht gut ist, und Sie weiterhin das Modell für die Extra-Stichprobe-Daten zu optimieren, bis die Extra-Stichprobe-Daten auch gut funktionieren, dann müssen Sie Ihr Geld am Ende verlieren.

Überlebensverzerrung

Die Überlebensverzerrung lässt sich durch folgende Beispiele erklären: 1. Wenn sie an der Tuyer stehen, fliegen die Schweine. 2. Menschen, die Fallschirme online verkaufen, werden gelobt, weil Menschen mit Problemen mit Fallschirmen nicht mehr leben. 3. Der Reporter befragte, ob die Passagiere Tickets im Bus gekauft hätten, weil ohne Tickets überhaupt nicht in den Bus steigen können. 4. Die Medien werben dafür, daß man die Lotterie gewinnen kann, weil die Medien Menschen, die die Lotterie nicht gewinnen, nicht aktiv fördern.

In dem obigen Beispiel können wir feststellen, dass die Informationen, die Menschen normalerweise erhalten, tatsächlich gefiltert werden, was eine große Anzahl von Daten oder Proben selektiv ignoriert, und das Ergebnis ist, dass die Schlussfolgerungen auf der Grundlage der Überlebensverzerrung von Echtzeit abgewichen sind.

From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return

  • Linkses Diagramm (ähnlichkeit): Eine sehr gute Handelsstrategie.
  • Rechtses Diagramm (die Realität): Dies ist nur der beste von 200 zufälligen Handels-Backtests.

Das Bild auf der linken Seite ist eine sehr gute Handelsstrategie. Die Kapitalkurve ist gut, und es gibt keinen signifikanten Rückzug, und stabile Gewinnrenditen können erzielt werden. Aber schauen Sie sich das Bild auf der rechten Seite an. Es ist nur das Beste unter den Hunderten von Backtest-Transaktionen. Auf der anderen Seite, wenn wir uns den Finanzmarkt ansehen, gibt es immer mehr Sterne und weniger Langlebigkeitssterne. Wenn die Strategie der Händler mit der Marktlage übereinstimmt, dann kann der Markt jedes Jahr eine Reihe von Sternen schaffen, aber es ist schwierig, Langlebigkeitssterne zu sehen, die mehr als drei Jahre in Folge stetige Gewinne erzielen können.

Kosten-Schock

Es sei denn, Sie warten auf einen Auftrag, haben Sie möglicherweise einen gleitenden Preis beim Handel. Bei den Varianten mit aktivem Handel unterscheiden sich der Bid-Preis und der Ask-Preis in der Regel an einem Punkt. Bei den Varianten mit inaktivem Handel kann der Unterschied größer sein. Jedes Mal, wenn Sie die Initiative ergreifen möchten, einen Deal zu schließen, benötigen Sie mindestens einen Punkt Unterschied, oder sogar mehr. Im Backtest müssen wir jedoch das Problem der Transaktion nicht berücksichtigen, solange es ein Signal gibt, können wir handeln, also müssen wir zumindest einen gleitenden Preis hinzufügen, um das reale Handelsumfeld zu simulieren.

Vor allem für die Strategie, die häufiger gehandelt wird, wenn der Gleitpreis nicht hinzugefügt wird, wenn die Strategie zurück getestet wird, wird die Kapitalkurve immer nach oben neigen, und sobald der angemessene Gleitpreis hinzugefügt wird, wird er sofort zu einem Verlust.

Strategische Kapazität

Die gleiche Strategie wird in effizienten und ineffizienten Märkten, sogar umgekehrt, ganz anders sein. Zum Beispiel in ineffizienten Märkten wie inländischen Aktienmärkten, Rohstoff-Futures und ausländischen digitalen Währungen ist die Kapazität der Hochfrequenzstrategie selbst aufgrund des geringen Handelsvolumens nicht sehr groß, und es gibt keinen Gewinnraum für mehr Menschen, und sogar die ursprünglich profitable Strategie ist zu einem Verlust geworden.

Die oben genannten sind die Probleme und Fallstricke, die bei der Entwicklung und Verwendung von Strategien auftreten können. Für einen erfahrenen Entwickler von Handelssystemen ist Backtesting ein Muss. Weil es Ihnen sagen kann, ob eine strategische Idee in historischen Transaktionen überprüft werden kann. Aber oft bedeutet Backtesting nicht, dass es in Zukunft profitabel sein wird. Weil es zu viele Fallstricke im Backtest gibt, werden Sie nicht verstehen, ohne für einige Lektionen zu bezahlen. Dieser Kurs kann Ihnen helfen, viele quantitative Umgehungen und Fallstricke zumindest zu vermeiden.

4.2 Festlegen des besten Positionsmanagements

In Reminiscenzen eines Aktienbetreibers gibt es einen sehr interessanten Absatz: Der Alte Türkei (früher bekannt als Partridge), der in derselben Wertpapiergesellschaft wie der Held Livermore ist, macht immer eine große Sache.

Selbst Livermore seufzte schließlich: Es gibt nichts Bemerkenswertes an dem Trend. Es gibt immer viele Leute, die auf dem Bullenmarkt bullisch und auf dem Bärenmarkt bearisch sind. Aber sie sind immer gut darin, mit dem Markt zu verhandeln, indem sie versuchen, am niedrigsten Punkt zu kaufen und am höchsten zu verkaufen. Wie der Alte Türkei sind es diejenigen, die den Markt sehen und ihre Positionen halten, die wirklich ein großes Vermögen machen, was auch am schwierigsten zu lernen ist. Dies steht nicht nur vor der Wahl des Ziels und des Timings, sondern auch vor einer wichtigeren Frage: Wie viel Position (Risiko) sollten wir halten (bären)?

Alle gescheiterten Trader haben einseitiges Denken. Beim Handel sehen gierige Menschen nur Gewinne und nicht Risiken, während schüchterne Menschen nur Risiken und nicht Gewinne sehen. Gierige und schüchterne Menschen vergessen Risiken, wenn sie steigen, und vergessen Gewinne, wenn sie fallen. Erfolgreiche Trader berücksichtigen jedoch sowohl Risiken als auch Renditen, dh sie tragen mehrere Dollar Risiko für jeden Dollar, den sie verdienen. Dann ist der Index zur Messung von Rendite und Risiko das Rendite-Risiko-Verhältnis.

Viele Menschen wissen, dass das Risiko so groß ist wie der Gewinn, das heißt, die Rendite ist proportional zum Risiko. Nach Ansicht einiger Leute sollte die Beziehung zwischen Rendite und Risiko wie folgt sein: Die horizontale Achse ist der Prozentsatz des Risikos, und die vertikale Achse ist der Prozentsatz der Rendite:

From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return

In der tatsächlichen Transaktion sind Rendite und Risiko jedoch weit davon entfernt, so einfach wie zwei Punkte einer Linie zu sein, zumindest bewegt sich es nicht immer linear. Das reale Risiko ist die maximale Menge an Verlust, die mit der erwarteten Rendite oder der so genannten maximalen Volatilität aufgenommen werden kann. Obwohl manchmal der maximale schwimmende Verlust nicht immer dem Schlusverlust in Bezug auf das Ergebnis des Handels entspricht, ist der maximale schwimmende Verlust real.

Aus diesem Grunde können wir feststellen, dass das Risiko-Rendite-Verhältnis in der obigen Abbildung nicht die tatsächliche Performance ist.

From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return

Schauen wir uns die obige Grafik an. Die gelbe Kurve zeigt die Schwankung des Nettovermögens bei verschiedenen Risiken. Mit der Erhöhung der erwarteten Renditen erweitern sich auch die Risiken allmählich. Wenn wir den Konkurs auf 0,5 setzen, dh der maximale Verlust 50% erreicht, dann ist dies eine gescheiterte Handelsstrategie. Obwohl die endgültige Rendite der Strategie aus dem Ergebnis positiv ist, ist sie bereits in der Mitte bankrott gegangen.

Selbst wenn Ihre Strategie positiv ist, werden es Liquidationen unter falschem Positionsmanagement sein. Also ist aus dieser Sicht wichtiger, wie viel zu kaufen und zu verkaufen ist als wann zu kaufen und zu verkaufen. Wie man die Position wissenschaftlich verwaltet, ist zu einem grundlegenden Problem in Finanztransaktionen geworden. Also, bevor wir versuchen, dieses Problem zu lösen, lassen Sie uns sehen, wie man wissenschaftlich im Glücksspiel wetten kann.

From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return

Nehmen wir das Münzwerfen als Beispiel. Angenommen, die beiden Seiten einer Münze sind gleichgewichtet. Wenn es einen Kopfgewinn von 2 Yuan und einen Schwanzverlust von 1 Yuan gibt, ist es offensichtlich, dass dies ein positives Erwartungsspiel ist. Die Gewinnrate ist 50% und ein Verlust von 2. Hier kommt die Frage: Jetzt, da Sie 100 Yuan haben, wie können Sie die Wette wiederholen, damit 100 Yuan mit der schnellsten Geschwindigkeit 1 Million Yuan erreichen können?

Wenn wir nicht genau darüber nachdenken, werden wir denken, dass, da die Rendite jeder Wette 50% * 2-50% * 1 ist, also 50%, dann, um die maximale Rendite schnell zu erzielen, sollten wir so viel Kapital wie möglich in jede Wette investieren.

Es ist jedoch offensichtlich, daß es unvernünftig ist, 100% des Kapitals in jedes Glücksspiel zu investieren, denn solange man einmal das Kapital verliert, wird es verloren gehen, auch wenn es sehr unwahrscheinlich ist.

Jemand mag fragen, da 100% Einsatz unvernünftig ist, was ist mit 90% oder niedrigerem Einsatz? Um dieses Problem zu lösen, können wir ein Experiment machen, um das Glücksspiel zu simulieren und zu sehen, wie das Ergebnis jeder Wette ist. Wie in der folgenden Tabelle gezeigt:

From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return

Aus dem Diagramm können wir sehen, dass, wenn wir die Position von 90%, 80%, 70%, 60% und 50% in demselben Glücksspiel allmählich reduzieren, die Ergebnisse völlig unterschiedlich sind.

Dann können einige Leute fragen, ob je kleiner die Wette jedes Mal ist, desto besser, z. B. 10%. Es ist unmöglich, jeden Wettanteil zu berechnen. Dies ist das Problem, das durch das berühmte Kelly-Kriterium gelöst werden muss. In der Statistik kann das Kelly-Kriterium die langfristige Wachstumsrate einer Strategie mit positiver Erwartung wiederholter Wetten maximieren und es kann die beste Wettquote in jeder Wette berechnen.

Nicht nur das, wenn man davon ausgeht, dass der Hauptbetrag und die Wette unendlich geteilt werden können, ist es unmöglich, bei jeder Wette durch die Verwendung des Kelly-Kriteriums bankrott zu gehen.

From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return

  • f ist die optimale Einsatzquote für das verfügbare Kapital;
  • b ist die Quotenquote, die auch als Gewinn/Verlustquote im Handel bezeichnet werden kann;
  • p ist die Erfolgsquote;
  • q ist die Ausfallrate.

Dann können wir das Glücksspielbeispiel in dieser Lektion nach dem Kelly-Kriterium berechnen. Das Anfangskapital von 100 Yuan kann mit der schnellsten Geschwindigkeit 1 Million Yuan erreichen, indem wir die Wettquote verwenden, wenn die Gewinnquote 50% ist und die Quoten 2 sind.

(0.5*(2+1) -1)/2=0.25

Die Gewinnquote von 50% beträgt 0,5. Die Quoten multiplizieren Sie mit 2 plus 1, dann subtrahieren Sie 1 und dann dividieren Sie durch 2. Das Berechnungsresultat beträgt 0,25. Das heißt, bei jeder Wette können Sie mit 25% des Hauptbetrags mit der schnellsten Geschwindigkeit 1 Million Yuan erreichen. Wir können manuell nach den Berechnungsresultaten simulieren, um zu sehen, ob es korrekt ist.

From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return

Die obige Abbildung ist das Ergebnis einer manuellen Simulation. Bitte beachten Sie die letzte Zeile. In derselben Wette erreichten 25% der Positionen nach mehr als 100 Runden zuerst 1 Million Yuan. Das Ergebnis von 90%, 80%, 70% und 60% der Positionen ist negativ, was zeigt, dass selbst eine positive Erwartungshandelsstrategie unter falschem Positionsmanagement bankrott geht.

Wir können auch sehen, dass 50% der Positionen am Ende nicht verlieren oder gewinnen, was auch mit dem Ergebnis des Gesetzes der großen Zahlen übereinstimmt. Um das Problem weiter zu veranschaulichen, wurde auch eine Position von 10% in der manuellen Simulation hinzugefügt. Obwohl das Endergebnis eine positive Rendite war, war der Effekt mehrere Größenordnungen schlechter als bei einer Position von 25%.

Sie können die Macht des Kelly-Kriteriums sehen. Wenn Sie 10% der Hauptposition in der tatsächlichen Anwendung wählen, wird Ihr Hauptbetrag in mehr als 100 Wetten mehr als 30.000 werden. Obwohl die Rendite groß ist, im Vergleich zu 25% der Hauptposition entspricht sie keinem Gewinn. Dies ist die Macht des Wissens.

Wenn Sie in Ihrem Leben mit dem Kelly-Kriterium profitieren möchten, müssen Sie die Anwendungsbedingungen des Kelly-Kriteriums erfüllen. Es besteht kein Zweifel, dass diese Wette vom Finanzmarkt kommen muss. Besonders im quantitativen Handel können wir die entsprechende Gewinnquote und Quoten durch historische Daten-Backtesting grob berechnen.

Natürlich kann die praktische Anwendung des Kelly-Kriteriums in Finanztransaktionen nicht so einfach sein, und es gibt viele Details, mit denen man sich befassen muss, wie die Kapitalkosten in Hebeltransaktionen, das Kapital und die Position in realen Transaktionen können nicht drahtlos geteilt werden, und die Gewinn- und Verlustquote in Transaktionen ändern sich dynamisch usw. Wie auch immer, das Kelly-Kriterium zeigt uns, wie wir die beste Positionsmanagementmethode festlegen können.


Inhalte dazu

Weitere Informationen