3
tập trung vào
1444
Người theo dõi

Giới thiệu về mã nguồn của chiến lược giao dịch cặp tiền kỹ thuật số và API mới nhất của nền tảng FMZ

Được tạo ra trong: 2024-07-10 16:36:54, cập nhật trên: 2024-07-12 15:53:41
comments   5
hits   2914

Giới thiệu về mã nguồn của chiến lược giao dịch cặp tiền kỹ thuật số và API mới nhất của nền tảng FMZ

Lời nói đầu

Bài viết trước đã giới thiệu các nguyên tắc và kiểm tra ngược về giao dịch theo cặp, https://www.fmz.com/digest-topic/10457. Đây là mã nguồn thực tế dựa trên nền tảng FMZ. Chiến lược tương đối đơn giản và rõ ràng, phù hợp với người mới bắt đầu học. Nền tảng FMZ gần đây đã nâng cấp một số API để thân thiện hơn với các chiến lược giao dịch nhiều cặp tiền tệ. Bài viết này sẽ giới thiệu chi tiết về mã nguồn JavaScript của chiến lược này. Mặc dù chiến lược này chỉ có một trăm dòng, nhưng nó chứa đựng tất cả các khía cạnh cần thiết cho một chiến lược hoàn chỉnh. Đối với các API cụ thể, vui lòng tham khảo tài liệu API được mô tả rất chi tiết. Địa chỉ công khai của Strategy: https://www.fmz.com/strategy/456143 có thể được sao chép trực tiếp.

Sử dụng nền tảng FMZ

Nếu bạn chưa quen với nền tảng FMZ, tôi thực sự khuyên bạn nên đọc hướng dẫn này: https://www.fmz.com/bbs-topic/4145. Bài viết giới thiệu các chức năng cơ bản của nền tảng và cách triển khai robot từ đầu.

Khung chính sách

Sau đây là khuôn khổ chiến lược đơn giản nhất, chức năng chính là điểm vào. Vòng lặp vô hạn đảm bảo chiến lược được thực thi liên tục và thêm một khoảng thời gian ngủ nhỏ để ngăn tần suất truy cập vượt quá giới hạn trao đổi quá nhanh.

function main(){
    while(true){
        //策略内容
        Sleep(Interval * 1000) //Sleep
    }
}

Ghi lại dữ liệu lịch sử

Robot sẽ khởi động lại nhiều lần vì nhiều lý do, chẳng hạn như lỗi, cập nhật tham số, cập nhật chiến lược, v.v. và một số dữ liệu cần được lưu lại để sử dụng cho lần khởi động tiếp theo. Sau đây là minh họa về cách tiết kiệm vốn chủ sở hữu ban đầu để sử dụng khi tính toán lợi nhuận._Hàm G() có thể lưu trữ nhiều dữ liệu khác nhau._G(key, value) có thể lưu trữ giá trị của value và gọi nó ra bằng _G(key), trong đó key là một chuỗi.

let init_eq = 0 //定义初始权益
if(!_G('init_eq')){  //如果没有储存_G('init_eq')返回null
    init_eq = total_eq
    _G('init_eq', total_eq) //由于没有储存,初始权益为当前权益,并在这里储存
}else{
    init_eq = _G('init_eq') //如果储存,读取初始权益的值
}

Chiến lược chịu lỗi

Khi lấy dữ liệu như vị thế và điều kiện thị trường thông qua API, lỗi có thể được trả về vì nhiều lý do khác nhau. Việc gọi trực tiếp dữ liệu trong đó sẽ khiến chiến lược bị lỗi và dừng lại, do đó cần có cơ chế chịu lỗi._Hàm C() sẽ tự động thử lại sau khi xảy ra lỗi cho đến khi dữ liệu trả về là chính xác. Hoặc kiểm tra xem dữ liệu có khả dụng sau khi trả về hay không.

let pos = _C(exchange.GetPosition, pair)

let ticker_A = exchange.GetTicker(pair_a)
let ticker_B = exchange.GetTicker(pair_b)
if(!ticker_A || !ticker_B){
    continue //如果数据不可用,就跳出这次循环
}

API tương thích đa tiền tệ

Các hàm như GetPosition, GetTicker và GetRecords có thể thêm tham số cặp giao dịch để lấy dữ liệu tương ứng mà không cần phải thiết lập cặp giao dịch ràng buộc với sàn giao dịch, điều này giúp tăng cường khả năng tương thích của nhiều chiến lược cặp giao dịch. Để biết thông tin nâng cấp chi tiết, vui lòng tham khảo bài viết: https://www.fmz.com/digest-topic/10451. Tất nhiên, bạn cần dịch vụ lưu trữ mới nhất để hỗ trợ. Nếu dịch vụ lưu trữ của bạn quá cũ, bạn cần phải nâng cấp.

Các thông số chiến lược

  • Cặp_A Tiền tệ giao dịch A: Cặp giao dịch A cần được ghép nối để giao dịch. Bạn cần tự chọn cặp giao dịch. Bạn có thể tham khảo phần giới thiệu và kiểm tra ngược trong bài viết trước.
  • Cặp_B Tiền tệ giao dịch B: Cặp tiền tệ giao dịch B cần được ghép nối
  • Tiền tệ cơ sở trích dẫn: Tiền tệ ký quỹ của sàn giao dịch tương lai, thường là USDT
  • Kích thước lưới Pct: độ lệch cần thêm vị thế là bao nhiêu, hãy xem bài viết về nguyên tắc chiến lược để biết chi tiết, vì lý do phí và trượt giá, kích thước này không nên quá nhỏ
  • Trade_Value: Giá trị giao dịch khi thêm vị thế cho mỗi độ lệch so với kích thước lưới.
  • Ice_Value: Nếu giá trị giao dịch quá lớn, bạn có thể sử dụng giá trị tảng băng trôi để mở vị thế. Nói chung, nó có thể được đặt thành cùng giá trị với giá trị giao dịch.
  • Max_Value Số lượng nắm giữ tối đa: Số lượng nắm giữ tối đa của một loại tiền tệ duy nhất, để tránh rủi ro nắm giữ quá nhiều vị thế
  • N Tham số giá trung bình: tham số dùng để tính tỷ lệ giá trung bình, đơn vị là giờ, ví dụ 100 biểu thị trung bình của 100 giờ
  • Thời gian ngủ ngắt quãng (giây): Thời gian ngủ giữa mỗi chu kỳ của chiến lược

Ghi chú chính sách đầy đủ

Nếu bạn vẫn chưa hiểu, bạn có thể sử dụng tài liệu API, công cụ gỡ lỗi và các công cụ đối thoại AI thường dùng trên thị trường của FMZ để giải quyết các câu hỏi của mình.

function GetPosition(pair){
    let pos = _C(exchange.GetPosition, pair)
    if(pos.length == 0){ //返回为空代表没有持仓
        return {amount:0, price:0, profit:0}
    }else if(pos.length > 1){ //策略要设置为单向持仓模式
        throw '不支持双向持仓'
    }else{ //为了方便,多仓持仓量为正,空仓持仓量为负
        return {amount:pos[0].Type == 0 ? pos[0].Amount : -pos[0].Amount, price:pos[0].Price, profit:pos[0].Profit}
    }
}

function GetRatio(){
    let kline_A = exchange.GetRecords(Pair_A+"_"+Quote+".swap", 60*60, N) //小时K线
    let kline_B = exchange.GetRecords(Pair_B+"_"+Quote+".swap", 60*60, N)
    let total = 0
    for(let i= Math.min(kline_A.length,kline_B.length)-1; i >= 0; i--){ //反过来计算,避免K线长度不够
        total += kline_A[i].Close / kline_B[i].Close
    }
    return total / Math.min(kline_A.length,kline_B.length)
}

function GetAccount(){
    let account = _C(exchange.GetAccount)
    let total_eq = 0
    if(exchange.GetName == 'Futures_OKCoin'){ //由于这里的API并不兼容,目前仅OKX期货交易所获取到总权益
        total_eq = account.Info.data[0].totalEq //其他交易所的宗权益Info中也包含,可以自己对着交易所API文档找找
    }else{
        total_eq = account.Balance //其它交易所暂时使用可用余额,会造成计算收益错误,但不影响策略使用
    }
    let init_eq = 0
    if(!_G('init_eq')){
        init_eq = total_eq
        _G('init_eq', total_eq)
    }else{
        init_eq = _G('init_eq')
    }
    LogProfit(total_eq - init_eq)
    return total_eq
}

function main(){
    var precision = exchange.GetMarkets() //这里获取精度
    var last_get_ratio_time = Date.now()
    var ratio = GetRatio()
    var total_eq = GetAccount()
    while(true){
        let start_loop_time = Date.now()
        if(Date.now() - last_get_ratio_time > 10*60*1000){ //每10分钟更新下均价和账户信息
            ratio = GetRatio()
            total_eq = GetAccount()
            last_get_ratio_time = Date.now()
        }
        let pair_a = Pair_A+"_"+Quote+".swap" //交易对的设置形如BTC_USDT.swap
        let pair_b = Pair_B+"_"+Quote+".swap"
        let CtVal_a = "CtVal" in precision[pair_a] ? precision[pair_a].CtVal : 1 //有的交易所用张来代表数量,如一张代表0.01个币,因此需要换算下
        let CtVal_b = "CtVal" in precision[pair_b] ? precision[pair_b].CtVal : 1 //不含这个字段的不用张
        let position_A = GetPosition(pair_a)
        let position_B = GetPosition(pair_b)
        let ticker_A = exchange.GetTicker(pair_a)
        let ticker_B = exchange.GetTicker(pair_b)
        if(!ticker_A || !ticker_B){ //如果返回数据异常,跳出这次循环
            continue
        }
        let diff = (ticker_A.Last / ticker_B.Last - ratio) / ratio //计算偏离的比例
        let aim_value = - Trade_Value * diff / Pct //目标持有的仓位
        let id_A = null
        let id_B = null
        //以下是具体的开仓逻辑
        if( -aim_value + position_A.amount*CtVal_a*ticker_A.Last > Trade_Value && position_A.amount*CtVal_a*ticker_A.Last > -Max_Value){
            id_A = exchange.CreateOrder(pair_a, "sell", ticker_A.Buy, _N(Ice_Value / (ticker_A.Buy * CtVal_a), precision[pair_a].AmountPrecision))
        }
        if( -aim_value - position_B.amount*CtVal_b*ticker_B.Last > Trade_Value && position_B.amount*CtVal_b*ticker_B.Last < Max_Value){
            id_B = exchange.CreateOrder(pair_b, "buy", ticker_B.Sell, _N(Ice_Value / (ticker_B.Sell * CtVal_b), precision[pair_b].AmountPrecision))
        }
        if( aim_value - position_A.amount*CtVal_a*ticker_A.Last > Trade_Value && position_A.amount*CtVal_a*ticker_A.Last < Max_Value){
            id_A = exchange.CreateOrder(pair_a, "buy", ticker_A.Sell, _N(Ice_Value / (ticker_A.Sell * CtVal_a), precision[pair_a].AmountPrecision))
        }
        if( aim_value + position_B.amount*CtVal_b*ticker_B.Last > Trade_Value &&  position_B.amount*CtVal_b*ticker_B.Last > -Max_Value){
            id_B = exchange.CreateOrder(pair_b, "sell", ticker_B.Buy, _N(Ice_Value / (ticker_B.Buy * CtVal_b), precision[pair_b].AmountPrecision))
        }
        if(id_A){
            exchange.CancelOrder(id_A) //这里直接撤销
        }
        if(id_B){
            exchange.CancelOrder(id_B)
        }
        let table = {
            type: "table",
            title: "交易信息",
            cols: ["初始权益", "当前权益", Pair_A+"仓位", Pair_B+"仓位", Pair_A+"持仓价", Pair_B+"持仓价", Pair_A+"收益", Pair_B+"收益", Pair_A+"价格", Pair_B+"价格", "当前比价", "平均比价", "偏离均价", "循环延时"],
            rows: [[_N(_G('init_eq'),2), _N(total_eq,2), _N(position_A.amount*CtVal_a*ticker_A.Last, 1), _N(position_B.amount*CtVal_b*ticker_B.Last,1),
                _N(position_A.price, precision[pair_a].PircePrecision), _N(position_B.price, precision[pair_b].PircePrecision),
                _N(position_A.profit, 1), _N(position_B.profit, 1), ticker_A.Last, ticker_B.Last,
                _N(ticker_A.Last / ticker_B.Last,6), _N(ratio, 6), _N(diff, 4), (Date.now() - start_loop_time)+"ms"
            ]]
        }
        LogStatus("`" + JSON.stringify(table) + "`") //这个函数会在机器人页面显示包含以上信息的表格
        Sleep(Interval * 1000) //休眠时间为ms
    }
}