avatar of 发明者量化-小小梦 发明者量化-小小梦
tập trung vào tin nhắn riêng tư
4
tập trung vào
1271
Người theo dõi

Hành trình định lượng bắt đầu từ FMZ

Được tạo ra trong: 2025-04-18 09:31:42, cập nhật trên: 2025-04-26 11:50:01
comments   0
hits   941

Hành trình định lượng bắt đầu từ FMZ

giới thiệu

Bạn đã bao giờ nghĩ rằng mình có thể dễ dàng bắt đầu giao dịch định lượng và bắt đầu ngay mà không cần phải thức trắng đêm để viết mã để xây dựng khuôn khổ, thiết kế giao diện người dùng và nhiều chi tiết thiết kế cũng như cơ chế khác nhau không? Mọi thứ đều có thể thực hiện được trên nền tảng định lượng FMZ. Bạn không cần có nền tảng lập trình nâng cao hay lo lắng về các quy trình triển khai phức tạp - tất cả những gì bạn cần là một máy tính và một tài khoản để bắt đầu hành trình định lượng “đi bất cứ đâu” của mình. Bài viết này sẽ hướng dẫn bạn từ đầu, nhanh chóng bắt đầu với FMZ, trải nghiệm sức hấp dẫn của giao dịch tự động và sử dụng dữ liệu và chiến lược để làm chủ nhịp điệu thị trường. Cho dù bạn là người mới bắt đầu hay người kỳ cựu muốn cải thiện hiệu quả thì hành trình này đều đáng để thử.

Sự bối rối của người mới bắt đầu giao dịch định lượng

Tôi thường giao lưu và trò chuyện với những người mới sử dụng nền tảng này. Người mới bắt đầu giao dịch định lượng thường bối rối trước toàn bộ quy trình thiết kế. Khi có ý tưởng giao dịch, tôi thường không biết bắt đầu từ đâu và cảm thấy choáng ngợp.

Bối rối về:

  • Cách thiết kế vị trí mở và đóng
  • Cách thiết kế tính toán doanh thu
  • Làm thế nào để thiết kế các chiến lược để khởi động lại và tiếp tục tiến trình giao dịch
  • Cách thiết kế biểu đồ chiến lược hiển thị
  • Làm thế nào để thiết kế kiểm soát tương tác chiến lược

Chúng ta hãy cùng nhau giải quyết sự nhầm lẫn trên.

Giải thích thiết kế

Trong thế giới giao dịch định lượng, thiết kế chiến lược thường là một hành trình khám phá không có hồi kết. Bạn có thể đã thử viết các chỉ báo hoặc cố gắng tuân theo các tín hiệu mua và bán một cách mù quáng, nhưng những chỉ báo thực sự có thể đi xa chính là các hệ thống chiến lược có thể “nhìn thấy được, điều chỉnh được và ổn định”. Dựa trên nền tảng định lượng FMZ, bạn có thể có trải nghiệm thực tế về “đi đúng giờ”. Xây dựng chiến lược đơn giản, từ thiết lập tham số, hiển thị biểu đồ, đến các chức năng tương tác và tính toán lãi lỗ, để đáp ứng đầy đủ các yêu cầu thiết kế của chiến lược.

Ý tưởng chiến lược là một chiến lược tăng vị thế từng bước dựa trên ATR, logic xây dựng vị thế dạng lưới từng bước (dài hạn và ngắn hạn theo hai chiều), tính toán độ biến động thích ứng của ATR và logic thanh lý vị thế (khi thị trường đảo ngược về trục trung tâm).

Chiến lược này dựa trên các yêu cầu thiết kế sau:

Thêm vị thế và đóng vị thế theo mức giá đột phá ở các mức khác nhau

Thiết lập hai mảng để kiểm soát sự tăng dần của các vị trí.

var arrUp = null 
var arrDown = null 

Mỗi lần bạn thêm một vị trí, thông tin vị trí sẽ được đẩy vào mảng, giúp kiểm soát vị trí và hiển thị dữ liệu trên giao diện chiến lược theo thời gian thực dễ dàng hơn.

Mở và đóng các vị thế theo mức đột phá giá. Để đơn giản, cả lệnh mở và lệnh đóng đều sử dụng lệnh thị trường, vừa đơn giản vừa hiệu quả.

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

Dọn sạch hàng tồn kho và sử dụng hàm để xử lý. Một số cấu trúc dữ liệu cần được thiết lập lại mỗi lần xóa hàng tồn kho, do đó chức năng xóa cần được đóng gói vào một hàm để tái sử dụng trong mô-đun tương tác.

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

Phân bổ vị trí từng bước

Nó được chia thành nhiều cấp độ và cấp độ tối đa là: maxRatio. Mỗi bậc tính toán ngưỡng giá khác nhau.

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

Hỗ trợ điều chỉnh tham số động, tạm dừng hoạt động, xóa nhanh và các tương tác khác

Thiết kế các chức năng tương tác, xóa kho, tạm dừng, bỏ tạm dừng, sửa đổi tham số, v.v. Rất thuận tiện để thiết kế các tương tác trên FMZ và nền tảng này cung cấp nhiều điều khiển tương tác. Chúng ta chỉ cần thêm các điều khiển tương tác vào chiến lược, sau đó viết các mã nhận dạng và xử lý khác nhau khi nhận được tin nhắn trong mã chiến lược.

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

Có cơ chế nhắc nhở đóng/mở

Khi mở hoặc đóng một chiến lược, bạn có thể dễ dàng đẩy tin nhắn đếnThư, ỨNG DỤNG FMZ, giao diện của bên thứ ba, v.v.

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

Nhận thông báo đẩy (FMZ APP và các ứng dụng khác cũng sẽ nhận được thông báo đẩy):

Hành trình định lượng bắt đầu từ FMZ

Thống kê thời gian thực và hiển thị lợi nhuận và vị thế

Hàm tính lãi lỗ được gọi mỗi khi một vị thế được đóng để tính lãi lỗ và đưa ra đường cong lãi lỗ.

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

Hỗ trợ tính bền vững của trạng thái (khôi phục điểm dừng)

Sử dụng FMZ_G()Chức năng, dễ dàng thiết kế cơ chế phục hồi tiến trình chiến lược.

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

Thiết kế đặt hàng theo số lượng

Khi giao dịch hợp đồng, số lượng lệnh trong giao diện lệnh chính là số lượng hợp đồng, vì vậy người dùng thường hỏi cách đặt lệnh theo số lượng Us:

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

Thực ra rất đơn giản, chỉ cần chia số tiền cho giá.

Thiết kế tỷ lệ dự trữ

Nếu bạn muốn luôn giữ lại một số tiền nhất định trong tài khoản để kiểm soát rủi ro, bạn có thể thiết kế cơ chế đơn giản này.

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

Biểu đồ trực quan

Khi điều hành một thị trường thực tế, chắc chắn cần phải quan sát chiến lược, bao gồm vốn chủ sở hữu tài khoản, trạng thái chiến lược, vị thế chiến lược, thông tin lệnh, biểu đồ thị trường, v.v. Chúng được thiết kế như sau:

        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) + "`")

Cuối cùng, hơn 200 dòng mã đã triển khai một chiến lược hoàn chỉnh có thể được kiểm tra lại và áp dụng trong giao dịch thực tế. Chúng tôi đã đạt được mục tiêu cuối cùng của mình: tạo ra một hệ thống giao dịch định lượng trọn gói trên FMZ kết hợp “hình ảnh hóa + tương tác + tự động hóa”.

Hiệu ứng hoạt động chiến lược và kết quả kiểm tra ngược

Kiểm tra ngược chỉ mang tính chất tham khảo. Những người thực hiện giao dịch định lượng biết rằng “kiểm tra ngược” không thể mô phỏng được tình huống thực tế 100%. Mục đích chính của việc kiểm tra ngược là kiểm tra tính logic của chiến lược, kiểm tra tính mạnh mẽ của chiến lược và kiểm tra các chức năng cơ bản.

Hành trình định lượng bắt đầu từ FMZ

Hành trình định lượng bắt đầu từ FMZ

Mã chiến lược, thiết kế tham số

Thiết kế tham số:

Hành trình định lượng bắt đầu từ FMZ

Thiết kế tương tác:

Hành trình định lượng bắt đầu từ FMZ

Mã nguồn chiến lược:

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

Chiến lược này chỉ nhằm mục đích giảng dạy. Mặc dù có thể sử dụng trong giao dịch thực tế và hiện đang có lợi nhuận, nhưng sẽ cần thời gian để kiểm tra hiệu quả lâu dài của nó. Vẫn còn chỗ để tối ưu hóa trong phần vẽ chiến lược, có thể tránh một số thao tác lặp lại và cải thiện hiệu quả của chương trình. Logic chiến lược cũng có thể được tối ưu hóa hơn nữa.

Giao dịch thực sự là một hành trình dài

Hành trình định lượng bắt đầu từ FMZ

Tóm tắt bằng thơ của GPT:

Giao dịch thực sự là một hành trình dài. Bất kể khi nào bạn trở về, bạn chỉ tìm kiếm sự bình yên trong tâm hồn. Mỗi lần bạn mở một vị thế, bạn gieo mầm hy vọng vào thị trường rộng lớn; Mỗi lần bạn dừng lỗ, bạn học được cách tiến về phía trước vững vàng hơn bất chấp mưa gió. Thị trường giống như thủy triều, lợi nhuận và thua lỗ giống như giấc mơ. Chúng ta nhảy múa trên đỉnh sóng số và quan sát dưới ngọn hải đăng của chiến lược. Mong rằng bạn và tôi, trên chặng đường dài này, không lạc lối, không sợ cô đơn và cuối cùng sẽ chạm tới được ánh sáng dành riêng cho mình.

Tóm tắt: Từ phát triển chiến lược đến tư duy hệ thống

Bài viết này không chỉ giới thiệu một chiến lược hoàn chỉnh mà quan trọng hơn là một ý tưởng phát triển chiến lược “có hệ thống”. Từ thiết kế chiến lược, quản lý trạng thái, kiểm soát rủi ro, tương tác biểu đồ đến triển khai thực tế, đây là một tập hợp các mẫu có thể được sử dụng lại nhiều lần và cũng là cách duy nhất để giao dịch định lượng hướng tới chuyên nghiệp hóa.

Tôi hy vọng bạn có thể sử dụng nền tảng FMZ để xây dựng hệ thống giao dịch tự động của riêng mình để không bao giờ bỏ lỡ bất kỳ tín hiệu nào.

Cảm ơn các bạn đã đọc và ủng hộ. Chiến lược này chỉ nhằm mục đích giảng dạy. Hãy thận trọng khi sử dụng nó trong giao dịch thực tế.