Bagi para pemula dalam Perdagangan Kuantitatif di Dunia Kripto, silakan lihat ini - Membawa Anda Lebih Dekat ke Perdagangan Kuantitatif di Dunia Kripto (V)
Pada artikel sebelumnya, kami menjelaskan analisis logika perdagangan dari strategi grid sederhana. Pada artikel ini, kami akan melanjutkan untuk melengkapi desain strategi pengajaran ini.
-
Analisis logika transaksi
Pada artikel sebelumnya, kami sebutkan bahwa selama kita melintasi setiap garis kisi dan menentukan apakah harga saat ini melintasi di atas atau di bawah garis kisi, tindakan transaksi dapat dipicu. Namun pada kenyataannya, masih banyak detail yang logis. Pemula yang tidak mengerti penulisan strategi sering kali membentuk persepsi yang salah bahwa "logikanya sangat sederhana, dan kodenya seharusnya hanya beberapa baris, tetapi dalam penulisan sebenarnya, masih ada banyak detail."Rincian pertama yang perlu kita pertimbangkan adalah desain jaringan tak terbatas. Ingat pada artikel terakhir kita merancang fungsi untuk menghasilkan struktur data grid awal
createNetApa? Fungsi ini menghasilkan struktur data grid dengan jumlah garis grid yang terbatas. Lalu bagaimana jika harga melampaui batas struktur data grid ini (melampaui garis grid teratas dengan harga tertinggi dan garis grid terbawah dengan harga terendah) saat strategi sedang berjalan?
Jadi pertama-tama kita perlu menambahkan mekanisme ekstensi ke struktur data grid.Mulailah menulis fungsi utama strategi, yang merupakan kode yang memulai eksekusi strategi.
var diff = 50 // 全局变量,网格间距,可以设计成参数,方便讲解,我们把这个参数写死在代码里。 function main() { // 实盘开始运行后,从这里开始执行策略代码 var ticker = _C(exchange.GetTicker) // 获取市场最新的行情数据ticker,ticker这个数据的结构参看FMZ API文档:https://www.fmz.com/api#ticker var net = createNet(ticker.Last, diff) // 我们上篇设计的初始构造网格数据结构的函数,这里构造一个网格数据结构net while (true) { // 然后程序逻辑就进入了这个while死循环,策略执行到此将不停的循环执行这里{}符号之内的代码 ticker = _C(exchange.GetTicker) // 死循环代码部分的第一行,获取最新的行情数据,更新给ticker变量 // 检查网格范围 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, }) } // 还有其它代码... } }Kode yang membuat struktur data grid dapat diperluas adalah ini (dikutip dari kode di atas):
// 检查网格范围 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 // 区别于向上添加,要注意向下添加新网格线的价格不能小于等于0,所以这里要判断 if (price <= 0) { // 小于等于0就不添加了,跳出这层循环 break } net.unshift({ // 就在网格最低价格的网格线之前添加一个新的网格线 buy : false, sell : false, price : price, }) }Langkah berikutnya adalah mempertimbangkan cara menerapkan pemicu transaksi secara rinci.
var diff = 50 var amount = 0.002 // 增加一个全局变量,也可以设计成参数,当然为了简便讲解,我们也写死在策略代码, // 这个参数控制每次网格线上触发交易时的交易量 function main() { var ticker = _C(exchange.GetTicker) var net = createNet(ticker.Last, diff) var preTicker = ticker // 在主循环(死循环)开始前,设置一个变量,记录上一次的行情数据 while (true) { ticker = _C(exchange.GetTicker) // 检查网格范围 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, }) } // 检索网格 for (var i = 0 ; i < net.length ; i++) { // 遍历网格数据结构中的所有网格线 var p = net[i] if (preTicker.Last < p.price && ticker.Last > p.price) { // 上穿,卖出,当前节点已经交易过不论SELL BUY ,都不再交易 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) { // 下穿,买入 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 // 把当前的行情数据记录在preTicker中,在下一次循环中,作为“上一次”行情数据和最新的对比,判断上穿下穿 Sleep(500) } }Anda dapat melihat:
- Kondisi melintasi garis grid:
preTicker.Last < p.price && ticker.Last > p.price - Kondisi melintasi garis grid:
preTicker.Last > p.price && ticker.Last < p.price
Inilah yang kita bicarakan di artikel sebelumnya:
Menyilangkan ke atas atau ke bawah hanyalah langkah pertama dalam menentukan apakah perdagangan dapat dilakukan, yang juga memerlukan penentuan tanda pada data garis kisi.
Jika terjadi persilangan ke atas, harga dinilai lebih rendah dari garis grid saat ini dan tanda beli pada garis grid terdekat. Jika nilai tanda beli benar, berarti garis grid sebelumnya telah dibeli, dan garis grid sebelumnya disetel ulang. Tanda beli akar adalah salah, dan tanda jual garis grid saat ini disetel ulang menjadi salah.
Setelah menilai kondisi sebelumnya, jika tidak dipicu, lanjutkan penilaian. Jika tanda beli/jual pada garis grid saat ini keduanya salah, itu berarti garis grid saat ini dapat diperdagangkan. Karena merupakan persilangan ke atas, kita jalankan operasi penjualan di sini. Setelah eksekusi, tandai tanda jual pada garis grid saat ini sebagai benar.
Logika untuk menangani underpass adalah sama (ini terserah pemula untuk memikirkannya).
- Kondisi melintasi garis grid:
Pengujian ulang strategi lengkap
Untuk melihat beberapa data backtesting, sebuah fungsi ditulisshowTblMenampilkan data.
function showTbl(arr) {
var tbl = {
type : "table",
title : "网格",
cols : ["网格信息"],
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 账户信息:", exchange.GetAccount())
}
Kode strategi lengkap:
/*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) { // 价格不能小于等于0
continue
}
down.push(downObj)
}
return down.concat(up)
}
function showTbl(arr) {
var tbl = {
type : "table",
title : "网格",
cols : ["网格信息"],
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 账户信息:", exchange.GetAccount())
}
function main() {
var ticker = _C(exchange.GetTicker)
var net = createNet(ticker.Last, diff)
var preTicker = ticker
while (true) {
ticker = _C(exchange.GetTicker)
// 检查网格范围
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,
})
}
// 检索网格
for (var i = 0 ; i < net.length ; i++) {
var p = net[i]
if (preTicker.Last < p.price && ticker.Last > p.price) { // 上穿,卖出,当前节点已经交易过不论SELL BUY ,都不再交易
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) { // 下穿,买入
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)
}
}
Pengujian Ulang Strategi:
Dapat dilihat bahwa karakteristik strategi grid ialah akan terjadi kerugian mengambang yang besar ketika terjadi pasar yang sedang tren, dan keuntungan hanya akan pulih dalam pasar yang bergejolak.
Oleh karena itu, strategi grid tidak bebas risiko. Strategi spot masih dapat dipertahankan, tetapi strategi grid kontrak berjangka lebih berisiko dan memerlukan pengaturan konservatif untuk parameter grid.
为啥判断上穿下穿条件的时候,每根网格线都要判断啊,这里面感觉有个逻辑漏洞啊,不应该是上穿卖出的时候只要遍历高于目前价格的网格线吗? 还有exchange.Sell(-1, amount, ticker)这个函数怎么和api文档里的不一样啊,我看api文档里写的是exchange.Sell(Price, Amount),为啥你有三个参数啊,搞不懂啊,好复杂啊,我人都晕了~
FMZ的API函数中可以产生日志输出的函数例如:Log(...)、exchange.Buy(Price, Amount)、exchange.CancelOrder(Id)等都可以在必要参数后跟一些附带输出参数。https://www.fmz.com/api#exchange.cancelorderid
上穿和下跌时,exchange.Buy(-1, amount * ticker.Last, ticker),amount*ticker.Last是啥意思,为啥sell没有呢?
https://www.fmz.com/strategy/291160
last_tick = []
line = []
grid_buy_list = []
def net(now_price):
global line
print(now_price)
line = [now_price*(1+0.003*i) for i in range(-1000,1000)]
Log(line)
def ontick():
global last_tick
global line
global grid_buy_list
account = exchange.GetAccount()
ticker = exchange.GetTicker()
last_tick.append(ticker['Last'])
if len(last_tick) == 1:return
elif len(last_tick) == 100:del last_tick[0]
for i in range(len(line)):
if last_tick[-1] > line[i] and last_tick[-2] < line[i] and len(grid_buy_list)!= 0 and i > min(grid_buy_list) and account['Stocks'] >= 0.001:
exchange.Sell(last_tick[-1],0.01)
del grid_buy_list[grid_buy_list.index(min(grid_buy_list))]
Log(exchange.GetAccount())
elif last_tick[-1] < line[i] and last_tick[-2] > line[i] and i not in grid_buy_list:
exchange.Buy(last_tick[-1],0.01)
grid_buy_list.append(i)
Log(exchange.GetAccount())
def main():
net(exchange.GetTicker()['Last'])
Log(exchange.GetAccount())
while(True):
ontick()
Sleep(1000)
- 1






