In dem letzten Artikel haben wir die Handelslogikanalyse einer einfachen Gitterstrategie erklärt.
Analyse der Handelslogik Wie wir in dem letzten Artikel erwähnt haben, kann die Handelsaktion ausgelöst werden, solange Sie jede Gitterlinie des Gitterwerks durchqueren und beurteilen, dass der aktuelle Preis die Gitterlinie nach oben oder nach unten überquert.
Das erste Detail, das wir berücksichtigen müssen, ist das Design des unendlichen Rasters.createNet
Diese Funktion erzeugt eine Gitterdatenstruktur mit einer endlichen Anzahl von Gitterlinien. Was also, wenn der Preis beim Ausführen der Strategie über die Grenzen dieser Gitterdatenstruktur hinausgeht (über die obere Gitterlinie hinaus, wo der Preis am höchsten ist, und die untere Gitterlinie, wo der Preis am niedrigsten ist)?
Zuerst müssen wir also einen Erweiterungsmechanismus in die Netzwerkdatenstruktur integrieren.
Beginnen Sie mit dem Schreiben der Hauptfunktion der Strategie, und die Hauptfunktion ist der Code, in dem die Strategie beginnt, ausgeführt zu werden.
var diff = 50 // global variable, the grid interval, can be designed as parameter; for an easy explanation, we write it in an infinite loop
function main() {
// After the bot starts running, execute the strategy code from here
var ticker = _C(exchange.GetTicker) // obtain the latest ticker data in the market; for the ticker data structure, you can refer to FMZ API documentation: https://www.fmz.com/api#ticker
var net = createNet(ticker.Last, diff) // the function to initially construct the grid data structure we designed in the previous article; here we construct a grid data structure net
while (true) { // then, the program logic enters the while infinite loop, and the strategy will repeatedly execute the code within the {} symbol from here
ticker = _C(exchange.GetTicker) // the first line of the infinite loop code gets the latest market quote data, and updates it to the variable ticker
// check the grid range
while (ticker.Last >= net[net.length - 1].price) {
net.push({
buy : false,
sell : false,
price : net[net.length - 1].price + diff,
})
}
while (ticker.Last <= net[0].price) {
var price = net[0].price - diff
if (price <= 0) {
break
}
net.unshift({
buy : false,
sell : false,
price : price,
})
}
// the code is not completed...
}
}
Es ist der Code, der die Netzdatenstruktur erweiterbar macht (auszug aus dem obigen Code):
// check the grid range
while (ticker.Last >= net[net.length - 1].price) { // if the price exceeds the grid line with the highest price in the grid,
net.push({ // add a new grid line after the grid line with the highest price in the grid
buy : false, // initialize the sell mark
sell : false, // initialize the buy mark
price : net[net.length - 1].price + diff, // add a grid interval on the basis of the previous highest price
})
}
while (ticker.Last <= net[0].price) { // if the price is lower than the grid line with the lowest price in the grid,
var price = net[0].price - diff // distinguished from adding upwards, you should notice the price of the newly added grid line downwards cannot be less than or equal to 0, so you need to judge here
if (price <= 0) { // do not add when the price is less than or equal to 0, and break the loop
break
}
net.unshift({ // add a new grid line before the grid line with the lowest price in the grid
buy : false,
sell : false,
price : price,
})
}
Als nächstes müssen wir überlegen, wie wir den Handelsauslöser umsetzen.
var diff = 50
var amount = 0.002 // add a global variable, which can also be designed as a parameter; for easy explanation, we can write it in an infinite loop
// the parameter controls the trading amount on the grid line each time the trading is triggered
function main() {
var ticker = _C(exchange.GetTicker)
var net = createNet(ticker.Last, diff)
var preTicker = ticker // before the start of the main (infinite) loop, set a variable, to record the market quotes of last time
while (true) {
ticker = _C(exchange.GetTicker)
// check the grid range
while (ticker.Last >= net[net.length - 1].price) {
net.push({
buy : false,
sell : false,
price : net[net.length - 1].price + diff,
})
}
while (ticker.Last <= net[0].price) {
var price = net[0].price - diff
if (price <= 0) {
break
}
net.unshift({
buy : false,
sell : false,
price : price,
})
}
// index the grid
for (var i = 0 ; i < net.length ; i++) { // traverse all grid lines in the grid data structure
var p = net[i]
if (preTicker.Last < p.price && ticker.Last > p.price) { // upcross, and sell; when the current node has been traded, no matter SELL or BUY, no more trade
if (i != 0) {
var downP = net[i - 1]
if (downP.buy) {
exchange.Sell(-1, amount, ticker)
downP.buy = false
p.sell = false
continue
}
}
if (!p.sell && !p.buy) {
exchange.Sell(-1, amount, ticker)
p.sell = true
}
} else if (preTicker.Last > p.price && ticker.Last < p.price) { // downcross, and buy
if (i != net.length - 1) {
var upP = net[i + 1]
if (upP.sell) {
exchange.Buy(-1, amount * ticker.Last, ticker)
upP.sell = false
p.buy = false
continue
}
}
if (!p.buy && !p.sell) {
exchange.Buy(-1, amount * ticker.Last, ticker)
p.buy = true
}
}
}
preTicker = ticker // record the current market quotes in preTicker, which will be used as the "last" market quote data to compare with the new one in next loop, to judge upcross and downcross
Sleep(500)
}
}
Sie können sehen:
preTicker.Last < p.price && ticker.Last > p.price
preTicker.Last > p.price && ticker.Last < p.price
Das ist es, worüber im letzten Artikel gesprochen wurde:
Die Beurteilung von Upcross oder Downcross ist nur der erste Schritt, um zu bestimmen, ob Bestellung und Handel erfolgen sollen, und auch die Markierung in den Gitterlinie-Daten muss beurteilt werden.
Wenn es aufwärts ist, beurteilen Sie, ob der Preis niedriger ist als die aktuelle Gitterlinie sowie das Kaufzeichen auf der letzten Gitterlinie; wenn der Wert der Kaufzeichen wahr ist, bedeutet dies, dass die letzte Gitterlinie den Kauf ausgeführt hat und das Kaufzeichen der letzten Gitterlinie als falsch zurücksetzt und das Verkaufszeichen der aktuellen Gitterlinie als falsch zurücksetzt.
Wenn das Kaufzeichen und das Verkaufszeichen der aktuellen Netzlinie beide falsch sind, bedeutet dies, dass die aktuelle Netzlinie handeln kann; denn sie ist auf dem Kopf, hier führen wir die Verkaufsoperation aus und markieren die Verkaufsmarke der Netzlinie nach der Ausführung als wahr.
Die Logik der Verarbeitung des Downcross ist die gleiche. (Neulinge können sich das selbst überlegen.)
Um einige Daten während des Backtests zu sehen, muss eine FunktionshowTbl
wird geschrieben, um die Daten anzuzeigen.
function showTbl(arr) {
var tbl = {
type : "table",
title : "grid",
cols : ["grid information"],
rows : []
}
var arrReverse = arr.slice(0).reverse()
_.each(arrReverse, function(ele) {
var color = ""
if (ele.buy) {
color = "#FF0000"
} else if (ele.sell) {
color = "#00FF00"
}
tbl.rows.push([JSON.stringify(ele) + color])
})
LogStatus(_D(), "\n`" + JSON.stringify(tbl) + "`", "\n account information:", exchange.GetAccount())
}
Vollständiger Strategiecode:
/*backtest
start: 2021-04-01 22:00:00
end: 2021-05-22 00:00:00
period: 1d
basePeriod: 1m
exchanges: [{"eid":"OKEX","currency":"ETH_USDT","balance":100000}]
*/
var diff = 50
var amount = 0.002
function createNet(begin, diff) {
var oneSideNums = 10
var up = []
var down = []
for (var i = 0 ; i < oneSideNums ; i++) {
var upObj = {
buy : false,
sell : false,
price : begin + diff / 2 + i * diff,
}
up.push(upObj)
var j = (oneSideNums - 1) - i
var downObj = {
buy : false,
sell : false,
price : begin - diff / 2 - j * diff,
}
if (downObj.price <= 0) { // the price cannot be less than or equal to 0
continue
}
down.push(downObj)
}
return down.concat(up)
}
function showTbl(arr) {
var tbl = {
type : "table",
title : "grid",
cols : ["grid information"],
rows : []
}
var arrReverse = arr.slice(0).reverse()
_.each(arrReverse, function(ele) {
var color = ""
if (ele.buy) {
color = "#FF0000"
} else if (ele.sell) {
color = "#00FF00"
}
tbl.rows.push([JSON.stringify(ele) + color])
})
LogStatus(_D(), "\n`" + JSON.stringify(tbl) + "`", "\n account information:", exchange.GetAccount())
}
function main() {
var ticker = _C(exchange.GetTicker)
var net = createNet(ticker.Last, diff)
var preTicker = ticker
while (true) {
ticker = _C(exchange.GetTicker)
// check the grid range
while (ticker.Last >= net[net.length - 1].price) {
net.push({
buy : false,
sell : false,
price : net[net.length - 1].price + diff,
})
}
while (ticker.Last <= net[0].price) {
var price = net[0].price - diff
if (price <= 0) {
break
}
net.unshift({
buy : false,
sell : false,
price : price,
})
}
// index grid
for (var i = 0 ; i < net.length ; i++) {
var p = net[i]
if (preTicker.Last < p.price && ticker.Last > p.price) { // upcross, and sell; when the current node has been traded, no matter SELL or BUY, no more trade
if (i != 0) {
var downP = net[i - 1]
if (downP.buy) {
exchange.Sell(-1, amount, ticker)
downP.buy = false
p.sell = false
continue
}
}
if (!p.sell && !p.buy) {
exchange.Sell(-1, amount, ticker)
p.sell = true
}
} else if (preTicker.Last > p.price && ticker.Last < p.price) { // downcross, and buy
if (i != net.length - 1) {
var upP = net[i + 1]
if (upP.sell) {
exchange.Buy(-1, amount * ticker.Last, ticker)
upP.sell = false
p.buy = false
continue
}
}
if (!p.buy && !p.sell) {
exchange.Buy(-1, amount * ticker.Last, ticker)
p.buy = true
}
}
}
showTbl(net)
preTicker = ticker
Sleep(500)
}
}
Strategie-Backtest:
Sie können die Eigenschaften der Netzstrategie sehen. Wenn es einen Trendmarkt gibt, wird es einen großen schwimmenden Verlust geben, und die Rendite wird in einem volatilen Markt zurückkehren. Daher ist die Netzstrategie nicht risikofrei, die Spotstrategie kann sich immer noch halten, um sie auszugleichen, während die Netzstrategie von Futures-Kontrakten riskanter ist und die Netzparameter konservativ festlegen muss.