
Pernahkah anda terfikir bahawa anda boleh memulakan dagangan kuantitatif dengan mudah dan mula serta-merta tanpa perlu berjaga sepanjang malam menulis kod untuk membina rangka kerja, mereka bentuk UI, dan pelbagai butiran reka bentuk serta mekanisme sendiri? Segala-galanya menjadi mungkin pada platform kuantitatif FMZ. Anda tidak memerlukan latar belakang pengaturcaraan lanjutan, dan anda juga tidak perlu risau tentang proses penggunaan yang rumit - anda hanya perlukan komputer dan akaun untuk memulakan perjalanan kuantitatif “pergi ke mana-mana” anda. Artikel ini akan membawa anda dari awal, mulakan dengan pantas dengan FMZ, alami daya tarikan perdagangan automatik dan menggunakan data serta strategi untuk menguasai rentak pasaran. Sama ada anda seorang pemula atau veteran yang ingin meningkatkan kecekapan, perjalanan ini patut dicuba.
Saya sering berkomunikasi dan berbual dengan pemula platform. Pemula perdagangan kuantitatif biasanya keliru dengan proses reka bentuk yang lengkap. Apabila saya mempunyai idea perdagangan, saya sering tidak tahu di mana untuk bermula dan berasa terharu.
Keliru tentang:
Mari kita selesaikan kekeliruan di atas bersama-sama.
Dalam dunia perdagangan kuantitatif, reka bentuk strategi selalunya merupakan perjalanan penerokaan tanpa penghujung. Anda mungkin telah cuba menulis penunjuk, atau cuba mengikuti isyarat beli dan jual secara membuta tuli, tetapi yang benar-benar boleh pergi jauh ialah sistem strategi yang boleh “kelihatan, boleh laras dan stabil.” Berdasarkan platform kuantitatif FMZ, anda boleh mempunyai pengalaman praktikal “menepati masa”. Bina strategi mudah, daripada tetapan parameter, paparan carta, kepada fungsi interaktif dan pengiraan untung rugi, untuk memenuhi sepenuhnya keperluan reka bentuk sesuatu strategi.
Idea strategi ialah strategi peningkatan kedudukan langkah demi langkah berdasarkan ATR, logik pembinaan kedudukan grid langkah demi langkah (dua arah panjang dan pendek), pengiraan turun naik penyesuaian ATR dan logik pembubaran kedudukan (apabila pasaran berbalik kepada paksi pusat).
Strategi ini adalah berdasarkan keperluan reka bentuk berikut:
Sediakan dua tatasusunan untuk mengawal peningkatan kedudukan secara beransur-ansur.
var arrUp = null
var arrDown = null
Setiap kali anda menambah kedudukan, maklumat kedudukan ditolak ke dalam tatasusunan, yang memudahkan untuk mengawal kedudukan dan memaparkan data pada antara muka masa nyata strategi.
Buka dan tutup kedudukan mengikut tahap penembusan harga. Demi kesederhanaan, kedua-dua kedudukan pembukaan dan penutupan menggunakan pesanan pasaran, yang mudah dan berkesan.
if (close > up && i >= arrUp.length && !isPaused) {
var id = exchange.CreateOrder(symbol, "sell", -1, tradeAmount)
if (!id) {
Log("下单失败")
continue
}
arrUp.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
_G("arrUp", arrUp)
arrSignal.push([r[r.length - 1].Time, "short", close, tradeAmount])
Log([r[r.length - 1].Time, "short", close, tradeAmount], "@")
} else if (close < down && i >= arrDown.length && !isPaused) {
var id = exchange.CreateOrder(symbol, "buy", -1, tradeAmount)
if (!id) {
Log("下单失败")
continue
}
arrDown.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
_G("arrDown", arrDown)
arrSignal.push([r[r.length - 1].Time, "long", close, tradeAmount])
Log([r[r.length - 1].Time, "long", close, tradeAmount], "@")
} else if (((arrUp.length > 0 && close < mid) || (arrDown.length > 0 && close > mid)) && !isPaused) {
clear(pos, r)
}
Kosongkan inventori dan gunakan fungsi untuk mengendalikannya. Sesetengah struktur data perlu ditetapkan semula setiap kali inventori dikosongkan, jadi fungsi pembersihan perlu dirangkumkan ke dalam fungsi untuk digunakan semula dalam modul interaktif.
function clear(positions, r) {
var close = r[r.length - 1].Close
for (var p of positions) {
if (p.Type == PD_LONG) {
var id = exchange.CreateOrder(symbol, "closebuy", -1, p.Amount)
if (!id) {
Log("下单失败")
continue
}
arrSignal.push([r[r.length - 1].Time, "closelong", close, p.Amount])
Log([r[r.length - 1].Time, "closelong", close, p.Amount], "@")
} else if (p.Type == PD_SHORT) {
var id = exchange.CreateOrder(symbol, "closesell", -1, p.Amount)
if (!id) {
Log("下单失败")
continue
}
arrSignal.push([r[r.length - 1].Time, "closeshort", close, p.Amount])
Log([r[r.length - 1].Time, "closeshort", close, p.Amount], "@")
}
}
arrUp = []
arrDown = []
_G("arrUp", arrUp)
_G("arrDown", arrDown)
var profit = calcProfit()
LogProfit(profit)
}
Ia dibahagikan kepada pelbagai peringkat, dan tahap maksimum ialah: maxNisbah. Setiap peringkat mengira ambang harga yang berbeza.
for (var i = 0; i < maxRatio; i++) {
var up = open + atr[atr.length - 1] * (i + 1)
var mid = open
var down = open - atr[atr.length - 1] * (i + 1)
atrs.push([open, (i + 1), atr])
var tradeAmount = baseAmount * Math.pow(2, i)
if (isAmountForUSDT) {
tradeAmount = tradeAmount * 1.05 / close
}
tradeAmount = _N(tradeAmount, amountPrecision)
var balance = acc.Balance
if (balance - initAcc.Equity * reserve < tradeAmount * close) {
continue
}
// ...
}
Reka bentuk fungsi interaktif, bersihkan inventori, jeda, nyahjeda, ubah suai parameter, dll. Sangat mudah untuk mereka bentuk interaksi pada FMZ, dan platform menyediakan banyak kawalan interaktif. Kami hanya perlu menambah kawalan interaktif pada strategi, dan kemudian menulis pelbagai kod pengecaman dan pemprosesan apabila menerima mesej dalam kod strategi.
var cmd = GetCommand()
if (cmd) {
Log("交互指令:", cmd)
var arrCmd = cmd.split(":")
if (arrCmd.length == 2) {
var strCmd = arrCmd[0]
var param = parseFloat(arrCmd[1])
if (strCmd == "atrPeriod") {
atrPeriod = param
Log("修改ATR参数:", atrPeriod)
}
} else {
if (cmd == "isPaused" && !isPaused) {
isPaused = true
Log("暂停交易")
} else if (cmd == "isPaused" && isPaused) {
isPaused = false
Log("取消暂停交易")
} else if (cmd == "clearAndPaused") {
clear(pos, r)
isPaused = true
Log("清仓、暂停交易")
}
}
}
Apabila membuka atau menutup strategi, anda boleh menolak mesej dengan mudahMel, APP FMZ, antara muka pihak ketiga, dsb.
Log([r[r.length - 1].Time, "long", close, tradeAmount], "@") // 消息推送
Terima pemberitahuan tolak (APP FMZ dan apl lain juga akan menerima pemberitahuan tolak):

Fungsi untuk mengira untung dan rugi dipanggil setiap kali kedudukan ditutup untuk mengira untung dan rugi dan mengeluarkan keluk untung rugi.
function calcProfit() {
var initAcc = _G("initAcc")
var nowAcc = _C(exchange.GetAccount)
var profit = nowAcc.Equity - initAcc.Equity
return profit
}
Gunakan FMZ_G()Fungsi, adalah mudah untuk mereka bentuk mekanisme pemulihan kemajuan strategi.
if (isReset) {
_G(null)
LogProfitReset()
LogReset(1)
c.reset()
}
arrUp = _G("arrUp")
if (!arrUp) {
arrUp = []
_G("arrUp", arrUp)
}
arrDown = _G("arrDown")
if (!arrDown) {
arrDown = []
_G("arrDown", arrDown)
}
Apabila berdagang kontrak, kuantiti pesanan dalam antara muka pesanan ialah bilangan kontrak, jadi pengguna sering bertanya bagaimana untuk membuat pesanan dalam nombor Kami:
if (isAmountForUSDT) {
tradeAmount = tradeAmount * 1.05 / close
}
tradeAmount = _N(tradeAmount, amountPrecision)
Ia sebenarnya sangat mudah, hanya bahagikan jumlah dengan harga.
Jika anda ingin sentiasa menempah sejumlah dana dalam akaun anda sebagai kawalan risiko, anda boleh mereka bentuk mekanisme mudah ini.
var balance = acc.Balance
if (balance - initAcc.Equity * reserve < tradeAmount * close) {
continue
}
Apabila menjalankan pasaran sebenar, strategi itu pasti perlu diperhatikan, termasuk ekuiti akaun, status strategi, kedudukan strategi, maklumat pesanan, carta pasaran, dll. Ini direka seperti berikut:
if (isShowPlot) {
r.forEach(function(bar, index) {
c.begin(bar)
for (var i in atrs) {
var arr = atrs[i]
var up = arr[0] + arr[2][index] * arr[1]
var mid = arr[0]
var down = arr[0] - arr[2][index] * arr[1]
c.plot(up, 'up_' + (i + 1))
c.plot(mid, 'mid_' + (i + 1))
c.plot(down, 'down_' + (i + 1))
}
for (var signal of arrSignal) {
if (signal[0] == bar.Time) {
c.signal(signal[1], signal[2], signal[3])
}
}
c.close()
})
}
// ...
var orderTbl = {"type": "table", "title": "order", "cols": ["symbol", "type", "ratio", "price", "amount"], "rows": []}
for (var i = arrUp.length - 1; i >= 0; i--) {
var order = arrUp[i]
orderTbl["rows"].push([order["symbol"], "short", order["ratio"], order["price"], order["amount"]])
}
for (var i = 0; i < arrDown.length; i++) {
var order = arrDown[i]
orderTbl["rows"].push([order["symbol"], "long", order["ratio"], order["price"], order["amount"]])
}
var posTbl = {"type": "table", "title": "pos", "cols": ["symbol", "type", "price", "amount"], "rows": []}
for (var i = 0; i < pos.length; i++) {
var p = pos[i]
posTbl["rows"].push([p.Symbol, p.Type == PD_LONG ? "long" : "short", p.Price, p.Amount])
}
LogStatus(_D(), "初始权益:" + initAcc.Equity, ", 当前权益:" + acc.Equity, ", 运行状态:" + (isPaused ? "暂停交易" : "运行中"),
"\n`" + JSON.stringify(orderTbl) + "`\n", "`" + JSON.stringify(posTbl) + "`")
Pada akhirnya, lebih daripada 200 baris kod melaksanakan strategi lengkap yang boleh diuji dan dilaksanakan dalam perdagangan sebenar. Kami telah mencapai matlamat utama kami: untuk mencipta sistem perdagangan kuantitatif semua-dalam-satu di FMZ yang menggabungkan “visualisasi + interaksi + automasi”.
Ujian belakang adalah untuk rujukan sahaja. Mereka yang melakukan perdagangan kuantitatif tahu bahawa “ujian belakang” tidak boleh mensimulasikan senario sebenar 100%. Tujuan utama ujian belakang adalah untuk menguji logik strategi, menguji keteguhan strategi, dan menguji fungsi asas.


Reka bentuk parameter:

Reka Bentuk Interaksi:

Kod sumber strategi:
/*backtest
start: 2024-04-27 18:40:00
end: 2025-04-10 00:00:00
period: 15m
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT","balance":100}]
*/
var atrPeriod = 20
var arrUp = null
var arrDown = null
var arrSignal = []
function calcProfit() {
var initAcc = _G("initAcc")
var nowAcc = _C(exchange.GetAccount)
var profit = nowAcc.Equity - initAcc.Equity
return profit
}
function clear(positions, r) {
var close = r[r.length - 1].Close
for (var p of positions) {
if (p.Type == PD_LONG) {
var id = exchange.CreateOrder(symbol, "closebuy", -1, p.Amount)
if (!id) {
Log("下单失败")
continue
}
arrSignal.push([r[r.length - 1].Time, "closelong", close, p.Amount])
Log([r[r.length - 1].Time, "closelong", close, p.Amount], "@")
} else if (p.Type == PD_SHORT) {
var id = exchange.CreateOrder(symbol, "closesell", -1, p.Amount)
if (!id) {
Log("下单失败")
continue
}
arrSignal.push([r[r.length - 1].Time, "closeshort", close, p.Amount])
Log([r[r.length - 1].Time, "closeshort", close, p.Amount], "@")
}
}
arrUp = []
arrDown = []
_G("arrUp", arrUp)
_G("arrDown", arrDown)
var profit = calcProfit()
LogProfit(profit)
}
function main() {
var symbolInfo = symbol.split(".")
if (symbolInfo.length != 2) {
throw "error symbol:" + symbol
} else {
exchange.SetCurrency(symbolInfo[0])
exchange.SetContractType(symbolInfo[1])
}
exchange.SetPrecision(pricePrecision, amountPrecision)
let c = KLineChart({
overlay: true
})
if (isReset) {
_G(null)
LogProfitReset()
LogReset(1)
c.reset()
}
arrUp = _G("arrUp")
if (!arrUp) {
arrUp = []
_G("arrUp", arrUp)
}
arrDown = _G("arrDown")
if (!arrDown) {
arrDown = []
_G("arrDown", arrDown)
}
var initAcc = _G("initAcc")
if (!initAcc) {
initAcc = _C(exchange.GetAccount)
_G("initAcc", initAcc)
}
var isPaused = false
while (true) {
var atrs = []
var r = _C(exchange.GetRecords, symbol)
var pos = _C(exchange.GetPositions, symbol)
var acc = _C(exchange.GetAccount)
var open = r[r.length - 1].Open
var close = r[r.length - 1].Close
var atr = TA.ATR(r, atrPeriod)
for (var i = 0; i < maxRatio; i++) {
var up = open + atr[atr.length - 1] * (i + 1)
var mid = open
var down = open - atr[atr.length - 1] * (i + 1)
atrs.push([open, (i + 1), atr])
var tradeAmount = baseAmount * Math.pow(2, i)
if (isAmountForUSDT) {
tradeAmount = tradeAmount * 1.05 / close
}
tradeAmount = _N(tradeAmount, amountPrecision)
var balance = acc.Balance
if (balance - initAcc.Equity * reserve < tradeAmount * close) {
continue
}
if (close > up && i >= arrUp.length && !isPaused) {
var id = exchange.CreateOrder(symbol, "sell", -1, tradeAmount)
if (!id) {
Log("下单失败")
continue
}
arrUp.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
_G("arrUp", arrUp)
arrSignal.push([r[r.length - 1].Time, "short", close, tradeAmount])
Log([r[r.length - 1].Time, "short", close, tradeAmount], "@")
} else if (close < down && i >= arrDown.length && !isPaused) {
var id = exchange.CreateOrder(symbol, "buy", -1, tradeAmount)
if (!id) {
Log("下单失败")
continue
}
arrDown.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
_G("arrDown", arrDown)
arrSignal.push([r[r.length - 1].Time, "long", close, tradeAmount])
Log([r[r.length - 1].Time, "long", close, tradeAmount], "@")
} else if (((arrUp.length > 0 && close < mid) || (arrDown.length > 0 && close > mid)) && !isPaused) {
clear(pos, r)
}
}
if (isShowPlot) {
r.forEach(function(bar, index) {
c.begin(bar)
for (var i in atrs) {
var arr = atrs[i]
var up = arr[0] + arr[2][index] * arr[1]
var mid = arr[0]
var down = arr[0] - arr[2][index] * arr[1]
c.plot(up, 'up_' + (i + 1))
c.plot(mid, 'mid_' + (i + 1))
c.plot(down, 'down_' + (i + 1))
}
for (var signal of arrSignal) {
if (signal[0] == bar.Time) {
c.signal(signal[1], signal[2], signal[3])
}
}
c.close()
})
}
var cmd = GetCommand()
if (cmd) {
Log("交互指令:", cmd)
var arrCmd = cmd.split(":")
if (arrCmd.length == 2) {
var strCmd = arrCmd[0]
var param = parseFloat(arrCmd[1])
if (strCmd == "atrPeriod") {
atrPeriod = param
Log("修改ATR参数:", atrPeriod)
}
} else {
if (cmd == "isPaused" && !isPaused) {
isPaused = true
Log("暂停交易")
} else if (cmd == "isPaused" && isPaused) {
isPaused = false
Log("取消暂停交易")
} else if (cmd == "clearAndPaused") {
clear(pos, r)
isPaused = true
Log("清仓、暂停交易")
}
}
}
var orderTbl = {"type": "table", "title": "order", "cols": ["symbol", "type", "ratio", "price", "amount"], "rows": []}
for (var i = arrUp.length - 1; i >= 0; i--) {
var order = arrUp[i]
orderTbl["rows"].push([order["symbol"], "short", order["ratio"], order["price"], order["amount"]])
}
for (var i = 0; i < arrDown.length; i++) {
var order = arrDown[i]
orderTbl["rows"].push([order["symbol"], "long", order["ratio"], order["price"], order["amount"]])
}
var posTbl = {"type": "table", "title": "pos", "cols": ["symbol", "type", "price", "amount"], "rows": []}
for (var i = 0; i < pos.length; i++) {
var p = pos[i]
posTbl["rows"].push([p.Symbol, p.Type == PD_LONG ? "long" : "short", p.Price, p.Amount])
}
LogStatus(_D(), "初始权益:" + initAcc.Equity, ", 当前权益:" + acc.Equity, ", 运行状态:" + (isPaused ? "暂停交易" : "运行中"),
"\n`" + JSON.stringify(orderTbl) + "`\n", "`" + JSON.stringify(posTbl) + "`")
Sleep(5000)
}
}
Strategi tersebut hanya untuk tujuan pengajaran. Walaupun ia boleh digunakan dalam perdagangan sebenar dan pada masa ini menguntungkan, ia akan mengambil masa untuk menguji keberkesanan jangka panjangnya. Masih terdapat ruang untuk pengoptimuman dalam bahagian lukisan strategi, yang boleh mengelakkan beberapa operasi berulang dan meningkatkan kecekapan program. Logik strategi juga boleh dioptimumkan lagi.

Ringkasan puitis daripada GPT:
Perdagangan sebenar adalah perjalanan yang panjang. Tidak kira apabila anda kembali, anda hanya mencari ketenangan fikiran. Setiap kali anda membuka kedudukan, anda menyemai cahaya harapan di pasaran yang luas; setiap kali anda menghentikan kerugian, anda belajar untuk bergerak ke hadapan dengan lebih teguh dalam angin dan hujan. Pasaran adalah seperti arus, dan keuntungan dan kerugian adalah seperti mimpi. Kami menari di puncak gelombang nombor dan menonton di bawah rumah api strategi. Semoga anda dan saya, dalam perjalanan yang panjang ini, tidak kehilangan arah atau takut kesepian, dan akhirnya mencapai cahaya yang menjadi milik kita.
Artikel ini bukan sahaja memperkenalkan strategi yang lengkap, tetapi yang lebih penting, idea pembangunan strategi “bersistematik”. Daripada reka bentuk strategi, pengurusan status, kawalan risiko, interaksi carta hingga pelaksanaan sebenar, ini adalah satu set templat yang boleh digunakan semula berulang kali, dan ia juga satu-satunya cara untuk perdagangan kuantitatif bergerak ke arah profesionalisasi.
Saya harap anda boleh menggunakan platform FMZ untuk membina sistem dagangan automatik anda sendiri supaya anda tidak akan terlepas sebarang isyarat.
Terima kasih atas bacaan dan sokongan anda. Strategi tersebut adalah untuk tujuan pengajaran sahaja. Sila gunakannya dengan berhati-hati dalam perdagangan sebenar.