The resource loading... loading...

Digital currency pairing trading strategy source code and the latest API of FMZ platform

Author: The grass, Created: 2024-07-10 16:36:54, Updated: 2024-07-12 15:53:41

img

The Foreword

In a previous post, I discussed the principles and feedback of pairing transactions.https://www.fmz.com/digest-topic/10457The FMZ platform has recently upgraded some of its APIs to be more friendly to multi-transaction. This article details the JavaScript source code for the policy. Although the policy is only a hundred lines long, it contains all the aspects needed for a complete policy.https://www.fmz.com/strategy/456143This is the first time I have ever seen this.

FMZ platform used

If you are not familiar with the FMZ platform, I strongly recommend you read this tutorial:https://www.fmz.com/bbs-topic/4145The basic functionality of the platform and how to deploy a robot from scratch are described in detail.

The strategic framework

The following is the simplest policy framework, the main function is the input. The dead cycle guarantee policy is constantly executed, adding a small sleep period to avoid access frequency too quickly beyond the exchange limit.

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

Recorded historical data

The robot will restart repeatedly for various reasons, such as errors, updating parameters, updating policies, etc. and needs to save some data for the next time it is started. Here is a demonstration of how to save the initial rights to use to calculate the gain. The function can store a variety of data.

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') //如果储存,读取初始权益的值
}

Strategy is blunder

When data such as positions, trades, etc. are retrieved through the API, for various reasons, it may return an error. This causes the policy to stop when the data is called directly, which requires an error tolerance mechanism.

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 //如果数据不可用,就跳出这次循环
}

Compatible with multi-currency APIs

Functions such as GetPosition, GetTicker, GetRecords can add parameters to a transaction pair to access the corresponding data without the need to set up exchange-bound pairs, which greatly facilitates multi-transaction versus strategy compatibility.https://www.fmz.com/digest-topic/10451◦ Of course, you need the latest host to support, but if your host is too old, you need to upgrade.

Policy parameters

  • Pair_A Trading Currency A: The pair A needs to be paired with the pair A, and the pair A needs to be chosen by the trader.
  • Pair_B Transaction currency B: a pair of transactions B that requires a pair of transactions
  • Quote Base Currency: The underlying currency of a futures exchange, usually USDT
  • Pct grid size: deviate from how much to add, see the article on the strategy principle for an introduction, it should not be too small due to the fees and slippage reasons
  • Trade_Value Trade value: the value of the trade for each deviation of a grid size
  • Ice_Value Ice Mountain commission value: If the transaction value is too large, the ice mountain commission value can be used to open a trade, usually set to the same size as the transaction value
  • Max_Value Maximum holdings: Maximum holdings of a single currency to avoid the risk of holding too much
  • N average price parameter: parameter used to calculate the average price comparison, the unit is the hour, such as 100 represents the average of 100 h
  • Interval Sleep time (s): the sleep time for each cycle interval of the strategy

Full policy notes

If you don't understand, you can use FMZ's API documentation, debugging tools, and commonly used AI dialog tools to solve your questions.

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

More

sidianshuiaHello, I'm trying to get exchange.GetMarkets ((() only get to one trading pair of data, how can I set up to get to multiple, such as two trading pairs of data.

77924998Is there a python version?

Beans 888/upload/asset/21c799a2c667c13fcb0bd.png This is a list of all the characters that appear in the game. A bit of a bump.

Inventors quantify - small dreamsThe GetMarkets function is not currently supported by the retest, but can be waited a bit longer.

ChaoZhangUpgrade to the latest host