avatar of 发明者量化-小小梦 发明者量化-小小梦
fokus pada Pesan pribadi
4
fokus pada
1271
Pengikut

Perjalanan Kuantitatif Dimulai dari FMZ

Dibuat di: 2025-04-18 09:31:42, diperbarui pada: 2025-04-26 11:50:01
comments   0
hits   937

Perjalanan Kuantitatif Dimulai dari FMZ

perkenalan

Pernahkah Anda berpikir bahwa Anda dapat dengan mudah memulai perdagangan kuantitatif dan segera memulainya tanpa harus begadang semalaman menulis kode untuk membangun kerangka kerja, mendesain UI, dan berbagai detail serta mekanisme desain sendiri? Segala sesuatu menjadi mungkin pada platform kuantitatif FMZ. Anda tidak memerlukan latar belakang pemrograman tingkat lanjut, Anda juga tidak perlu khawatir tentang proses penerapan yang rumit - yang Anda perlukan hanyalah komputer dan akun untuk memulai perjalanan kuantitatif Anda yang “bisa dilakukan di mana saja”. Artikel ini akan membawa Anda dari awal, memulai dengan cepat menggunakan FMZ, merasakan pesona perdagangan otomatis, dan menggunakan data dan strategi untuk menguasai ritme pasar. Apakah Anda seorang pemula atau veteran yang ingin meningkatkan efisiensi, perjalanan ini patut dicoba.

Kebingungan bagi pemula perdagangan kuantitatif

Saya sering berkomunikasi dan mengobrol dengan para pemula di platform ini. Pemula perdagangan kuantitatif biasanya bingung dengan proses desain yang lengkap. Ketika saya memiliki ide perdagangan, saya sering tidak tahu harus mulai dari mana dan merasa kewalahan.

Bingung tentang:

  • Cara mendesain posisi pembukaan dan penutupan
  • Cara merancang perhitungan pendapatan
  • Cara merancang strategi untuk memulai kembali dan melanjutkan kemajuan perdagangan
  • Cara mendesain tampilan bagan strategi
  • Cara merancang kontrol interaksi strategis

Mari kita selesaikan kebingungan di atas bersama-sama.

Penjelasan Desain

Dalam dunia perdagangan kuantitatif, desain strategi sering kali merupakan perjalanan eksplorasi tanpa akhir. Anda mungkin telah mencoba menulis indikator, atau mencoba mengikuti sinyal beli dan jual secara membabi buta, tetapi yang benar-benar dapat membawa hasil yang jauh adalah sistem strategi yang dapat “terlihat, dapat disesuaikan, dan stabil.” Berdasarkan platform kuantitatif FMZ, Anda dapat memiliki pengalaman praktis “berjalan tepat waktu”. Bangun strategi sederhana, dari pengaturan parameter, tampilan grafik, hingga fungsi interaktif dan perhitungan untung rugi, untuk sepenuhnya memenuhi persyaratan desain strategi.

Ide strateginya adalah strategi peningkatan posisi langkah demi langkah berdasarkan ATR, logika pembentukan posisi grid langkah demi langkah (dua arah panjang dan pendek), perhitungan volatilitas adaptif ATR, dan logika likuidasi posisi (ketika pasar berbalik ke sumbu pusat).

Strategi ini didasarkan pada persyaratan desain berikut:

Tambahkan posisi dan tutup posisi sesuai dengan terobosan harga di berbagai level

Siapkan dua susunan untuk mengendalikan peningkatan posisi secara bertahap.

var arrUp = null 
var arrDown = null 

Setiap kali Anda menambahkan posisi, informasi posisi tersebut akan dimasukkan ke dalam array, yang memudahkan pengendalian posisi dan tampilan data pada antarmuka strategi waktu nyata.

Buka dan tutup posisi berdasarkan level terobosan harga. Demi kesederhanaan, baik pembukaan maupun penutupan posisi menggunakan perintah pasar, yang sederhana dan efektif.

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

Bersihkan inventaris dan gunakan fungsi untuk menanganinya. Beberapa struktur data perlu diatur ulang setiap kali inventaris dihapus, sehingga fungsi penghapusan perlu dienkapsulasi ke dalam fungsi untuk digunakan kembali 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)
}

Alokasi posisi langkah demi langkah

Terbagi menjadi beberapa level, dan level maksimumnya adalah: maxRatio. Setiap tingkatan menghitung ambang harga yang berbeda.

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

Mendukung penyesuaian parameter dinamis, operasi jeda, pembersihan cepat, dan interaksi lainnya

Rancang fungsi interaktif, hapus inventaris, jeda, lanjutkan jeda, modifikasi parameter, dsb. Sangat mudah untuk merancang interaksi di FMZ, dan platform ini menyediakan banyak kontrol interaktif. Kita hanya perlu menambahkan kontrol interaktif ke strategi, lalu menulis berbagai kode pengenalan dan pemrosesan saat menerima pesan dalam kode 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 pengingat buka/tutup

Saat membuka atau menutup strategi, Anda dapat dengan mudah mengirim pesan keSurat, Aplikasi FMZ, antarmuka pihak ketiga, dll.

Log([r[r.length - 1].Time, "long", close, tradeAmount], "@")  // 消息推送

Terima pemberitahuan push (Aplikasi FMZ dan aplikasi lain juga akan menerima pemberitahuan push):

Perjalanan Kuantitatif Dimulai dari FMZ

Statistik waktu nyata dan tampilan keuntungan dan posisi

Fungsi untuk menghitung untung rugi dipanggil setiap kali posisi ditutup untuk menghitung untung rugi dan mengeluarkan kurva untung rugi.

function calcProfit() {
    var initAcc = _G("initAcc")
    var nowAcc = _C(exchange.GetAccount)
    var profit = nowAcc.Equity - initAcc.Equity
    return profit
}

Mendukung persistensi status (pemulihan titik henti)

Gunakan FMZ_G()Fungsi, mudah untuk merancang 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)
    }

Desain penempatan pesanan berdasarkan jumlah

Saat memperdagangkan kontrak, jumlah pesanan di antarmuka pesanan adalah jumlah kontrak, sehingga pengguna sering bertanya bagaimana cara memesan dalam jumlah AS:

            if (isAmountForUSDT) {
                tradeAmount = tradeAmount * 1.05 / close
            }
            tradeAmount = _N(tradeAmount, amountPrecision)

Sebenarnya sangat sederhana, cukup bagi jumlah dengan harga.

Desain rasio cadangan

Jika Anda ingin selalu menyisihkan sejumlah dana tertentu di akun Anda sebagai pengendalian risiko, Anda dapat merancang mekanisme sederhana ini.

            var balance = acc.Balance
            if (balance - initAcc.Equity * reserve < tradeAmount * close) {
                continue 
            }

Bagan visualisasi

Ketika menjalankan pasar riil, sangat penting untuk memperhatikan strategi, termasuk ekuitas akun, status strategi, posisi strategi, informasi pesanan, grafik pasar, dll. Berikut ini adalah beberapa hal yang dirancang:

        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 dari 200 baris kode menerapkan strategi lengkap yang dapat diuji kembali dan diimplementasikan dalam perdagangan nyata. Kami telah mencapai tujuan akhir kami: menciptakan sistem perdagangan kuantitatif lengkap di FMZ yang menggabungkan “visualisasi + interaksi + otomatisasi”.

Efek operasi strategi dan hasil pengujian ulang

Pengujian ulang hanya untuk referensi. Mereka yang melakukan perdagangan kuantitatif tahu bahwa “pengujian ulang” tidak dapat mensimulasikan skenario sebenarnya 100%. Tujuan utama pengujian ulang adalah untuk menguji logika strategi, menguji ketahanan strategi, dan menguji fungsi dasar.

Perjalanan Kuantitatif Dimulai dari FMZ

Perjalanan Kuantitatif Dimulai dari FMZ

Kode strategi, desain parameter

Desain parameter:

Perjalanan Kuantitatif Dimulai dari FMZ

Desain Interaksi:

Perjalanan Kuantitatif Dimulai dari FMZ

Kode 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 ini hanya untuk tujuan pengajaran. Meskipun dapat digunakan dalam perdagangan nyata dan saat ini menguntungkan, perlu waktu untuk menguji efektivitas jangka panjangnya. Masih ada ruang untuk pengoptimalan di bagian gambar strategi, yang dapat menghindari beberapa operasi berulang dan meningkatkan efisiensi program. Logika strategi juga dapat dioptimalkan lebih lanjut.

Perdagangan nyata adalah perjalanan yang panjang

Perjalanan Kuantitatif Dimulai dari FMZ

Ringkasan puitis dari GPT:

Perdagangan sesungguhnya adalah perjalanan yang panjang. Kapan pun kau kembali, yang kau cari hanya ketenangan batin. Setiap kali Anda membuka posisi, Anda menabur cahaya harapan di pasar yang luas; setiap kali Anda menghentikan kerugian, Anda belajar untuk bergerak maju lebih kuat dalam angin dan hujan. Pasar itu seperti pasang surut, untung ruginya bagaikan mimpi. Kami menari di puncak gelombang angka dan menonton di bawah mercusuar strategi. Semoga Anda dan saya, dalam perjalanan panjang ini, tidak tersesat atau takut pada kesendirian, dan akhirnya mencapai cahaya yang menjadi milik kita.

Ringkasan: Dari pengembangan strategi hingga pemikiran sistem

Artikel ini tidak hanya memperkenalkan strategi yang lengkap, tetapi yang lebih penting, ide pengembangan strategi yang “sistematis”. Dari desain strategi, manajemen status, pengendalian risiko, interaksi grafik hingga implementasi aktual, ini adalah serangkaian templat yang dapat digunakan kembali secara berulang, dan ini juga satu-satunya cara bagi perdagangan kuantitatif untuk bergerak menuju profesionalisasi.

Saya harap Anda dapat menggunakan platform FMZ untuk membangun sistem perdagangan otomatis Anda sendiri sehingga Anda tidak akan pernah kehilangan sinyal apa pun.

Terima kasih atas bacaan dan dukungan Anda. Strategi ini hanya untuk tujuan pengajaran. Harap menggunakannya dengan hati-hati dalam perdagangan nyata.