Hedging-Strategien sind sehr gute Praxisstrategien für Anfänger in der Strategieentwicklung.
Zunächst einmal ist klar, dass die zu entwerfende Strategie eine Kryptowährungs-Spot-Hedging-Strategie ist. Wir entwerfen die einfachste Hedging-Strategie. Wir verkaufen an der Börse mit dem höheren Preis nur zwischen den beiden Spot-Börsen und kaufen an der Börse mit dem niedrigeren Preis, um den Unterschied zu nehmen. Wenn die Börsen mit den höheren Preisen alle Münzen sind (weil die Münzen mit den höheren Preisen verkauft werden), und die Börsen mit den niedrigeren Preisen alle Münzen sind (die Münzen mit den niedrigeren Preisen werden gekauft), kann es nicht abgesichert werden. Zu diesem Zeitpunkt können wir nur warten, bis die Preisumkehr abgesichert ist.
Bei der Absicherung sind Preis und Menge des Auftrags durch den Austausch begrenzt, und es gibt auch eine Grenze für die Mindestbestellmenge. Zusätzlich zu der Mindestgrenze muss die Strategie bei der Absicherung auch das maximale Auftragsvolumen gleichzeitig berücksichtigen. Wenn das Auftragsvolumen zu groß ist, wird es nicht genug Auftragsvolumen geben. Es ist auch notwendig, zu prüfen, wie der Wechselkurs umgerechnet wird, wenn die beiden börsennotierten Münzen unterschiedlich sind. Bei der Absicherung sind die Bearbeitungsgebühr und der Rutsch des Auftragsnehmers alle Transaktionskosten, nicht solange es einen Preisunterschied gibt, der abgesichert werden kann. Daher hat der Absicherungspreisunterschied auch einen Auslöserwert. Wenn er unter einem bestimmten Preisunterschied liegt, verliert die Absicherung.
Auf der Grundlage dieser Überlegungen muss die Strategie mit mehreren Parametern konzipiert werden:
hedgeDiffPrice
, wenn die Differenz diesen Wert übersteigt, wird die Absicherungsaktion ausgelöst.minHedgeAmount
, der Mindestbestellbetrag (Münzen), der abgesichert werden kann.maxHedgeAmount
, der maximale Auftragsbetrag (Münzen) für eine Absicherung.pricePrecisionA
, die von der Börse A platzierte Orderpreisgenauigkeit (Anzahl der Dezimalstellen).amountPrecisionA
, die Höhe der Präzision der Bestellung der Börse A (Anzahl der Dezimalstellen).pricePrecisionB
, die von Börse B platzierte Orderpreisgenauigkeit (Anzahl der Dezimalstellen).amountPrecisionB
, die Höhe der Präzision der Bestellung der Börse B (Anzahl der Dezimalstellen).rateA
, die Wechselkursumrechnung des ersten hinzugefügten Wechselgegenstands, Standardwert 1, nicht umgerechnet.rateB
, die Wechselkursumrechnung des zweiten hinzugefügten Tauschgegenstands, Standardwert 1, nicht umgerechnet.Die Absicherungsstrategie muss die Anzahl der Münzen in den beiden Konten unverändert halten (d. h. keine Positionen in irgendeiner Richtung halten und Neutralität beibehalten), 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 keine abgeschlossene Bestellung gibt, müssen wir sie rechtzeitig stornieren, und die Bestellung kann nicht ausstehen bleiben.
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 Anzahl der Münzen ausgleichen, müssen wir den Preis finden, der auf eine bestimmte Anzahl von Münzen in einer bestimmten Tiefe angehäuft ist, also brauchen wir eine solche Funktion, um sie 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 für gleichzeitige Aufträge konzipiert werden muss:
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 kompliziert 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 the currency difference
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("unable to balance")
} else {
// balance order
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("errors:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
return true
} else {
return true
}
}
Nach der Gestaltung dieser Funktionen nach den Anforderungen der Strategie, dann beginnen, die Hauptfunktion der Strategie zu entwerfen.
Auf der FMZ-Plattform wird die Strategie vonmain
In den letzten Jahren hat sich die Zahl dermain
Funktion, müssen wir einige Initialisierung Arbeit der Strategie zu tun.
Name des Austauschobjekts Da viele Operationen in der Strategie die Austauschobjekte verwenden müssen, wie z.B. Marktnoten zu erhalten, Aufträge zu platzieren und so weiter.
var exA = exchanges[0]
var exB = exchanges[1]
Das macht es einfacher, später Code zu schreiben.
Wechselkurs, Präzisionskonstruktion
// precision, exchange rate settings
if (rateA != 1) {
// set exchange rate A
exA.SetRate(rateA)
Log("Exchange A sets the exchange rate:", rateA, "#FF0000")
}
if (rateB != 1) {
// set exchange rate B
exB.SetRate(rateB)
Log("Exchange B sets the exchange rate:", rateB, "#FF0000")
}
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
Wenn die WechselkursparameterrateA
, rateB
sind auf 1 festgelegt (Standard ist 1), d. h.rateA != 1
oderrateB != 1
wird nicht ausgelöst, so dass die Wechselkursumrechnung nicht festgelegt wird.
Alle Daten zurücksetzen
Manchmal ist es notwendig, alle Protokolle zu löschen und die aufgezeichneten Daten zu löschen, wenn die Strategie startet.isReset
, und entwerfen Sie den Reset-Code im Initialisierungsteil der Strategie, zum Beispiel:
if (isReset) { // When isReset is true, reset the data
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("reset all data", "#FF0000")
}
Wiederherstellung der ursprünglichen Kontodaten, Aktualisierung der Leistungskontodaten
Um den Saldo zu beurteilen, muss die Strategie die Ausgangssaldoaktivitäten kontinuierlich zur Vergleichung mit dem aktuellen Konto erfassen.nowAccs
wird verwendet, um die Leistungsbilanzdaten zu erfassen, mit der Funktion, die wir gerade entworfenupdateAccs
um die Konto-Daten der aktuellen Börse zu erhalten.initAccs
wird verwendet, um den ursprünglichen Kontozustand (Anzahl der Münzen, Anzahl der laufenden Münzen usw. an den Börsen A und B) zu erfassen.initAccs
, verwenden_G()
Funktion, um zuerst wiederherzustellen (die _G-Funktion wird Daten dauerhaft aufzeichnen, und kann die aufgezeichneten Daten wieder zurückgeben, siehe die API-Dokumentation für Details: [Link](https://www.fmz.com/api#_gk-v)), wenn die Abfrage nicht funktioniert, verwenden Sie die Leistungskontoinformationen, um den Wert zuzuordnen, und verwenden Sie die_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 Ausführungsrunde der Strategie-Logik, die immer wieder ausgeführt wird, um die Hauptschleife der Strategie zu bilden.
Erhalten von Marktdaten und Beurteilung der Gültigkeit von Marktdaten
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 wir sehen, dass die Gleichzeitfunktionexchange.Go
der FMZ-Plattform verwendet wird, um gleichzeitige Objekte zu erstellendepthARoutine
, depthBRoutine
Das nennen dieGetDepth()
Wenn diese beiden gleichzeitigen Objekte erstellt werden,GetDepth()
Die Interface wird sofort aufgerufen, und beide Anfragen für Tiefendaten werden an den Austausch gesendet.
Dann rufen Sie diewait()
Verfahren derdepthARoutine
, depthBRoutine
Objekte zur Erfassung der Tiefendaten.
Nach Erhalt der Tiefendaten ist es notwendig, die Tiefendaten zu überprüfen, um ihre Gültigkeit zu ermitteln.continue
Die Anweisung wird ausgelöst, um die Hauptschleife erneut auszuführen.
Verwendenspread value
Parameter oder diespread ratio
Parameter?
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
Wir haben eine solche Konstruktion für die Parameter gemacht: die Parameter des FMZ könnenSchauoderversteckenbasierend auf einem Parameter, so können wir einen Parameter zu entscheiden, ob zu verwendenprice spread
, oderspread ratio
.
Ein ParameterdiffAsPercentage
Die beiden anderen Parameter-Einstellungen, die anhand dieses Parameters angezeigt oder verschwiegen werden sollen, sind:hedgeDiffPrice@!diffAsPercentage
, die angezeigt wird, wenndiffAsPercentage
ist falsch.hedgeDiffPercentage@diffAsPercentage
, die angezeigt wird, wenndiffAsPercentage
Das ist wahr.
Nach diesem Entwurf haben wir diediffAsPercentage
Bei der Ermittlung der Risikopositionsquote wird die Risikopositionsquote in einem bestimmten Wert berechnet.diffAsPercentage
Der Wert des Wertpapiers, der in einem anderen Wertpapier verbucht wird, wird durch die Preisdifferenz ausgelöst.
Bestimmung der Absicherungsauslöser
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B market conditions are met
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("trigger A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // Tips
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 conditions are met
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("trigger B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // Tips
hedge(exA, exB, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Die Auslöserbedingungen für die Absicherung sind wie folgt:
isTrade
Hier wird die Variable, wenn die Absicherung ausgelöst wird, auftrue
. Und die globale Variable zurücksetzenlastKeepBalanceTS
Die Ausgleichszahlung wird auf 0 (lastKeepBalanceTS wird verwendet, um den Zeitstempel der letzten Ausgleichszahlung zu kennzeichnen, wenn sie auf 0 eingestellt wird, wird die Ausgleichszahlung sofort ausgelöst) und dann alle ausstehenden Aufträge storniert.Ausgleichsvorgang
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 kann festgestellt werden, daß die Ausgleichsfunktion periodisch ausgeführt wird, aber wenn dielastKeepBalanceTS
Der Gewinn wird nach erfolgreicher Ausgleichsberechnung berechnet.
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",
"current A, Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
"current B, Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
"initial A, Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
"initial B, Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
Die Statusleiste ist nicht besonders kompliziert gestaltet. Sie zeigt die aktuelle Zeit, den Preisunterschied von Börse A zu Börse B und den Preisunterschied von Börse B zu Börse A an. Und sie zeigt den aktuellen Hedge-Ziel-Spread, die Vermögensdaten des Börsenkontos A und des Börsenkontos B an.
In bezug auf die Parameter haben wir den Wertparameter für den Wechselkurs entworfen, und wir haben auch die Wechselkursumrechnung für den ersten Betrieb desmain
Die Strategie wird von der Kommission und den Mitgliedstaaten in Zusammenarbeit mit den Mitgliedstaaten durchgeführt.SetRate
Die Wechselkursumrechnung muss zuerst ausgeführt werden.
Weil diese Funktion zwei Aspekte betrifft:
BTC_USDT
, ist die PreiseinheitUSDT
, und die verfügbare Nennwährung in den Kontovermögen ist ebenfallsUSDT
. Wenn ich den Wert in CNY umwandeln möchte, setzenexchange.SetRate(6.8)
in dem Code, um die Daten zu konvertieren, die von allen Funktionen im Rahmen derexchange
Umtauschobjekt in CNY.
Um zu welcher Währung konvertieren, geben Sie inder Wechselkurs von der aktuellen Währung in der ZielwährungDieSetRate
function.Vollständige Strategie:Die Risikopositionen werden von den Risikopositionen gemäß Artikel 42 Absatz 1 Buchstabe c der CRR ausgegliedert.