Notificación de una orden de negociación Precio: 1000, // Precio de la orden; tenga en cuenta que este atributo de las órdenes de mercado puede ser 0 o -1 Cantidad: 10, // Cantidad de la orden; tenga en cuenta que este atributo de las órdenes de mercado puede ser cantidad de dinero, no número de moneda DealAmount : 10, // Volumen ejecutado; si la interfaz de la plataforma no proporciona este tipo de datos, probablemente use 0 para rellenar AvgPrice : 1000, // El precio promedio ejecutado; note que algunas plataformas no proporcionan estos datos. Estado: 1, // Estado del pedido; hacer referencia al estado del pedido en las constantes, como ORDER_STATE_CLOSED Tipo: 0, // Tipo de orden; hacer referencia al tipo de orden en las constantes, como ORDER_TYPE_BUY Offset: 0 // La dirección de apertura y cierre de la orden en los datos de orden de futuros de criptomonedas;ORDER_OFFSET_OPEN es la posición abierta, mientras que ORDER_OFFSET_CLOSE es la dirección de cierre ContractType : // Este atributo es para órdenes al contado, es decir, cadena nula; La propiedad de la orden de futuros es el código específico del contrato ¿ Por qué?


##### MarketOrder

Market depth order, that is, ```exchange.GetDepth()``` returns the data structure of the elements in the **Bids** and **Asks** arrays in the data.

```javascript
{
    Price   : 1000,              // Price
    Amount  : 1                  // Volume
}
Profundidad

La profundidad del mercado se devuelve por la funciónexchange.GetDepth().

{
    Asks    : [...],             // The array of sell orders, namely MarketOrder array, sorted by price from low to high
    Bids    : [...],             // The array of buy orders, namely MarketOrder array, sorted by price from high to low
    Time    : 1567736576000      // Millisecond-level timestamp
}
Cuenta

Información de la cuenta, devuelta por la funciónexchange.GetAccount()Los datos devueltos en la estructura están relacionados con los pares de operaciones y códigos de contrato establecidos actualmente.

{
    Info            : {...},     // After requesting the platform interface, this attribute is not available in the raw data that the platform interface responds to, during the backtest
    Balance         : 1000,      // The available amount of quote currency; if the trading pair is BTC_USDT in the spot trading, "balance" refers to the current USDT amount. In the USDT-margined futures contract, "balance" refers to the available margin amount in USDT
    FrozenBalance   : 0,         // Here "balance" refers to the frozen amount of the assets for pending orders
    Stocks          : 1,         // The available amount of base currency; if the trading pair is BTC_USDT in the spot trading, "stocks" refers to the current BTC amount. In the crypto-margined futures contract, "stocks" refers to the available margin amount (base currency)
    FrozenStocks    : 0          // Here "stocks" refers to the frozen amount of the assets for pending orders
}
Posición

Para la información sobre las posiciones mantenidas en el comercio de futuros, elel conjuntode estePositionestructura es devuelta por la funciónexchange.GetPosition() function.

{
    Info            : {...},     // After requesting the platform interface, this attribute is not available in the raw data that the platform interface responds to, during the backtest
    MarginLevel     : 10,        // The leverage size of positions; if this data is not provided by the platform interface, fill in the data by calculation, possibly with errors
    Amount          : 100,       // Position volume; the contract quantity of positions is normally positive integer. Notice every platform might have different contract specifications, such as contract multiplier and value, etc., so the rules for ordering might be different; for example, Binance contract might order by 0.1
    FrozenAmount    : 0,         // The quantity of frozen positions, used as the number of temporary position freeze when close positions and pending orders
    Price           : 10000,     // Average position price; in principle, the attribute is the average price of the entire position (which does not involve in the settlement); if the platform interface does not provide the data, use the existing average position price of the platform to fill in (which involves in the settlement)
    Profit          : 0,         // Position floating profit and loss, namely the failure of realizing position profit and loss. If the platform interface does not provide the data, use other profit and loss data from the interface to fill in; the unit of the profit and loss values is the same as the unit of the current contract margin 

    Type            : 0,         // PD_LONG is a long position, while PD_SHORT is a short position
    ContractType    : "quarter", // Contract code; for more details, refer to the transmitted parameters in the description of the function "SetContractType"
    Margin          : 1          // Margin occupied by positions; if the platform interface does not provide the data, use 0 to fill in
}

Para los futuros de criptomonedas, prestar atención a laPositionmatriz de estructura devuelta por la funciónexchange.GetPosition()En cuanto a los atributos en su estructura de datos de posición, tales comoFrozenAmount, ProfityMargin, diferentes definiciones de datos pueden ser devueltos por diferentes objetos de intercambio, cuando llamanexchange.GetPosition()Por ejemplo, algunos intercambios no incluyen datos congelados de posición, lo que indica que el intercambio de datos de posición de la plataforma de intercambio de datos de intercambio de datos de intercambio de datos de intercambio de datos de intercambio de datos de intercambio de datos de intercambio de datos de intercambio de datos.FrozenAmountes 0. Si necesita calcular algunos datos, puede utilizar los datos de origen en el atributoInfopara el cálculo y el análisis.

Funciones globales

Versión

Version()devuelve el número de versión actual del sistema; el valor devuelto: tipo de cadena.

El sueño (millisegundo)

Sleep(Millisecond), función de reposo, hace que el programa se detenga por un período de tiempo.Millisecondes un tipo de número. La unidad de parámetro es el milisegundo, por ejemplo:Sleep(1000)significa dormir por un segundo. Operaciones de apoyo con un tiempo de reposo inferior a 1 milisegundo, tales como ajusteSleep (0.1)El parámetro mínimo soportado es0.000001Un nanosegundo es igual a1e-6 milliseconds.

Nota: Cuando usted escribe estrategias enPythonEl lenguaje, la funciónSleep(Millisecond)No se recomienda utilizar la función de intervalo de votación y tiempo de espera.time.time(second)de lastimeLa biblioteca enPythonPorque cuando se utiliza la funcióntime.time(second)En la estrategia, el programa de estrategia realmente esperará por un cierto número de segundos (el parámetrosecondes el segundo número de la configuración de la pausa), lo que dará lugar a una estrategia muy lenta backtest.

¿Es Virtual?

IsVirtual(), para determinar si se trata de una prueba de retroceso simulada. Devuelve el estado del backtest simuladotrue, y el verdadero robot regresafalse.

El correo...

Mail(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)es una función de envío de correo. Valor del parámetro: todos son de tipo de cadena. Valor de retorno: tipo bool;truese devuelve después de un envío exitoso.smtpServersirve para el buzón de envíosmtp; smtpUsernamees la cuenta de buzón;smtpPasswordes la contraseña STMP del buzón;mailToes la cuenta de buzón destinatario;titlees el título del correo enviado;bodyes el contenido del correo enviado, por ejemplo:

function main(){
    Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
}
def main():
    Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
void main() {
    Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body");
}
  • ElMail_GoLa función es una versión asíncrona de la funciónMailEn el caso de: Su uso es similar a la funciónexchange.Go.

    function main() {
        var r1 = Mail_Go("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
        var r2 = Mail_Go("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
        
        var ret1 = r1.wait()
        var ret2 = r2.wait()
        
        Log("ret1:", ret1)
        Log("ret2:", ret2)
    }
    
    # Not supported
    
    // Not supported
    

Nota: El servidor de Alibaba Cloud puede bloquear algunos puertos, por lo que el correo no se puede enviar.smtp.qq.com:587(QQmail), el puerto está disponible para pruebas. Si se produce un error:unencrypted connection, usted necesita modificar el formato de parámetros desmtpServeren la funciónMailpara:ssl://xxxxx.com:xxxPor ejemplo, el método SMTP ssl de QQmail:ssl://smtp.qq.com:465o biensmtp://xxxxx.com:xxx.

SetErrorFilter ((...) y el resto de las aplicaciones

ErrorFilter(RegEx), filtrando los registros de errores. Valor del parámetro: tipo de cadena. Los errores que coincidan con esta expresión regular no se cargarán en el sistema de registros, y se puede llamar varias veces (los registros filtrados no se escribirán en el archivo de base de datos del ID de bot correspondiente en los registros / bots en el contenido del docker, para evitar la expansión del archivo de base de datos causada por informes de errores frecuentes).

function main() {
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
}
def main():
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
void main() {
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused");
}

Filtrar la información de error de una interfaz:

function main() {
    // Randomly check a non-existent order with ID 123; intentionally make the interface report an error
    var order = exchange.GetOrder("123")
    Log(order)
    // Filter http502 errors and GetOrder interface errors; after set the error filter, the second time to call GetOrder no longer reports errors
    SetErrorFilter("502:|GetOrder")
    order = exchange.GetOrder("123")
    Log(order)
}
def main():
    order = exchange.GetOrder("123")
    Log(order)
    SetErrorFilter("502:|GetOrder")
    order = exchange.GetOrder("123")
    Log(order)
void main() {
    TId orderId;
    Order order = exchange.GetOrder(orderId);
    Log(order);
    SetErrorFilter("502:|GetOrder");
    order = exchange.GetOrder(orderId);
    Log(order);
}

¿ Qué te parece?

GetPid()devuelve el ID del proceso del bot.

function main(){
    var id = GetPid()
    Log(id)
}
def main():
    id = GetPid()
    Log(id)
void main() {
    auto id = GetPid();
    Log(id);
}

Obtenga el último error

GetLastError()Obtiene la información de error más reciente; generalmente, no necesita ser utilizado, porque el programa cargará la información de error al sistema de registro automáticamente.GetLastError(), se borrará la memoria caché de errores; cuando se llame de nuevo, no se devolverá la información de error registrada la última vez.

function main(){
    // Because the order with ID number of 123 does not exist, an error is reported
    exchange.GetOrder("123")
    var error = GetLastError()
    Log(error)
}
def main():
    exchange.GetOrder("123")
    error = GetLastError()
    Log(error)
void main() {
    // The type of order ID: TId; so no string can be passed in, and we can trigger it by placing an order that does not meet the exchange specifications
    exchange.GetOrder(exchange.Buy(1, 1));
    auto error = GetLastError();
    Log(error);
}

Obtener el comando

GetCommand()obtiene comandos interactivos (utf-8). Obtiene el comando enviado por la interfaz interactiva de la estrategia y borra la caché; si no hay comando, devuelve una cadena vacía.button name: parameter; si no hay un parámetro en los controles interactivos (por ejemplo, el control del botón sin cuadro de entrada), el comando es el nombre del botón.

function main(){
    while(true) { 
        var cmd = GetCommand()
        if (cmd) { 
            Log(cmd)
        }
        Sleep(1000) 
    }
}
def main():
    while True:
        cmd = GetCommand()
        if cmd:
            Log(cmd)
        Sleep(1000)
void main() {
    while(true) {
        auto cmd = GetCommand();
        if(cmd != "") {
            Log(cmd);
        }
        Sleep(1000);
    }
}

El sistema subyacente tiene una estructura de cola para registrar el comando interactivo.GetCommand()se llama, el comando interactivo que entra primero en la cola se sacará (si no hay un comando interactivo, una cadena vacía).

Ejemplos de uso de controles interactivos; establecer controles interactivos en la interfaz de edición de estrategias:

img

Diseñar códigos interactivos en la estrategia:

function main() {
    while (true) {
        LogStatus(_D())
        var cmd = GetCommand()
        if (cmd) {
            Log("cmd:", cmd)    
            var arr = cmd.split(":")
            if (arr[0] == "buy") {
                Log("Buy, the control without quantity")
            } else if (arr[0] == "sell") {
                Log("Sell, the control with quantity: ", arr[1])
            } else {
                Log("Other controls trigger:", arr)
            }
        }
        Sleep(1000)
    } 
}
def main():
    while True:
        LogStatus(_D())
        cmd = GetCommand()
        if cmd:
            Log("cmd:", cmd)
            arr = cmd.split(":")
            if arr[0] == "buy":
                Log("Buy, the control without quantity")
            elif arr[0] == "sell":
                Log("Sell, the control with quantity:", arr[1])
            else:
                Log("Other controls trigger:", arr)
        Sleep(1000)
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
void split(const string& s,vector<string>& sv,const char flag = ' ') {
    sv.clear();
    istringstream iss(s);
    string temp;

    while (getline(iss, temp, flag)) {
        sv.push_back(temp);
    }
    return;
}

void main() {
    while(true) {
        LogStatus(_D());
        auto cmd = GetCommand();
        if (cmd != "") {
            vector<string> arr;
            split(cmd, arr, ':');
            if(arr[0] == "buy") {
                Log("Buy, the control without quantity");
            } else if (arr[0] == "sell") {
                Log("Sell, the control with quantity:", arr[1]);
            } else {
                Log("Other controls trigger:", arr);
            }
        }
        Sleep(1000);
    }
}

Obtener Meta()

La funciónGetMeta()devuelve el valor deMetacuando se genera el token de estrategia, el valor de retorno de la función es de tipo cadena. Aplicaciones: por ejemplo, la estrategia debe establecer limitaciones de activos para diferentes inquilinos. Nota: cuando se genera el token de estrategia, la duración deMetano puede exceder de 190 cadenas; la función, sólo aplicable a la negociación real y requiere el docker más reciente.GetMeta()devuelve nulo.

la información relacionada demostrada por las solicitudes

function main() {
    // The largest assets value of quote currency allowed by the strategy
    
    // Get the metadata generated when the strategy token is established
    var level = GetMeta()
    
    // Check the corresponding conditions of "Meta"
    if (level == "level1") {
        // "-1" indicates no limitation 
        maxBaseCurrency = -1       
    } else if (level == "level2") {
        maxBaseCurrency = 10     
    } else if (level == "level3") {
        maxBaseCurrency = 1
    } else {
        maxBaseCurrency = 0.5
    }
    
    while(1) {
        Sleep(1000)
        var ticker = exchange.GetTicker()
        
        // Check the assets value
        var acc = exchange.GetAccount()
        if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Stocks + acc.FrozenStocks) {
            // Stop the execution of the strategy trading logic 
            LogStatus(_D(), "level:", level, "The position exceeds the strategy token's using limitation, and stop the execution of the strategy trading logic!")
            continue
        }
        
        // Other trading logic
        
        // Export the information of status bar normally
        LogStatus(_D(), "level:", level, "The strategy runs normally! Ticker data: \n", ticker)
    }
}
def main():
    maxBaseCurrency = null
    level = GetMeta()
    
    if level == "level1":
        maxBaseCurrency = -1       
    elif level == "level2":
        maxBaseCurrency = 10     
    elif level == "level3":
        maxBaseCurrency = 1
    else:
        maxBaseCurrency = 0.5
    
    while True:
        Sleep(1000)
        ticker = exchange.GetTicker()        
        acc = exchange.GetAccount()
        if maxBaseCurrency != -1 and maxBaseCurrency < acc["Stocks"] + acc["FrozenStocks"]:
            LogStatus(_D(), "level:", level, "The position exceeds the strategy token's using limitation, and stop the execution of the strategy trading logic!")
            continue        
        
        # Other trading logic
        
        # Export the information of status bar normally
        LogStatus(_D(), "level:", level, "The strategy runs normally! Ticker data: \n", ticker)
void main() {
    auto maxBaseCurrency = 0.0;
    auto level = GetMeta();
    
    if (level == "level1") {
        maxBaseCurrency = -1;  
    } else if (level == "level2") {
        maxBaseCurrency = 10;
    } else if (level == "level3") {
        maxBaseCurrency = 1;
    } else {
        maxBaseCurrency = 0.5;
    }
    
    while(1) {
        Sleep(1000);
        auto ticker = exchange.GetTicker();  
        auto acc = exchange.GetAccount();
        if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Stocks + acc.FrozenStocks) {
            // Stop the execution of the strategy trading logic
            LogStatus(_D(), "level:", level, "The position exceeds the strategy token's using limitation, and stop the execution of the strategy trading logic!");
            continue;
        }
        
        // Other trading logic
        
        // Export the information of status bar normally
        LogStatus(_D(), "level:", level, "The strategy runs normally! Ticker data: \n", ticker);
    }
}

Marca el número.

Dial(Address, Timeout), accesos de enchufe originales, soportestcp, udp, tlsyunixEl valor de los parámetros:Addresses un tipo de cadena; unidad es segundo; si el tiempo está fuera, la funciónDial(...)devuelve un valor vacío.

Descripción detallada deAddressParámetro:

Detalles del parámetro
Parámetros para el ajuste de la funciónDial Separar añadiendo el|símbolo después de la dirección normal:wss://ws.okx.com:8443/ws/v5/public; si las hay|caracteres en la cadena de parámetros, uso||Conectar cada parámetro con&Por ejemplo, los parámetros de sustitución y compresión ss5 se establecen juntos:Dial("wss://ws.okx.com:8443/ws/v5/public|proxy=socks5://xxx:9999&compress=gzip_raw&mode=recv").
En el protocolo ws, los parámetros relacionados de compresión de datos:compress=parameter value comprimir es el método de compresión; los parámetros de compresión se pueden elegir degzip_rawygzip, etc. Si el método gzip no es gzip estándar, puede usar el método de extensión:gzip_raw, es decir, añadir la configuracióncompress=gzip_rawdespués del separador|, y utilizar el&símbolo y el siguiente parámetro de modo a separar.
En el protocolo ws, los parámetros relacionados de compresión de datos:mode=parameter value modo es el modo, que incluye tres opciones, a saber:dual, sendyrecv. duales bidireccional, enviando y recibiendo datos comprimidos.sendes enviar datos comprimidos.recvLa función principal de este sistema es recibir datos comprimidos y descomprimirlos localmente.
Parámetros relacionados para el ajuste de los calcetines5 proxy:proxy=parameter value proxy es el ajuste de proxy ss5; formato del valor del parámetro:socks5://name:pwd@192.168.0.1:1080; name es el nombre de usuario del servidor ss5; pwd es la contraseña de inicio de sesión del servidor ss5; 1080 es el puerto del servidor ss5
En el protocolo ws, los parámetros relacionados para configurar el auto-reconexión subyacente:reconnect=parameter value "reconectar" se refiere a si se ha de configurar "reconectar";reconnect=trueLa configuración predeterminada es no volver a conectar.
En el protocolo ws, los parámetros relacionados para configurar el auto-reconexión subyacente:interval=parameter value el intervalo es el intervalo de reintentos en milisegundos,interval=10000es el intervalo de 10 segundos para el nuevo ensayo, y el valor predeterminado es de 1 segundo, es decir,interval=1000.
En el protocolo ws, los parámetros relacionados para configurar el auto-reconexión subyacente:payload= parameter value carga útil es el mensaje de suscripción que se enviará cuando ws se vuelva a conectar, por ejemplo:payload=okok.
function main(){
    // Dial supports tcp://, udp://, tls://, unix:// protocol, so you can add a parameter to specify the number of seconds to timeout
    var client = Dial("tls://www.baidu.com:443")  
    if (client) {
        // "Write" can be followed by a numeric parameter to specify the timeout, and "write" returns the number of bytes successfully sent
        client.write("GET / HTTP/1.1\nConnection: Closed\n\n")
        while (true) {
            // "Read" can be followed by a numeric parameter to specify the timeout, in millisecond. Return null to indicate error, timeout or closed socket
            var buf = client.read()
            if (!buf) {
                 break
            }
            Log(buf)
        }
        client.close()
    }
}
def main():
    client = Dial("tls://www.baidu.com:443")
    if client:
        client.write("GET / HTTP/1.1\nConnection: Closed\n\n")
        while True:
            buf = client.read()
            if not buf:
                break
            Log(buf)
        client.close()
void main() {
    auto client = Dial("tls://www.baidu.com:443");
    if(client.Valid) {
        client.write("GET / HTTP/1.1\nConnection: Closed\n\n");
        while(true) {
            auto buf = client.read();
            if(buf == "") {
                break;
            }
            Log(buf);
        }
        client.close();
    }
}

La funciónreadadmite los siguientes parámetros:

  • Cuando no se pasan parámetros, se bloquea hasta que hay un mensaje, comows.read().
  • Cuando se introduzcan parámetros, especificar el tiempo de espera para los mensajes en espera en milisegundos, por ejemplo:ws.read(2000)especifica que el tiempo de espera será de dos segundos (2000 milisegundos).
  • Los dos parámetros siguientes sólo son válidos parawebsocketEn el caso de: Pasando el parámetro-1significa regresar inmediatamente independientemente de si hay algún mensaje, comows.read(-1)¿ Qué pasa? Pasando el parámetro-2significa devolver inmediatamente independientemente de si hay algún mensaje, pero sólo se devuelve el último mensaje, y el mensaje en el búfer se descartará, comows.read(-2).

La funciónread()Descripción del búfer: Si los datos enviados por el protocolo ws son de largos intervalos entre la estrategiaread()La estructura de datos del búfer es una cola, con un límite superior de 2000. Después de superar el 2000, los datos más recientes entran en el búfer y los datos más antiguos se borran.

Escenarios \ReadParámetro de la función No hay parámetro Parámetro: -1 Parámetro: -2 Parámetro: 2000 (unidad: ms)
Buffer ya tiene datos Devuelva los datos más antiguos inmediatamente Devuelva los datos más antiguos inmediatamente Devuelva los últimos datos de inmediato. Devuelva los datos más antiguos inmediatamente
No hay datos en el búfer Devuelva los datos cuando haya datos bloqueados Devuelva nulo inmediatamente Devuelva nulo inmediatamente Esperar 2000ms, devuelve nulo si no hay datos, devuelve los datos si hay datos
Ws conexión se desconecta o la capa inferior se vuelve a conectar La función read() devuelve nulo, es decir: , y la función write() devuelve 0. Si se detecta esta situación, la función close() se puede usar para cerrar la conexión. Si se establece la reconexión automáticamente, no necesitará cerrarse, y la capa inferior del sistema se volverá a conectar automáticamente.
  • Soporte para el protocolo wss (WebSocket) Acceso a la interfaz de mercado del websocket de Binance:

    function main() {
        LogStatus("connecting...")
        // Access Binance websocket interface
        var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
        if (!client) {
            Log("connection failed, program exited")
            return
        }
        
        while (true) {
            // "read" only returns the obtained data after call "read" 
            var buf = client.read()      
            if (!buf) {
                break
            }
            var table = {
                type: 'table',
                title: 'Quote Chart',
                cols: ['Currency', 'Highest', 'Lowest', 'Buy One', 'Sell One', 'Last Executed Price', 'Volume', 'Update Time'],
                rows: []
            }
            var obj = JSON.parse(buf)
            _.each(obj, function(ticker) {
                table.rows.push([ticker.s, ticker.h, ticker.l, ticker.b, ticker.a, ticker.c, ticker.q, _D(ticker.E)])
            })
            LogStatus('`' + JSON.stringify(table) + '`')
        }
        client.close()
    }
    
    import json
    def main():
        LogStatus("connecting...")
        client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
        if not client:
            Log("Connection failed, program exited")
            return 
        
        while True:
            buf = client.read()
            if not buf:
                break
            table = {
                "type" : "table", 
                "title" : "Quote Chart", 
                "cols" : ['Currency', 'Highest', 'Lowest', 'Buy One', 'Sell One', 'Last Executed Price', 'Volume', 'Update Time'], 
                "rows" : [] 
            }
            obj = json.loads(buf)
            for i in range(len(obj)):
                table["rows"].append([obj[i]["s"], obj[i]["h"], obj[i]["l"], obj[i]["b"], obj[i]["a"], obj[i]["c"], obj[i]["q"], _D(int(obj[i]["E"]))])
            LogStatus('`' + json.dumps(table) + '`')
        client.close()
    
    void main() {
        LogStatus("connecting...");
        auto client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
        if(!client.Valid) {
            Log("Connection failed, program exited");
            return;
        }
        
        while(true) {
            auto buf = client.read();
            if(buf == "") {
                break;
            }
            json table = R"({
                "type" : "table", 
                "title" : "Quote Chart", 
                "cols" : ['Currency', 'Highest', 'Lowest', 'Buy One', 'Sell One', 'Last Executed Price', 'Volume', 'Update Time'], 
                "rows" : []
            })"_json;
            json obj = json::parse(buf);
            for(auto& ele : obj.items()) {
                table["rows"].push_back({ele.value()["s"], ele.value()["h"], ele.value()["l"], ele.value()["b"], ele.value()["a"], ele.value()["c"], 
                    ele.value()["q"], _D(ele.value()["E"])});
            }
            LogStatus("`" + table.dump() + "`");
        }
        client.close();
    }
    

    Interfaz de mercado de soporte web OKX de acceso:

    var ws = null 
    function main(){
          var param = {
            "op": "subscribe",
            "args": [{
                "channel": "tickers",
                "instId": "BTC-USDT"
            }]
        }
        // When call the function "Dial", specify "reconnect=true" and set to reconnect; specify "payload" as the message to be sent when reconnect. When websocket closes the connection, it will automatically reconnect and send the message
        ws = Dial("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true&payload="+ JSON.stringify(param))
        if(ws){
            ws.write(JSON.stringify(param))
            var pingCyc = 1000 * 20
            var lastPingTime = new Date().getTime()
            while(true){
                var nowTime = new Date().getTime()
                var ret = ws.read()
                Log("ret:", ret)
                if(nowTime - lastPingTime > pingCyc){
                    var retPing = ws.write("ping")
                    lastPingTime = nowTime
                    Log("send: ping", "#FF0000")
                }
                LogStatus("current time:", _D())
                Sleep(1000)
            }
        }
    }
    
    function onexit() {
        ws.close() 
        Log("exit")
    }
    
    import json
    import time
    
    ws = None
    def main():
        global ws 
        param = {
            "op": "subscribe",
            "args": [{
                "channel": "tickers",
                "instId": "BTC-USDT"
            }]
        }
        ws = Dial("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true&payload=" + json.dumps(param))
        if ws:
            pingCyc = 1000 * 20
            lastPingTime = time.time() * 1000
            while True:
                nowTime = time.time() * 1000
                ret = ws.read()
                Log("ret:", ret)
                if nowTime - lastPingTime > pingCyc:
                    retPing = ws.write("ping")
                    lastPingTime = nowTime
                    Log("send: ping", "#FF0000")
                LogStatus("current time:", _D())
                Sleep(1000)
    
    def onexit():
        ws.close()
        Log("exit")
    
    auto objWS = Dial("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true");  
    
    void main() {
            json param = R"({"op": "subscribe", "args": ["spot/ticker:ETH-USDT"]})"_json;
             "op": "subscribe",
            "args": [{
                "channel": "tickers",
                "instId": "BTC-USDT"
            }]
        })"_json;
        
        objWS.write(param.dump());
        if(objWS.Valid) {
            uint64_t pingCyc = 1000 * 20;
            uint64_t lastPingTime = Unix() * 1000;
            while(true) {
                uint64_t nowTime = Unix() * 1000;
                auto ret = objWS.read();
                Log("ret:", ret);
                if(nowTime - lastPingTime > pingCyc) {
                    auto retPing = objWS.write("ping");
                    lastPingTime = nowTime;
                    Log("send: ping", "#FF0000");
                }
                LogStatus("current time:", _D());
                Sleep(1000);
            }
        }
    }  
    
    void onexit() {
        objWS.close();
        Log("exit");
    }
    

    Acceso a la interfaz de mercado de Huobi websocket:

    var ws = null   
    
    function main(){
        var param = {"sub": "market.btcusdt.detail", "id": "id1"}
        ws = Dial("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload="+ JSON.stringify(param))
        if(ws){
            while(1){
                var ret = ws.read()
                Log("ret:", ret)
                // Respond to heartbeat
                try {
                    var jsonRet = JSON.parse(ret)
                    if(typeof(jsonRet.ping) == "number") {
                        var strPong = JSON.stringify({"pong" : jsonRet.ping})
                        ws.write(strPong)
                        Log("respond ping, send pong:", strPong, "#FF0000")
                    }
                } catch(e) {
                    Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message)
                }
                
                LogStatus("current time:", _D())
                Sleep(1000)
            }
        }
    }  
    
    function onexit() {
        ws.close() 
        Log("execute function ws.close()")
    }
    
    import json
    ws = None  
    
    def main():
        global ws
        param = {"sub" : "market.btcusdt.detail", "id" : "id1"}
        ws = Dial("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload=" + json.dumps(param))
        if ws:
            while True:
                ret = ws.read()
                Log("ret:", ret)              
                # Respond to heartbeat 
                try:
                    jsonRet = json.loads(ret)
                    if "ping" in jsonRet and type(jsonRet["ping"]) == int:
                        strPong = json.dumps({"pong" : jsonRet["ping"]})
                        ws.write(strPong)
                        Log("respond ping, send pong:", strPong, "#FF0000")
                except Exception as e:
                    Log("e:", e)
                    
                LogStatus("current time: ", _D())
                Sleep(1000)
        
    def onexit():
        ws.close()
        Log("execute function ws.close()")  
    
    using namespace std;
    void main() {
        json param = R"({"sub" : "market.btcusdt.detail", "id" : "id1"})"_json;
        auto ws = Dial("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload=" + param.dump());
        if(ws.Valid) {
            while(true) {
                auto ret = ws.read();
                Log("ret:", ret);              
                // Respond to heartbeat
                try 
                {
                    auto jsonRet = json::parse(ret);
                    if(jsonRet["ping"].is_number()) {
                        json pong = R"({"pong" : 0})"_json;
                        pong["pong"] = jsonRet["ping"];
                        auto strPong = pong.dump();
                        ws.write(strPong);
                        Log("respond ping, send pong:", strPong, "#FF0000");
                    }
                } catch(exception &e) 
                {
                    Log("e:", e.what());
                }
                
                LogStatus("current time:", _D());
                Sleep(1000);
            }
        }
    }  
    
    void onexit() {
        // ws.close();
        Log("execute function ws.close()");
    }
    

    Interfaz de verificación de la interfaz del websocket que accede a OKX:

      function getLogin(pAccessKey, pSecretKey, pPassphrase) {
        // Signature function for login
        var ts = (new Date().getTime() / 1000).toString()
        var login = {
            "op": "login",
            "args":[{
                "apiKey"    : pAccessKey,
                "passphrase" : pPassphrase,
                "timestamp" : ts,
                "sign" : exchange.HMAC("sha256", "base64", ts + "GET" + "/users/self/verify", pSecretKey)
            }]
        }    
        return login
    }    
    
    var client_private = null 
    function main() {
        // Because the read function adopts the timeout setting, the timeout error is filtered, otherwise there will be redundant error output
        SetErrorFilter("timeout")
        
        // Position channel subscription information
        var posSubscribe = {
            "op": "subscribe",
            "args": [{
                "channel": "positions",
                "instType": "ANY"
            }]
        }    
    
        var accessKey = "xxx"
        var secretKey = "xxx"
        var passphrase = "xxx"
    
        client_private = Dial("wss://ws.okx.com:8443/ws/v5/private")
        client_private.write(JSON.stringify(getLogin(accessKey, secretKey, passphrase)))
        Sleep(3000)  // When logging in, we cannot subscribe to private channels immediately, we need to wait for server response
        client_private.write(JSON.stringify(posSubscribe))
        if (client_private) {
            var lastPingTS = new Date().getTime()
            while (true) {
                var buf = client_private.read(-1)
                if (buf) {
                    Log(buf)
                }
                
                // Detect disconnection, reconnect
                if (buf == "" && client_private.write(JSON.stringify(posSubscribe)) == 0) {
                    Log("Disconnection detected, connection closed, reconnect")
                    client_private.close()
                    client_private = Dial("wss://ws.okx.com:8443/ws/v5/private")
                    client_private.write(JSON.stringify(getLogin(accessKey, secretKey, passphrase)))
                    Sleep(3000)
                    client_private.write(JSON.stringify(posSubscribe))
                }
                
                // Send heartbeat packets
                var nowPingTS = new Date().getTime()
                if (nowPingTS - lastPingTS > 10 * 1000) {
                    client_private.write("ping")
                    lastPingTS = nowPingTS
                }            
            }        
        }
    }    
    
    function onexit() {    
        var ret = client_private.close()
        Log("Close the connection!", ret)
    }
    
    import json
    import time
        
    def getLogin(pAccessKey, pSecretKey, pPassphrase):
        ts = str(time.time())
        login = {
            "op": "login",
            "args":[{
                "apiKey"    : pAccessKey,
                "passphrase" : pPassphrase,
                "timestamp" : ts,
                "sign" : exchange.HMAC("sha256", "base64", ts + "GET" + "/users/self/verify", pSecretKey)
            }]
        }
        return login     
    
    client_private = None 
    def main():
        global client_private
        SetErrorFilter("timeout")
        
        posSubscribe = {
            "op": "subscribe",
            "args": [{
                "channel": "positions",
                "instType": "ANY"
            }]
        }      
    
        accessKey = "xxx"
        secretKey = "xxx"
        passphrase = "xxx"
        
        client_private = Dial("wss://ws.okx.com:8443/ws/v5/private")
        client_private.write(json.dumps(getLogin(accessKey, secretKey, passphrase)))
        Sleep(3000)
        client_private.write(json.dumps(posSubscribe))
        if client_private:
            lastPingTS = time.time() * 1000
            while True:
                buf = client_private.read(-1)
                if buf:
                    Log(buf)
                
                if buf == "" and client_private.write(json.dumps(posSubscribe)) == 0:
                    Log("Disconnection detected, connection closed, reconnect")
                    ret = client_private.close()
                    client_private = Dial("wss://ws.okx.com:8443/ws/v5/private")
                    client_private.write(json.dumps(getLogin(accessKey, secretKey, passphrase)))
                    Sleep(3000)
                    client_private.write(json.dumps(posSubscribe))
                
                nowPingTS = time.time() * 1000
                if nowPingTS - lastPingTS > 10 * 1000:
                    client_private.write("ping")
                    lastPingTS = nowPingTS    
    
    def onexit():
        ret = client_private.close()
        Log("Close the connection!", ret)
    

Auto client_private = Marcar (wss://ws.okx.com:8443/ws/v5/private);

json getLogin ((cuadrícula pAccessKey, cuadrícula pSecretKey, cuadrícula pPassphrase) { auto ts = std::to_string ((Unix))); json login = R"({ op: login, ¿Por qué no lo haces? apiKey: , frase de acceso: , tiempo: , signo : ¿ Qué pasa? ¿Qué está pasando? login[args][0][apiKey] = pAccessKey; login[args][0][passphrase] = pPassphrase; Login[args][0][timestamp] = ts; login[args][0][sign] = exchange.HMAC(sha256, base64, ts + GET + /users/self/verify, pSecretKey); Regreso de inicio de sesión; ¿ Por qué?

No hay nada en la caja principal. SetErrorFilter (( tiempo de espera ); Json posSubscribe = R"({ op: se suscribe, ¿Por qué no lo haces? canal: posiciones, instTipo: ANÍ ¿ Qué pasa? ¿Qué está pasando?

auto accessKey = "xxx";
auto secretKey = "xxx";
auto passphrase = "xxx";

client_private.write(getLogin(accessKey, secretKey, passphrase).dump());
Sleep(3000);
client_private.write(posSubscribe.dump());  

if (client_private.Valid) {
    uint64_t lastPingTS = Unix() * 1000;    

    while (true) {
        auto buf = client_private.read(-1);
        if (buf != "") {
            Log(buf);
        }
        if (buf == "") {
            if (client_private.write(posSubscribe.dump()) == 0) {
                Log("Disconnection detected, connection closed, reconnect");
                client_private.close();
                client_private = Dial("wss://ws.okx.com:8443/ws/v5/private");
                client_private.write(getLogin(accessKey, secretKey, passphrase).dump());
                Sleep(3000);
                client_private.write(posSubscribe.dump());
            }
        }
        
        uint64_t nowPingTS = Unix() * 1000;
        if (nowPingTS - lastPingTS > 10 * 1000) {
            client_private.write("ping");
            lastPingTS = nowPingTS;
        }
    }
}

}

No se puede emitir. cliente_private.close ((); el nombre de la aplicación Registro de salida ¿ Por qué?


#### HttpQuery(...)

```HttpQuery(Url, PostData, Cookies, Headers, IsReturnHeader)``` is an access of web URL. Parameter value: all are of string types.

Note:
* The ```HttpQuery(...)``` function only supports ```JavaScript``` language.
* For the ```Python``` language, you can use ```urllib``` to send http requests directly.

```HttpQuery(...)``` is mainly used to access the exchange interfaces that do not require signatures, such as public interfaces including market information.
An example of an API that does not require a signature to access OKX: the return value is a ```JSON``` string, which can be parsed by using the function ```JSON.parse()``` in JavaScript language strategies.

```js
function main(){
  // An example of GET access without parameters
  var info = JSON.parse(HttpQuery("https://www.okx.com/api/v5/public/time"))
  Log(info)
  // An example of GET access with parameters
  var ticker = JSON.parse(HttpQuery("https://www.okx.com/api/v5/market/books?instId=BTC-USDT"))
  Log(ticker)
}
import json
import urllib.request
def main():
    # HttpQuery does not support Python, you can use urllib/urllib2 instead
    info = json.loads(urllib.request.urlopen("https://www.okx.com/api/v5/public/time").read().decode('utf-8'))
    Log(info)
    ticker = json.loads(urllib.request.urlopen("https://www.okx.com/api/v5/market/books?instId=BTC-USDT").read().decode('utf-8'))
    Log(ticker)
void main() {
    auto info = json::parse(HttpQuery("https://www.okx.com/api/v5/public/time"));
    Log(info);
    auto ticker = json::parse(HttpQuery("https://www.okx.com/api/v5/market/books?instId=BTC-USDT"));
    Log(ticker);
}

Para obtener el contenido de retorno de una URL, si el segundo parámetroPostDataes en forma de cadenaa=1&b=2&c=abc, presentado porPOST, otros porPUT; el parámetro dePostDataes{method:'PUT', data:'a=1&b=2&c=abc'}.

ElPostDataParámetro también puede ser unJSON string.

El formato del parámetroCookieses:a=10; b=20; con cada parámetro separado por un punto y coma;¿ Qué pasa? El formato del parámetroHeaderses:User-Agent: Mobile\nContent-Type: text/html; con cada parámetro separado por un carácter de línea nueva\n.

El segundo parámetro,PostData, puede ser personalizado, por ejemplo:HttpQuery("http://www.abc.com", {method:'PUT', data:'a=1&b=2&c=abc'}); nota: si necesita establecer el tiempo de espera para elHttpQueryfunción, se puede añadir eltimeoutatributo en{method:'put',data:'a=1&B=2&C=ABC'}(el valor predeterminado es de 60 segundos).

Establezca un tiempo de espera de 1 segundo:HttpQuery("http://www.abc.com", {method:'PUT', data:'a=1&b=2&c=abc', timeout:1000})

El tercer parámetro es necesario para pasar la cadenaCookie, peroPOSTNo se requiere para establecer el segundo parámetro a cero. Durante la prueba de simulación, debido a que la URL no se puede simular, la función devuelve una cadena fijaDummy DataPuede usar esta interfaz para enviar mensajes de texto o interactuar con otras interfaces API.

GETejemplo de llamada de método:HttpQuery("http://www.baidu.com"). POSTejemplo de llamada de método:HttpQuery("http://www.163.com", "a=1&b=2&c=abc").

Ejemplo de invocación de la devoluciónHeader:

HttpQuery("http://www.baidu.com", null, "a=10; b=20", "User-Agent: Mobile\nContent-Type: text/html", true)  // will return {Header: HTTP Header, Body: HTML}
  • La funciónHttpQueryutiliza la configuración de proxy:

    function main() {
        // This time, set proxy and send http request; without username and password, this http request will be sent through the proxy
        HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/")
    
        // Set the proxy and send http request this time, enter the user name and password, only the current call of HttpQuery takes effect, then call HttpQuery ("http://www.baidu.com") again so that the proxy will not be used
        HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/")
    }
    
    # If HttpQuery does not support Python, you can use Python urllib2 library
    
    void main() {
        HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/");
        HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/");
    }
    
  • La funciónHttpQueryEs una versión asíncronaHttpQuery_GoEn el caso de: El método de uso es similar a la funciónexchange.Go, como acceder a la interfaz pública de la bolsa de forma asíncrona para obtener datos de mercado agregados.

    function main() {
        // Set up the first asyncthread
        var r1 = HttpQuery_Go("https://www.okx.com/api/v5/market/tickers?instType=SPOT")
        // Set up the second asyncthread
        var r2 = HttpQuery_Go("https://api.huobi.pro/market/tickers")
        
        // Get the return value of the first asyncthread
        var tickers1 = r1.wait()
        // Get the return value of the second asyncthread
        var tickers2 = r2.wait()
        
        // Print result
        Log("tickers1:", tickers1)
        Log("tickers2:", tickers2)
    }
    
    # Not supported
    
    // Not supported
    
  • El uso de la funciónHttpQuery(...)en el sistema de ensayos posteriores: Los datos se pueden obtener utilizandoHttpQuery(...)para enviar solicitudes (sólo apoyo)GETEn el sistema de backtest se impone un límite de 20 veces en el backtest yHttpQuery(...)el acceso almacenará los datos en caché, mientras que la funciónHttpQuery(...)devuelve los datos almacenados en caché en el segundo acceso a la misma URL (no más solicitudes web reales).

    Podemos ejecutar un programa de servicio en un servidor o un dispositivo que responde a las solicitudes enviadas porHttpQuery(...)en el programa de estrategia, y el programa de servicio en lenguaje Go para pruebas se muestra como sigue:

    package main
    import (
        "fmt"
        "net/http"
        "encoding/json"
    )
    
    func Handle (w http.ResponseWriter, r *http.Request) {
        defer func() {
            fmt.Println("req:", *r)
            ret := map[string]interface{}{
                "schema" : []string{"time","open","high","low","close","vol"},
                "data" : []interface{}{
                    []int64{1564315200000,9531300,9531300,9497060,9497060,787},
                    []int64{1564316100000,9495160,9495160,9474260,9489460,338},
                },
            }
            b, _ := json.Marshal(ret)
            w.Write(b)
        }()
    }
    
    func main () {
        fmt.Println("listen http://localhost:9090")
        http.HandleFunc("/data", Handle)
        http.ListenAndServe(":9090", nil)
    }
    

    Utilice la funciónHttpQuery(...)para enviar solicitudes durante las pruebas posteriores de la estrategia:

    function main() {
        // You can write the IP address of the device where the service program is run
        Log(HttpQuery("http://xxx.xx.x.xxx:9090/data?msg=hello"));
        Log(exchange.GetAccount());
    }
    
    # If HttpQuery does not support Python, you can use Python urllib2 library
    
    void main() {
        //  You can write the IP address of the device where the service program is run
        Log(HttpQuery("http://xxx.xx.x.xxx:9090/data?msg=hello"));
        Log(exchange.GetAccount());
    }
    

    img

    Soporta la transcodificación de los datos de respuesta en la solicitud, y también soporta la codificación común. Especificación de laPostDataParámetro:{method: "GET", charset: "GB18030"}puede realizar la transcodificación de datos de respuesta (GB18030).

Codificación

Encode(algo, inputFormat, outputFormat, data, keyFormat, key string), la función codifica los datos de acuerdo con los parámetros pasados, y devuelve un valor de cadena.

El parámetroalgoes el algoritmo utilizado para el cálculo de codificación, que se puede establecer como: raw (sin algoritmo), sign, signTx, md4, md5, sha256, sha512, sha1, keccak256, sha3.224, sha3.256, sha3.384, sha3.512, sha3.keccak256, sha3.keccak512, sha512.384, sha512.256, sha512.224, emd160, ripke2b.256, blake2b.512, blake2s.1288, blake2s.256dataEl tratamiento de los datos es el que se trata.inputFormat/outputFormat/keyFormatLos parámetros soportan métodos de codificación tales comoraw, hex, base64ystring¿ Qué pasa? Si elkeyFormatel parámetro no está vacío, elkeyParámetro se utiliza para el cifrado (HMAC), de lo contrario el predeterminadokeyCuando el ` ` alg