avatar of 发明者量化-小小梦 发明者量化-小小梦
fokus pada mesej peribadi
4
fokus pada
1271
Pengikut

Perjalanan Kuantitatif Bermula dari FMZ

Dicipta dalam: 2025-04-18 09:31:42, dikemas kini pada: 2025-04-26 11:50:01
comments   0
hits   937

Perjalanan Kuantitatif Bermula dari FMZ

pengenalan

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.

Kekeliruan pemula perdagangan kuantitatif

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:

  • Bagaimana untuk mereka bentuk kedudukan pembukaan dan penutupan
  • Bagaimana untuk mereka bentuk pengiraan hasil
  • Bagaimana untuk mereka bentuk strategi untuk memulakan semula dan meneruskan kemajuan dagangan
  • Bagaimana untuk mereka bentuk paparan carta strategi
  • Bagaimana untuk mereka bentuk kawalan interaksi strategik

Mari kita selesaikan kekeliruan di atas bersama-sama.

Penerangan Reka Bentuk

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:

Tambah kedudukan dan tutup kedudukan mengikut penembusan harga pada tahap yang berbeza

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)
}

Peruntukan jawatan langkah demi langkah

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 
            }
        // ...
        }

Menyokong pelarasan parameter dinamik, operasi jeda, pelepasan cepat dan interaksi lain

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("清仓、暂停交易")
                }
            }
        }

Dengan mekanisme peringatan pembukaan/tutup

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):

Perjalanan Kuantitatif Bermula dari FMZ

Statistik masa nyata dan paparan keuntungan dan kedudukan

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
}

Menyokong kegigihan keadaan (pemulihan titik putus)

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)
    }

Reka bentuk membuat pesanan mengikut jumlah

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.

Reka bentuk nisbah rizab

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 
            }

Carta visualisasi

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”.

Kesan operasi strategi dan keputusan ujian belakang

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.

Perjalanan Kuantitatif Bermula dari FMZ

Perjalanan Kuantitatif Bermula dari FMZ

Kod strategi, reka bentuk parameter

Reka bentuk parameter:

Perjalanan Kuantitatif Bermula dari FMZ

Reka Bentuk Interaksi:

Perjalanan Kuantitatif Bermula dari FMZ

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.

Perdagangan sebenar adalah perjalanan yang panjang

Perjalanan Kuantitatif Bermula dari FMZ

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.

Ringkasan: Daripada pembangunan strategi kepada pemikiran sistem

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.