Für Anfänger in der Strategieentwicklung ist die Hedge-Strategie sehr gut für die Praxis.
Zunächst einmal müssen wir sicherstellen, dass die zu entwerfende Strategie eine Kryptowährungs-Spot-Hedging-Strategie ist. Wir entwerfen die einfachste Hedging-Strategie. Wir verkaufen nur auf der Plattform mit dem höheren Preis zwischen den beiden Spot-Plattformen und kaufen auf der Plattform mit dem niedrigeren Preis, um den Preis-Spread zu verdienen. Wenn die Plattform mit dem höheren Preis voller Quote-Währungs-Symbole ist (weil der Preis hoch ist, werden alle Währungs-Symbole verkauft), oder wenn die Plattform mit dem niedrigeren Preis voller Währungs-Symbole ist (weil der Preis niedrig ist, Währungs-Symbole werden von allen Vermögenswerten gekauft), kann sie nicht abgesichert werden. Zu diesem Zeitpunkt können Sie nur warten, bis der Preis umgekehrt ist, um zu sichern.
Für den Bestellpreis und die Menge während der Absicherung gibt es in jeder Plattform Präzisionsgrenzen, und es gibt auch eine Grenze für den Mindestbestellbetrag. Zusätzlich zum Mindestlimit muss die Strategie auch die maximale Bestellmenge für eine Absicherung berücksichtigen. Wenn die Bestellmenge zu groß ist, hat der Markt dafür kein ausreichendes Auftragsvolumen. Es ist auch notwendig, zu prüfen, wie der Wechselkurs umgerechnet wird, wenn die beiden Plattformen unterschiedliche Angebotskursen haben. Die Handlinggebühr während der Absicherung und der Rutsch des Auftragnehmers sind alle Handelskosten. Die Absicherung geschieht nicht immer, solange es einen Preisunterschied gibt. Daher hat die Absicherungspreisbreite auch einen Auslöserwert. Wenn sie niedriger ist als ein bestimmter Preisspreis, wird die Absicherung einen Verlust machen.
Auf dieser Grundlage muss die Strategie mit mehreren Parametern konzipiert werden:
hedgeDiffPrice
Wenn der Spread den Wert übersteigt, wird eine Absicherung ausgelöst.minHedgeAmount
, der für eine Absicherung verfügbare Mindestauftragsbetrag (Symbolbetrag).maxHedgeAmount
, der für eine Absicherung verfügbare maximale Auftragsbetrag (Symbolbetrag).pricePrecisionA
, die Präzision des Auftragspreises (Dezimalzahlen) der Plattform A.amountPrecisionA
, die Auftragsbetragsgenauigkeit (Dezimalstellen) der Plattform A.pricePrecisionB
, die Präzision des Auftragspreises (Dezimalzahlen) der Plattform B.amountPrecisionB
, die Auftragsbetragsgenauigkeit (Dezimalzahlen) der Plattform B.rateA
, der Wechselkurs der Umrechnung des ersten hinzugefügten Tauschgegenstands; Standardwert 1 bedeutet, dass keine Umrechnung erfolgt.rateB
, der Umrechnungskurs des zweiten hinzugefügten Tauschgegenstandes; Standardwert 1 bedeutet, dass keine Umrechnung erfolgt.Die Absicherungsstrategie muss den Währungssymbolbetrag der beiden Konten unverändert halten (d. h. keine Richtpositionen halten und neutral bleiben), so dass eine Balance-Logik in der Strategie vorhanden sein muss, um immer das Gleichgewicht zu erkennen.
function updateAccs(arrEx) {
var ret = []
for (var i = 0 ; i < arrEx.length ; i++) {
var acc = arrEx[i].GetAccount()
if (!acc) {
return null
}
ret.push(acc)
}
return ret
}
Nach der Auftragserteilung, wenn es keinen ausgeführten Auftrag gibt, müssen wir ihn rechtzeitig stornieren, und der Auftrag kann nicht in Erwartung gehalten werden.
function cancelAll() {
_.each(exchanges, function(ex) {
while (true) {
var orders = _C(ex.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0 ; i < orders.length ; i++) {
ex.CancelOrder(orders[i].Id, orders[i])
Sleep(500)
}
}
})
}
Wenn wir die Menge der Währungssymbole ausgleichen, müssen wir den Preis mit einer bestimmten Menge in einer bestimmten Tiefe Daten finden, also brauchen wir eine Funktion wie diese, um es zu handhaben.
function getDepthPrice(depth, side, amount) {
var arr = depth[side]
var sum = 0
var price = null
for (var i = 0 ; i < arr.length ; i++) {
var ele = arr[i]
sum += ele.Amount
if (sum >= amount) {
price = ele.Price
break
}
}
return price
}
Dann müssen wir die spezifische Absicherungsorder-Operation entwerfen und schreiben, die entworfen werden muss, um gleichzeitig Aufträge zu platzieren:
function hedge(buyEx, sellEx, price, amount) {
var buyRoutine = buyEx.Go("Buy", price, amount)
var sellRoutine = sellEx.Go("Sell", price, amount)
Sleep(500)
buyRoutine.wait()
sellRoutine.wait()
}
Abschließend wollen wir das Design der Balance-Funktion, die etwas komplizierter ist, abschließen.
function keepBalance(initAccs, nowAccs, depths) {
var initSumStocks = 0
var nowSumStocks = 0
_.each(initAccs, function(acc) {
initSumStocks += acc.Stocks + acc.FrozenStocks
})
_.each(nowAccs, function(acc) {
nowSumStocks += acc.Stocks + acc.FrozenStocks
})
var diff = nowSumStocks - initSumStocks
// calculate currency spread
if (Math.abs(diff) > minHedgeAmount && initAccs.length == nowAccs.length && nowAccs.length == depths.length) {
var index = -1
var available = []
var side = diff > 0 ? "Bids" : "Asks"
for (var i = 0 ; i < nowAccs.length ; i++) {
var price = getDepthPrice(depths[i], side, Math.abs(diff))
if (side == "Bids" && nowAccs[i].Stocks > Math.abs(diff)) {
available.push(i)
} else if (price && nowAccs[i].Balance / price > Math.abs(diff)) {
available.push(i)
}
}
for (var i = 0 ; i < available.length ; i++) {
if (index == -1) {
index = available[i]
} else {
var priceIndex = getDepthPrice(depths[index], side, Math.abs(diff))
var priceI = getDepthPrice(depths[available[i]], side, Math.abs(diff))
if (side == "Bids" && priceIndex && priceI && priceI > priceIndex) {
index = available[i]
} else if (priceIndex && priceI && priceI < priceIndex) {
index = available[i]
}
}
}
if (index == -1) {
Log("cannot balance")
} else {
// balanced ordering
var price = getDepthPrice(depths[index], side, Math.abs(diff))
if (price) {
var tradeFunc = side == "Bids" ? exchanges[index].Sell : exchanges[index].Buy
tradeFunc(price, Math.abs(diff))
} else {
Log("invalid price", price)
}
}
return false
} else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
Log("error:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
return true
} else {
return true
}
}
Diese Funktionen wurden nach den Anforderungen der Strategie konzipiert, und wir können mit der Konzeption der Hauptfunktion der Strategie beginnen.
Auf FMZ wird die Strategie von dermain
In den letzten Jahren hat sich die Zahl dermain
Funktion, müssen wir einige Initialisierung der Strategie zu tun.
Name des Exchange-Objekts Für viele Operationen in der Strategie verwenden Austauschobjekte, wie zum Beispiel das Erhalten von Marktnoten, Platzieren von Aufträgen, und so weiter, so wäre es unbequem, einen längeren Namen jedes Mal zu verwenden, mein kleiner Trick ist, einen einfachen kurzen Namen stattdessen zu verwenden, zum Beispiel:
var exA = exchanges[0]
var exB = exchanges[1]
Dann wird es bequemer sein, den Code später zu schreiben.
Wechselkurs und Präzision
// settings of precision and exchange rate
if (rateA != 1) {
// set exchange rate A
exA.SetRate(rateA)
Log("Platform A sets exchange rate:", rateA, "#FF0000")
}
if (rateB != 1) {
// set exchange rate B
exB.SetRate(rateB)
Log("Platform B sets exchange rate:", rateB, "#FF0000")
}
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
Wenn einer der Wechselkursparameter, nämlichrateA
undrateB
, wird auf 1 gesetzt (Standard ist 1), d. h.rateA != 1
oderrateB != 1
Der Wechselkurs kann nicht umgerechnet werden.
Alle Daten zurücksetzen
Manchmal ist es notwendig, alle Protokolle zu löschen und die Datensätze zu säubern, wenn die Strategie gestartet wird.isReset
, und dann den Reset-Code im Initialisierungsteil der Strategie entwerfen, zum Beispiel:
if (isReset) { // when "isReset" is true, reset the data
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("Reset all data", "#FF0000")
}
Wiederherstellen der ursprünglichen Kontodaten und Aktualisieren der aktuellen Kontodaten
Um den Saldo zu beurteilen, muss die Strategie die Ausgangszustand des Kontovermögens kontinuierlich für den Vergleich mit dem aktuellen Konto erfassen.nowAccs
Die Daten für das Leistungsbilanzkonto werden mit demupdateAccs
Funktion, die wir gerade entwickelt haben, um die Konto-Daten der aktuellen Plattform zu erhalten.initAccs
wird verwendet, um den ursprünglichen Kontozustand zu erfassen (Daten wie Währungssymbolbetrag sowohl für A als auch für B, Kurswährungssatz usw.).initAccs
, verwenden Sie zunächst die_G()
Funktion zur Wiederherstellung (die _G-Funktion wird Daten dauerhaft aufzeichnen und kann die aufgezeichneten Daten erneut zurückgeben; lesen Sie die API-Dokumentation für Details:Verbindung).
Wenn Sie die Daten nicht abfragen können, verwenden Sie die Leistungskontoinformationen zuzuweisen und zu verwenden_G()
Funktion zum Aufzeichnen.
Der folgende Code:
var nowAccs = _C(updateAccs, exchanges)
var initAccs = _G("initAccs")
if (!initAccs) {
initAccs = nowAccs
_G("initAccs", initAccs)
}
Der Code in der Hauptschleife ist der Prozess jeder Runde der Strategie-Logik-Ausführung, und die non-stop wiederholende Ausführung konstruiert die Strategie-Hauptschleife.
Erhalten Sie die Marktzitate und beurteilen Sie die Gültigkeit
var ts = new Date().getTime()
var depthARoutine = exA.Go("GetDepth")
var depthBRoutine = exB.Go("GetDepth")
var depthA = depthARoutine.wait()
var depthB = depthBRoutine.wait()
if (!depthA || !depthB || depthA.Asks.length == 0 || depthA.Bids.length == 0 || depthB.Asks.length == 0 || depthB.Bids.length == 0) {
Sleep(500)
continue
}
Hier können Sie sehen, dass die gleichzeitige Funktionexchange.Go
der FMZ-Plattform verwendet wird, um gleichzeitige Objekte zu erstellendepthARoutine
unddepthBRoutine
Das nennen dieGetDepth()
Wenn diese beiden gleichzeitigen Objekte erstellt werden,GetDepth()
Die Anforderung der Tiefendaten wird sofort an die Plattform gesendet.
Dann rufen Sie diewait()
Methode des ObjektsdepthARoutine
und ObjektdepthBRoutine
Um die Tiefendaten zu erhalten.
Nach Erhalt der Tiefendaten ist es notwendig, die Tiefendaten zu überprüfen, um deren Gültigkeit zu beurteilen.continue
Die Anweisung wird ausgelöst, um die Hauptschleife erneut auszuführen.
Verwendungprice spread
oderspread ratio
?
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
Die Parameter von FMZ könnenSchauoderversteckenbasierend auf einem Parameter, so können wir einen Parameter zu entscheiden, ob zu verwendenprice spread
, oderspread ratio
.
Der ParameterdiffAsPercentage
Die beiden anderen Parameter, die je nach Parameter angezeigt oder ausgeblendet werden, werden so eingestellt:hedgeDiffPrice@!diffAsPercentage
; wenndiffAsPercentage
ist falsch, wird gezeigt.hedgeDiffPercentage@diffAsPercentage
; wenndiffAsPercentage
ist wahr, wird es angezeigt.
Nach dem Entwurf haben wir diediffAsPercentage
Die Risikopositionsquote ist eine Parameterfunktion, bei der die Spread-Ratio als Auslöserbedingungen für die Absicherung verwendet wird.diffAsPercentage
Der Parameter wird nicht geprüft, wird der Kursspread als Triggerbedingung für die Absicherung verwendet.
Richter Hedge Trigger
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B market condition satisfied
var price = (depthA.Bids[0].Price + depthB.Asks[0].Price) / 2
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance / price, maxHedgeAmount)
Log("triggerA->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // prompt message
hedge(exB, exA, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPrice && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A market condition satisfied
var price = (depthB.Bids[0].Price + depthA.Asks[0].Price) / 2
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance / price, maxHedgeAmount)
Log("triggerB->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // prompt message
hedge(exA, exB, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Es gibt mehrere Auslösebedingungen für die Absicherung: 1.Zunächst ist die Absicherungsspanne zu erfüllen; die Absicherung ist nur möglich, wenn die Marktspannen den festgelegten Parameter der Absicherungsspanne erfüllen.
2.Der Sicherungsbetrag des Marktes sollte dem in den Parametern festgelegten Mindestsicherungsbetrag entsprechen.Da der Mindestauftragssatz verschiedener Plattformen unterschiedlich ist, sollte der kleinste der beiden platziert werden.
3.Die Vermögenswerte in der Plattform mit der Verkaufsoperation sind ausreichend zum Verkaufen und die Vermögenswerte in der Plattform mit der Kaufoperation sind ausreichend zum Kaufen.Wenn diese Bedingungen erfüllt sind, wird die Absicherungsfunktion ausgeführt, um Aufträge nach Absicherung zu platzieren.isTrade
Hier, wenn die Absicherung ausgelöst wird, wird die Variable auftrue
. Und die globale Variable zurücksetzenlastKeepBalanceTS
Die Ausgabe von LastKeepBalanceTS wird verwendet, um den Zeitstempel der letzten Balance-Operation zu kennzeichnen, und wenn sie auf 0 gesetzt wird, wird die Balance-Operation sofort ausgelöst.
Ausgleichsbetrieb
if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) {
nowAccs = _C(updateAccs, exchanges)
var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB])
cancelAll()
if (isBalance) {
lastKeepBalanceTS = ts
if (isTrade) {
var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs)
isTrade = false
}
}
}
Es ist zu sehen, daß die Balancefunktion periodisch ausgeführt wird, aber wenn dielastKeepBalanceTS
Wenn der Wert der Risikoposition nach Auslösung der Absicherungsoperation auf 0 zurückgesetzt wird, wird die Saldooperation sofort ausgelöst.
Informationen aus der Statusleiste
LogStatus(_D(), "A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, " B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, " targetDiffPrice:", targetDiffPrice, "\n",
"currentA,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
"currentB,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
"initialA,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
"initialB,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
Die Statusleiste ist nicht besonders kompliziert konzipiert. Sie zeigt die aktuelle Uhrzeit, den Preis-Spread von Plattform A auf Plattform B sowie den Preis-Spread von Plattform B auf Plattform A an; sie zeigt auch den aktuellen Hedge-Ziel-Spread, die Kontoanlagendaten von Plattform A und die Kontoanlagendaten von Plattform B an.
In bezug auf die Parameter haben wir den Parameter der Umrechnung des Wechselkurswerts entworfen, und wir haben auch die Umrechnung des Wechselkurses immain
Die Strategie wird von der Kommission und den Mitgliedstaaten in Zusammenarbeit mit den Mitgliedstaaten durchgeführt.SetRate
Die Wechselkursumrechnung muss zuerst ausgeführt werden.
Für die Funktion werden zwei Aspekte betroffen sein:
Zum Beispiel ist das aktuelle HandelspaarBTC_USDT
, ist die PreiseinheitUSDT
, und die verfügbare Kurswährung in den Kontovermögen ist ebenfallsUSDT
. Wenn ich den Wert der Vermögenswerte in CNY umwandeln möchte, setzenexchange.SetRate(6.8)
in dem Code, um die Daten zu konvertieren, die von allen Funktionen im Rahmen derexchange
Gegenstand und dann in CNY umgerechnet.
Um zu welcher Währung zu konvertieren, importierender Wechselkurs von der aktuellen Kurswährung zur Zielkurswährungin dieSetRate
function.
Vollständige Strategie:Strategie zur Spot-Besicherung verschiedener Notenwährungen (Teaching)