O recurso está a ser carregado... Carregamento...

Instruções da API FMZ

Autora:Zero., Criado: 2020-04-20 10:19:00, Atualizado: 2023-04-12 14:44:56

Notificação de uma ordem de negociação Preço: 1000, // Preço da ordem; observe que este atributo das ordens de mercado pode ser 0 ou -1 Montante: 10, // Montante da ordem; observe que este atributo das ordens de mercado pode ser o montante do dinheiro, não o número da moeda DealAmount : 10, // Volume executado; se a interface da plataforma não fornecer este tipo de dados, provavelmente use 0 para preencher AvgPrice : 1000, // O preço médio executado; observe que algumas plataformas não fornecem esses dados. Status: 1, // Order status; referir-se ao status da ordem nas constantes, tais como ORDER_STATE_CLOSED Tipo: 0, // Tipo de ordem; referir o tipo de ordem nas constantes, como ORDER_TYPE_BUY Offset: 0 // A direção de ordem aberta e fechada nos dados de ordem de futuros de criptomoedas;ORDER_OFFSET_OPEN é a posição aberta, enquanto ORDER_OFFSET_CLOSE é a direção de fechamento ContractType : // Este atributo é para ordens spot, ou seja, cadeia nula; A propriedade de uma ordem de futuros é o código específico do contrato - Não.


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

A profundidade de mercado é devolvida pela funçãoexchange.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
}
Conta

Informações da conta, devolvidas pela funçãoexchange.GetAccount()Os dados devolvidos na estrutura estão relacionados com os pares de negociação e códigos de contrato atualmente definidos.

{
    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
}
Posição

Para as informações sobre as posições detidas na negociação de futuros, omatrizdo presentePositionestrutura é devolvida pela funçãoexchange.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 os futuros de criptomoeda, preste atenção aoPositionmatriz de estrutura devolvida pela funçãoexchange.GetPosition()Quanto aos atributos na sua estrutura de dados de posição, tais comoFrozenAmount, ProfiteMargin, diferentes definições de dados podem ser devolvidos por diferentes objetos de troca, quando eles chamamexchange.GetPosition()Por exemplo, algumas bolsas não incluem dados congelados de posição, o que indicaFrozenAmounté 0, se necessário calcular alguns dados, você pode usar os dados de origem no atributoInfopara cálculo e análise.

Funções globais

Versão (((

Version()Retorna o número de versão corrente do sistema; valor de retorno: tipo de cadeia.

Durma (milissegundos)

Sleep(Millisecond), função de sono, faz o programa pausar por um período de tempo.Millisecondé um tipo de número. A unidade de parâmetro é milissegundo, por exemplo:Sleep(1000)significa dormir por um segundo. Operações de suporte com tempo de sono inferior a 1 milissegundo, tais como configuraçãoSleep (0.1). O parâmetro mínimo suportado é0.000001Um nanossegundo é igual a1e-6 milliseconds.

Nota: Quando você escreve estratégias emPythonlinguagem, a funçãoSleep(Millisecond)Não é recomendável utilizar a funçãotime.time(second)dotimeBiblioteca emPythonPorque quando se usa a funçãotime.time(second)na estratégia, o programa de estratégia vai realmente esperar por um certo número de segundos (o parâmetrosecondé o segundo número da configuração de pausa), o que levará a um backtest de estratégia muito lento.

É Virtual?

IsVirtual(), para determinar se se trata de um backtest simulado. Retorna o estado do backtest simuladotrue, e o verdadeiro robô retornafalse.

Correio...

Mail(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)é uma função de envio de correio. Valor do parâmetro: todos são de tipo de string. Valor de retorno: tipo bool;trueé devolvido após um envio bem-sucedido.smtpServerserve para a caixa de correio de enviosmtp; smtpUsernameé a conta da caixa de correio;smtpPasswordé a senha STMP da caixa de correio;mailToé a conta da caixa de correio destinatária;titleé o título do correio enviado;bodyÉ o conteúdo do correio enviado, por exemplo:

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");
}
  • OMail_Gofunção é uma versão assíncrona de funçãoMail- Não. A sua utilização é semelhante à funçãoexchange.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: O servidor Alibaba Cloud pode bloquear algumas portas, para que o e-mail não possa ser enviado.smtp.qq.com:587(QQmail), a porta está disponível para testes. Se ocorrer um erro:unencrypted connection, você precisa modificar o formato do parâmetro desmtpServerna funçãoMailpara:ssl://xxxxx.com:xxxPor exemplo, o método SMTP ssl do QQmail:ssl://smtp.qq.com:465ousmtp://xxxxx.com:xxx.

SetErrorFilter ((...)

ErrorFilter(RegEx), filtragem de registos de erros. Valor do parâmetro: tipo de cadeia. Os erros correspondidos por esta expressão regular não serão carregados no sistema de logs, e ele pode ser chamado várias vezes (logs filtrados não serão escritos no arquivo de banco de dados da ID de bot correspondente em logs/bots no conteúdo do docker, para evitar a expansão do arquivo de banco de dados causada por relatórios de erros frequentes).

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 as informações de erro de uma interface:

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

GetPid ((()

GetPid()Retorna o ID do processo bot. Retorna o valor: tipo de string.

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

GetLastError (()

GetLastError()obtém a informação de erro mais recente; geralmente, não precisa ser usado, porque o programa irá carregar a informação de erro para o sistema de log automaticamente.GetLastError(), o cache de erro será apagado; quando ele for chamado novamente, a informação de erro registrada da última vez não será devolvida.

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

Obter Comando ((()

GetCommand()obtém comandos interativos (utf-8). Ele obtém o comando enviado pela interface interativa estratégia e limpar o cache; se não houver nenhum comando, ele retorna uma cadeia vazia. O formato de comando retornado ébutton name: parameter; se não houver um parâmetro nos comandos interativos (por exemplo, o comando do botão sem caixa de entrada), o comando é o nome do botão.

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

O sistema subjacente tem uma estrutura de fila para registrar o comando interativo.GetCommand()é chamado, o comando interativo que primeiro entrar na fila será retirado (se não houver comando interativo, uma cadeia vazia).

Exemplos de utilização de controles interativos; definir controles interativos na interface de edição de estratégia:

img

Projetar códigos interativos na estratégia:

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

GetMeta ((()

A funçãoGetMeta()Retorna o valor deMetaescrito quando o token de estratégia é gerado, o valor de retorno da função é de tipo string. Aplicações: por exemplo, a estratégia deve estabelecer limitações de ativos para diferentes inquilinos. Nota: quando o token de estratégia é gerado, a duração doMetanão pode exceder 190 cadeias; a função, aplicável apenas à negociação real e requer o mais recente docker.GetMeta()Retorna nulo.

A informação relacionada demonstrada pelos pedidos

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

Marque...

Dial(Address, Timeout), Acesso ao Socket original, suportestcp, udp, tlseunixValor do parâmetro:Addressé um tipo de string; unidade é segundo; se o tempo está fora, a funçãoDial(...)Retorna um valor vazio.

Descrição pormenorizadaAddressParâmetro:

Parâmetros detalhados
Parâmetros para definir a funçãoDial Separar adicionando o|símbolo após o endereço normal:wss://ws.okx.com:8443/ws/v5/publicSe existirem;|caracteres na cadeia de parâmetros, uso||Conecte cada parâmetro com&Por exemplo, os parâmetros de substituição e de compressão ss5 são definidos juntos:Dial("wss://ws.okx.com:8443/ws/v5/public|proxy=socks5://xxx:9999&compress=gzip_raw&mode=recv").
No protocolo ws, os parâmetros relacionados da compressão de dados:compress=parameter value comprimir é o método de compressão; os parâmetros de compressão podem ser escolhidos entregzip_rawegzip, etc. Se o método gzip é gzip não padrão, você pode usar o método de extensão:gzip_raw, isto é, adicionar a configuraçãocompress=gzip_rawapós o separador|, e utilizar o&símbolo e o próximo parâmetro de modo a separar.
No protocolo ws, os parâmetros relacionados da compressão de dados:mode=parameter value modo é o modo, incluindo três opções, a saber:dual, senderecv. dualé bidirecional, enviando e recebendo dados comprimidos.sendé enviar dados comprimidos.recvA função principal do sistema é receber dados comprimidos e descomprimí-los localmente.
Parâmetros relacionados para a fixação de meias5 substitutivas:proxy=parameter value proxy é a definição de proxy ss5; formato do valor do parâmetro:socks5://name:pwd@192.168.0.1:1080; name é o nome de usuário do servidor ss5; pwd é a senha de login do servidor ss5; 1080 é a porta do servidor ss5
No protocolo ws, os parâmetros relacionados para definir a auto-reconexão subjacente:reconnect=parameter value "Reconectar" significa se deve ser definida a "reconectar";reconnect=trueé para invocar a reconexão; a configuração padrão é não reconectar.
No protocolo ws, os parâmetros relacionados para definir a auto-reconexão subjacente:interval=parameter value o intervalo é o intervalo de reatendimento em milissegundos,interval=10000é o intervalo de tentativa de 10 segundos e a definição por defeito é de 1 segundo, ou seja,interval=1000.
No protocolo ws, os parâmetros relacionados para definir a auto-reconexão subjacente:payload= parameter value A carga útil é a mensagem de subscrição a enviar quando a ws se reconecta, por exemplo: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();
    }
}

A funçãoreadSuporta os seguintes parâmetros:

  • Quando nenhum parâmetro é passado, ele bloqueia até que haja uma mensagem, comows.read().
  • Ao transmitir parâmetros, especificar o timeout para as mensagens em espera na unidade de milissegundos; por exemplo:ws.read(2000)especifica que o timeout é de dois segundos (2000 milissegundos).
  • Os dois parâmetros seguintes só são válidos parawebsocket- Não. Passagem de parâmetro-1significa retornar imediatamente, independentemente da existência de qualquer mensagem, comows.read(-1)- Não. Passagem de parâmetro-2significa retornar imediatamente, independentemente de haver alguma mensagem, mas somente a mensagem mais recente é devolvida, e a mensagem no buffer será descartada, comows.read(-2).

A funçãoread()Descrição do buffer: Se os dados empurrados pelo protocolo ws são de longos intervalos entre a estratégiaread()A estrutura de dados do buffer é uma fila, com um limite superior de 2000. Depois de exceder 2000, os dados mais recentes são inseridos no buffer e os dados mais antigos são apagados.

Scenários \ReadParâmetro da função Nenhum parâmetro Parâmetro: -1 Parâmetro: -2 Parâmetro: 2000 (unidade: ms)
O buffer já tem dados. Devolva os dados mais antigos imediatamente. Devolva os dados mais antigos imediatamente. Devolva os dados mais recentes imediatamente. Devolva os dados mais antigos imediatamente.
Não há dados no buffer Retornar os dados quando há dados bloqueados Retorne nulo imediatamente Retorne nulo imediatamente Espere por 2000ms, retornar nulo se não houver dados, retornar os dados se houver dados
Ws ligação é desconectada ou a camada inferior é reconectada A função read() retorna nulo, ou seja: , e a função write() retorna 0. Se esta situação for detectada, a função close() pode ser usada para fechar a conexão.
  • Apoio ao protocolo wss (WebSocket) Acesso à interface de mercado do websocket da 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();
    }
    

    Interface de mercado do websocket OKX:

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

    Acesso à interface de mercado do websocket Huobi:

    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()");
    }
    

    Interface de verificação da interface do websocket que acede ao 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 = Dial ((wss://ws.okx.com:8443/ws/v5/private);

json getLogin ((string pAccessKey, string pSecretKey, string pPassphrase) { auto ts = std::to_string ((Unix()); json login = R"({ op: login, - Não, não. apiKey: senha: , horário: , sinal: Não, não. }) "_json; 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); registo de registo; - Não.

- Não, não. SetErrorFilter ((timeout); Json posSubscribe = R"({ op: abone, - O que é que estás a fazer? canal: posições, instType: any - Não. }) "_json;

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

}

Anulação cliente_private.close ((); Registo ((saída); - Não.


#### 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 obter o conteúdo de retorno de um URL, se o segundo parâmetroPostDataestá na forma de uma cadeiaa=1&b=2&c=abc, apresentado porPOST, outros porPUT; o parâmetro dePostDataé{method:'PUT', data:'a=1&b=2&c=abc'}.

OPostDataParâmetro também pode ser umJSON string.

O formato do parâmetroCookiesé:a=10; b=20; com cada parâmetro separado por um ponto e vírgula;- Não. O formato do parâmetroHeadersé:User-Agent: Mobile\nContent-Type: text/html; com cada parâmetro separado por um carácter de linha nova\n.

O segundo parâmetro,PostData, podem ser personalizados, por exemplo:HttpQuery("http://www.abc.com", {method:'PUT', data:'a=1&b=2&c=abc'})Nota: se precisar de definir o timeout para oHttpQueryfunção, você pode adicionar otimeoutatributo em{method:'put',data:'a=1&B=2&C=ABC'}(o padrão é 60 segundos).

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

O terceiro parâmetro é necessário para passar a cadeiaCookieMas...POSTNão é necessário definir o segundo parâmetro para nulo. Durante o teste de simulação, porque o URL não pode ser simulado, a função retorna uma cadeia fixaDummy Data. Você pode usar esta interface para enviar mensagens de texto ou interagir com outras interfaces de API.

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

Exemplo de invocação de retornoHeader:

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}
  • A funçãoHttpQueryusa configurações 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/");
    }
    
  • A funçãoHttpQueryÉ a versão assíncronaHttpQuery_Go- Não. O método de utilização é similar à funçãoexchange.Go, como aceder à interface pública da bolsa de valores de forma assíncrona para obter dados 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
    
  • Utilização da funçãoHttpQuery(...)no sistema de backtest: Os dados podem ser obtidos utilizandoHttpQuery(...)para enviar pedidos (apenas apoio)GETO limite de 20 vezes é imposto no backtest e o limite de 20 vezes é fixado no backtest.HttpQuery(...)acesso irá armazenar os dados em cache, enquanto a funçãoHttpQuery(...)Retorna os dados armazenados em cache no segundo acesso ao mesmo URL (não há mais solicitações web reais).

    Podemos executar um programa de serviço em um servidor ou um dispositivo que responde aos pedidos enviados porHttpQuery(...)no programa de estratégia, e o programa de serviço em linguagem Go para testes é mostrado da seguinte forma:

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

    Use a funçãoHttpQuery(...)para enviar pedidos durante o backtesting da estratégia:

    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

    Ele suporta a transcodificação dos dados de resposta na solicitação, e também suporta codificação comum. Especificação doPostDataParâmetro:{method: "GET", charset: "GB18030"}Pode realizar a transcodificação de dados de resposta (GB18030).

Codificação

Encode(algo, inputFormat, outputFormat, data, keyFormat, key string), a função codifica os dados de acordo com os parâmetros passados e retorna um valor de string.

O parâmetroalgoé o algoritmo usado para o cálculo de codificação, que pode ser definido para: raw (sem 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, blake2rip2b.256, blake2b.512, blake2s.1288, blake2s.256dataOs dados a processar são os seguintes:inputFormat/outputFormat/keyFormatOs parâmetros suportam métodos de codificação como:raw, hex, base64estring- Não. Se okeyFormatParâmetro não está vazio, okeyParâmetro é usado para criptografia (HMAC), caso contrário o padrãokeyQuando o ` ` alg


Mais.