En la carga de los recursos... Cargando...

Introducción al código fuente de la estrategia de negociación de pares de divisas digitales y la API más reciente de la plataforma FMZ

El autor:FMZ~Lydia, Creado: 2024-07-11 14:57:15, Actualizado: 2024-07-12 15:55:53

img

Prefacio

El artículo anterior introdujo el principio y la prueba de retrospectiva del comercio de pares,https://www.fmz.com/bbs-topic/10459. Aquí hay un código fuente práctico basado en la plataforma FMZ. La estrategia es simple y clara, adecuada para que los principiantes aprendan. La plataforma FMZ ha actualizado recientemente algunas API para ser más amigables con las estrategias de pares de múltiples operaciones. Este artículo presentará el código fuente JavaScript de esta estrategia en detalle. Aunque el código de estrategia es de solo cien líneas, contiene todos los aspectos necesarios para una estrategia completa. La API específica se puede ver en el documento API, que es muy detallado.https://www.fmz.com/strategy/456143puede ser copiado directamente.

Uso de la plataforma FMZ

Si no está familiarizado con la plataforma FMZ, le recomiendo encarecidamente que lea este tutorial:https://www.fmz.com/bbs-topic/4145Introduce las funciones básicas de la plataforma en detalle, así como cómo desplegar un robot desde cero.

Marco estratégico

El siguiente es un marco de estrategia simple. La función principal es el punto de entrada. El bucle infinito asegura que la estrategia se ejecute continuamente, y se agrega un pequeño tiempo de sueño para evitar que la frecuencia de acceso exceda el límite de intercambio.

function main(){
    while(true){
        //strategy content
        Sleep(Interval * 1000) //Sleep
    }
}

Registro de datos históricos

El robot se reiniciará repetidamente por varias razones, como errores, actualizaciones de parámetros, actualizaciones de estrategia, etc., y algunos datos deben guardarse para la próxima puesta en marcha. Aquí hay una demostración de cómo guardar el capital inicial para calcular los rendimientos. La función _G() puede almacenar varios datos. _G(key, value) puede almacenar el valor del valor y llamarlo con _G(key), donde key es una cadena.

let init_eq = 0 //defining initial equity
if(!_G('init_eq')){  //If there is no storage, _G('init_eq') returns null.
    init_eq = total_eq
    _G('init_eq', total_eq) //Since there is no storage, the initial equity is the current equity and is stored here
}else{
    init_eq = _G('init_eq') //If stored, read the value of the initial equity
}

Estrategia de tolerancia a las fallas

Cuando se obtienen datos como posiciones y condiciones de mercado a través de la API, pueden regresarse errores debido a varias razones. Llamar directamente los datos hará que la estrategia se detenga debido a errores, por lo que se necesita un mecanismo tolerante a fallos. La función _C() volverá a intentar automáticamente después de un error hasta que se devuelvan los datos correctos. O comprobar si los datos están disponibles después de devolverlos.

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 //If the data is not available, exit the loop.
}

API compatible con varias monedas

Funciones como GetPosition, GetTicker y GetRecords pueden agregar un parámetro de par de operaciones para obtener los datos correspondientes, sin tener que establecer el par de operaciones vinculado al intercambio, lo que facilita enormemente la compatibilidad de múltiples estrategias de par de operaciones.https://www.fmz.com/bbs-topic/10456Por supuesto, se requiere el último docker para soportarlo. Si su docker es demasiado viejo, necesita actualizar.

Parámetros de la estrategia

  • Pair_A: Pareja de trading A que necesita ser emparejada para el trading. Usted necesita elegir el par de trading usted mismo. Puede consultar la introducción y backtesting en el artículo anterior.
  • Pareja_B: Pareja de operaciones B que necesita ser emparejada.
  • Citación: La moneda de margen de la bolsa de futuros, por lo general en USDT.
  • Pct: Cuánta desviación añadir posiciones, ver el artículo sobre los principios de estrategia para más detalles, debido a las tarifas de manejo y razones de deslizamiento, no debe establecerse demasiado pequeño.
  • El valor de negociación de la suma de posiciones para cada desviación del tamaño de la cuadrícula.
  • Ice_Value: Si el valor de la transacción es demasiado grande, puede usar el valor de la comisión del iceberg para abrir una posición.
  • Valor máximo de las tenencias de una moneda única, para evitar el riesgo de mantener demasiadas posiciones.
  • N: el parámetro utilizado para calcular la relación de precio medio, la unidad es hora, por ejemplo, 100 representa la media de 100 horas.
  • Intervalo: el tiempo de sueño entre cada ciclo de la estrategia.

Notas completas de la estrategia

Si todavía no entiendes, puedes usar la documentación de la API de FMZ, las herramientas de depuración y las herramientas de diálogo de IA comúnmente utilizadas en el mercado para resolver tus preguntas.

function GetPosition(pair){
    let pos = _C(exchange.GetPosition, pair)
    if(pos.length == 0){ //Returns null to indicate no position
        return {amount:0, price:0, profit:0}
    }else if(pos.length > 1){ //The strategy should be set to unidirectional position mode
        throw 'Bidirectional positions are not supported'
    }else{ //For convenience, long positions are positive and short positions are negative
        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) //Hourly K-line
    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--){ //Calculate in reverse to avoid the K-line being too short.
        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'){ //Since the API here is not compatible, only OKX Futures Exchange obtains the total equity currently.
        total_eq = account.Info.data[0].totalEq //The equity information of other exchanges is also included. You can look for it yourself in the exchange API documentation.
    }else{
        total_eq = account.Balance //Temporary use of available balances on other exchanges will cause errors in calculating returns, but will not affect the use of strategies.
    }
    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() //Get the precision here
    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){ //Update the average price and account information every 10 minutes
            ratio = GetRatio()
            total_eq = GetAccount()
            last_get_ratio_time = Date.now()
        }
        let pair_a = Pair_A+"_"+Quote+".swap" //The trading pair is set as BTC_USDT.swap
        let pair_b = Pair_B+"_"+Quote+".swap"
        let CtVal_a = "CtVal" in precision[pair_a] ? precision[pair_a].CtVal : 1 //Some exchanges use sheets to represent quantity, such as one sheet represents 0.01 coin, so you need to convert.
        let CtVal_b = "CtVal" in precision[pair_b] ? precision[pair_b].CtVal : 1 //No need to include this field
        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){ //If the returned data is abnormal, jump out of this loop
            continue
        }
        let diff = (ticker_A.Last / ticker_B.Last - ratio) / ratio //Calculate the ratio of deviation
        let aim_value = - Trade_Value * diff / Pct //Target holding position
        let id_A = null
        let id_B = null
        //The following is the specific logic of opening a position
        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) //Cancel directly here
        }
        if(id_B){
            exchange.CancelOrder(id_B)
        }
        let table = {
            type: "table",
            title: "trading Information",
            cols: ["initial equity", "current equity", Pair_A+"position", Pair_B+"position", Pair_A+"holding price", Pair_B+"holding price", Pair_A+"profits", Pair_B+"profits", Pair_A+"price", Pair_B+"price", "current price comparison", "average price comparison", "deviation from average price", "loop delay"],
            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) + "`") //This function will display a table containing the above information on the robot page.
        Sleep(Interval * 1000) //Sleep time in ms
    }
}

Más.