oparameter is set tosigneorsigne, thele paramètre key``` est requis.

function main(){
    Log(Encode("md5", "raw", "hex", "hello"))
    Log(Encode("sha512", "raw", "base64", "hello"))
    Log(Encode("keccak256", "raw", "hex", "unwrapWETH9(uint256,address)"))

    Log(Encode("raw", "string", "hex", "example"))          // 6578616d706c65
    Log(Encode("raw", "hex", "string", "6578616d706c65"))   // example
}
def main():
    Log(Encode("md5", "raw", "hex", "hello", "", ""))
    Log(Encode("sha512", "raw", "base64", "hello", "", ""))
    Log(Encode("keccak256", "raw", "hex", "unwrapWETH9(uint256,address)", "", ""))

    Log(Encode("raw", "string", "hex", "example", "", ""))
    Log(Encode("raw", "hex", "string", "6578616d706c65", "", ""))
void main(){
    Log(Encode("md5", "raw", "hex", "hello"));
    Log(Encode("sha512", "raw", "base64", "hello"));
    Log(Encode("keccak256", "raw", "hex", "unwrapWETH9(uint256,address)"));
    
    Log(Encode("raw", "string", "hex", "example"));          // 6578616d706c65
    Log(Encode("raw", "hex", "string", "6578616d706c65"));   // example
}

Le paramètrealgoprend également en charge le codage et le décodage de chaînes, telles quetext.encoder.utf8, text.decoder.utf8, text.encoder.gbkettext.decoder.gbk.

function main(){
    var ret1 = Encode("text.encoder.utf8", "raw", "hex", "hello")     // e4bda0e5a5bd
    Log(ret1)    
    var ret2 = Encode("text.decoder.utf8", "hex", "string", ret1)   
    Log(ret2)

    var ret3 = Encode("text.encoder.gbk", "raw", "hex", "hello")      // c4e3bac3
    Log(ret3)
    var ret4 = Encode("text.decoder.gbk", "hex", "string", ret3)
    Log(ret4)
}
def main():
    ret1 = Encode("text.encoder.utf8", "raw", "hex", "hello", "", "")     # e4bda0e5a5bd
    Log(ret1)    
    ret2 = Encode("text.decoder.utf8", "hex", "string", ret1, "", "")   
    Log(ret2)

    ret3 = Encode("text.encoder.gbk", "raw", "hex", "hello", "", "")      # c4e3bac3
    Log(ret3)
    ret4 = Encode("text.decoder.gbk", "hex", "string", ret3, "", "")
    Log(ret4)
void main(){
    auto ret1 = Encode("text.encoder.utf8", "raw", "hex", "hello");     // e4bda0e5a5bd
    Log(ret1);    
    auto ret2 = Encode("text.decoder.utf8", "hex", "string", ret1);   
    Log(ret2);

    auto ret3 = Encode("text.encoder.gbk", "raw", "hex", "hello");      // c4e3bac3
    Log(ret3);
    auto ret4 = Encode("text.decoder.gbk", "hex", "string", ret3);
    Log(ret4);
}

UnixNano (en anglais)

UnixNano()renvoie l'horodatage au niveau des nanosecondes; si vous avez besoin d'obtenir l'horodatage au niveau des millisecondes, vous pouvez utiliser le code suivant:

function main() {
    var time = UnixNano() / 1000000
    Log(_N(time, 0))
}
def main():
    time = UnixNano()
    Log(time)
void main() {
    auto time = UnixNano();
    Log(time);
}

Unix (()

Unix()renvoie une heure en secondes.

function main() {
    var t = Unix()
    Log(t)
}
def main():
    t = Unix()
    Log(t)
void main() {
    auto t = Unix();
    Log(t);
}

Je suis désolée.

GetOS()renvoie des informations sur le système où se trouve le docker.

function main() {
    Log("GetOS:", GetOS())
}
def main():
    Log("GetOS:", GetOS())
void main() {
    Log("GetOS:", GetOS());
}

La sortie du journal du docker exécuté parMac OSde l'ordinateur Apple:

Je ne sais pas.

darwinest le nom deMac OS system.

MD5 ((String)

MD5(String); valeur du paramètre: type de chaîne.

function main() {
    Log("MD5", MD5("hello world"))
}
def main():
    Log("MD5", MD5("hello world"))
void main() {
    Log("MD5", MD5("hello world"));
}

Sortie du journal:

Je suis désolé, je suis désolé.

Je suis désolé.

DBExec(), sa valeur de paramètre peut être une chaîne, un nombre ou un Boolean, null et d'autres types; retourner la valeur: l'objet avec les résultats d'exécution en langage SQLite.DBExec(), la fonction d'interface de la base de données, à travers les paramètres de passage, peut exécuter la base de données du bot (base de données SQLite).SQLiteLe système dans la base de données des robots sauvegarde des tables, y compris:kvdb, cfg, log, profitetchart; ne pas utiliser les tables mentionnées ci-dessus.DBExec()Ne prend en charge que le vrai robot.

  • Prend en charge la base de données de mémoire Pour les paramètres de fonctionnementDBExec, si leLe secteurla déclaration commence par:, les opérations dans la base de données de mémoire seront plus rapides sans écrire de fichiers.

    function main() {
        var strSql = [
            ":CREATE TABLE TEST_TABLE(", 
            "TS INT PRIMARY KEY NOT NULL,",
            "HIGH REAL NOT NULL,", 
            "OPEN REAL NOT NULL,", 
            "LOW REAL NOT NULL,", 
            "CLOSE REAL NOT NULL,", 
            "VOLUME REAL NOT NULL)"
        ].join("")
        var ret = DBExec(strSql)
        Log(ret)
        
        // Add a piece of data
        Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
        
        // Query the data
        Log(DBExec(":SELECT * FROM TEST_TABLE;"))
    }
    
    def main():
        arr = [
            ":CREATE TABLE TEST_TABLE(", 
            "TS INT PRIMARY KEY NOT NULL,",
            "HIGH REAL NOT NULL,", 
            "OPEN REAL NOT NULL,", 
            "LOW REAL NOT NULL,", 
            "CLOSE REAL NOT NULL,", 
            "VOLUME REAL NOT NULL)"
        ]
        strSql = ""
        for i in range(len(arr)):
            strSql += arr[i]
        ret = DBExec(strSql)
        Log(ret)
        
        # Add a piece of data
        Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
        
        # Query the data
        Log(DBExec(":SELECT * FROM TEST_TABLE;"))
    
    void main() {
        string strSql = ":CREATE TABLE TEST_TABLE(\
            TS INT PRIMARY KEY NOT NULL,\
            HIGH REAL NOT NULL,\
            OPEN REAL NOT NULL,\
            LOW REAL NOT NULL,\
            CLOSE REAL NOT NULL,\
            VOLUME REAL NOT NULL)";
        auto ret = DBExec(strSql);
        Log(ret);
        
        // Add a piece of data
        Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"));
        
        // Query the data
        Log(DBExec(":SELECT * FROM TEST_TABLE;"));
    }
    
  • Créer une table

function main() {
    var strSql = [
        "CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ].join("")
    var ret = DBExec(strSql)
    Log(ret)
}
def main():
    arr = [
        "CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ]
    strSql = ""
    for i in range(len(arr)):
        strSql += arr[i]
    ret = DBExec(strSql)
    Log(ret)
void main() {
    string strSql = "CREATE TABLE TEST_TABLE(\
        TS INT PRIMARY KEY NOT NULL,\
        HIGH REAL NOT NULL,\
        OPEN REAL NOT NULL,\
        LOW REAL NOT NULL,\
        CLOSE REAL NOT NULL,\
        VOLUME REAL NOT NULL)";
    auto ret = DBExec(strSql);
    Log(ret);
}
  • Les opérations d'ajout, de suppression, de requête et de modification enregistrées dans le tableau
function main() {
    var strSql = [
        "CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ].join("")
    Log(DBExec(strSql))
    
    // Add a piece of data
    Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    // Query the data
    Log(DBExec("SELECT * FROM TEST_TABLE;"))
    
    // Modify the data
    Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))    
    
    // Delete the data
    Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))
}
def main():
    arr = [
        "CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ]
    strSql = ""
    for i in range(len(arr)):
        strSql += arr[i]
    Log(DBExec(strSql))
    
    # Add a piece of data
    Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    # Query the data
    Log(DBExec("SELECT * FROM TEST_TABLE;"))
    
    # Modify the data
    Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))
    
    # Delete the data
    Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))
void main() {
    string strSql = "CREATE TABLE TEST_TABLE(\
        TS INT PRIMARY KEY NOT NULL,\
        HIGH REAL NOT NULL,\
        OPEN REAL NOT NULL,\
        LOW REAL NOT NULL,\
        CLOSE REAL NOT NULL,\
        VOLUME REAL NOT NULL)";
    Log(DBExec(strSql));

    // Add a piece of data
    Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"));
    
    // Query the data
    Log(DBExec("SELECT * FROM TEST_TABLE;"));
    
    // Modify the data
    Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000));
    
    // Delete the data
    Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110));
}

UUID (()

UUID(), renvoie un UUID unique de 32 bits, cette fonction n'est disponible que pour les vrais robots.

function main() {
    var uuid1 = UUID()
    var uuid2 = UUID()
    Log(uuid1, uuid2)
}
def main():
    uuid1 = UUID()
    uuid2 = UUID()
    Log(uuid1, uuid2)
void main() {
    auto uuid1 = UUID();
    auto uuid2 = UUID();
    Log(uuid1, uuid2);
}

Le temps d'arrêt d'événement

EventLoop(timeout), renvoie après qu' il y ait unewebsocketlisible ouexchange.Go, HttpQuery_GoSi le paramètretimeoutest réglé sur 0, attendre qu'un événement se produise avant de retourner. S'il est supérieur à 0, définir le temps d'attente de l'événement. S'il est inférieur à 0, retourner le dernier événement immédiatement. Si l'objet retourné n'est pasnull, leEventCette fonction est disponible uniquement pour le trading de vrais robots.

Le premier appel deEventLoopDans le code, le mécanisme d'écoute de l'événement sera initialisé.EventLoopLa structure de file d'attente encapsulée par le système sous-jacent cache jusqu'à 500 callbacks d'événements.EventLoopsi l'appel n'est pas appelé à temps pour suppression pendant l'exécution du programme, les rappels ultérieurs d'événements au-delà de la mémoire cache 500 seront perdus.EventLoopCette fonction n'affectera pas la file d'attente de cache dewebsocketLe système sous-jacent, ni le cache deexchange.GoPour ces caches, vous devez toujours utiliser leurs propres méthodes pour récupérer les données.EventLoopfonction retourne, aucun événement de retour ne sera généré dans leEventLoop function.

L'objectif principal du projetEventLoopLa fonction principale de la plateforme est d'aviser la couche de stratégie que le système sous-jacent a reçu de nouvelles données réseau.EventLoopSi la fonction renvoie un événement, vous n'avez qu'à parcourir toutes les sources de données.websocketconnexion et les objets créés parexchange.GoVous pouvez vous référer à une conception de bibliothèque de classes open source:lien vers la bibliothèque de classe.

function main() {
    var routine_getTicker = exchange.Go("GetTicker")
    var routine_getDepth = exchange.Go("GetDepth")
    var routine_getTrades = exchange.Go("GetTrades")
    
     // Sleep(2000), if the Sleep statement is used here, the subsequent EventLoop function will miss the previous events, because after waiting for 2 seconds, the concurrent function has received the data, and the EventLoop monitoring mechanism will start later, and these events will be missed
     // Unless you start calling EventLoop(-1) on the first line of code, first initialize the listening mechanism of EventLoop, you will not miss these events

     // Log("GetDepth:", routine_getDepth.wait()) If the wait function is called in advance to get the result of the concurrent call of the GetDepth function, the event that the GetDepth function receives the request result will not be returned in the EventLoop function
    var ts1 = new Date().getTime()
    var ret1 = EventLoop(0)
    
    var ts2 = new Date().getTime()
    var ret2 = EventLoop(0)
    
    var ts3 = new Date().getTime()
    var ret3 = EventLoop(0)
    
    Log("The first concurrent task completed was:", _D(ts1), ret1)
    Log("The second concurrent task completed was:", _D(ts2), ret2)
    Log("The third concurrent task completed was:", _D(ts3), ret3)
    
    Log("GetTicker:", routine_getTicker.wait())
    Log("GetDepth:", routine_getDepth.wait())
    Log("GetTrades:", routine_getTrades.wait())
}
import time
def main():
    routine_getTicker = exchange.Go("GetTicker")
    routine_getDepth = exchange.Go("GetDepth")
    routine_getTrades = exchange.Go("GetTrades")
    
    ts1 = time.time()
    ret1 = EventLoop(0)
    
    ts2 = time.time()
    ret2 = EventLoop(0)
    
    ts3 = time.time()
    ret3 = EventLoop(0)
    
    Log("The first concurrent task completed was:", _D(ts1), ret1)
    Log("The second concurrent task completed was:", _D(ts2), ret2)
    Log("The third concurrent task completed was:", _D(ts3), ret3)
    
    Log("GetTicker:", routine_getTicker.wait())
    Log("GetDepth:", routine_getDepth.wait())
    Log("GetTrades:", routine_getTrades.wait())
void main() {
    auto routine_getTicker = exchange.Go("GetTicker");
    auto routine_getDepth = exchange.Go("GetDepth");
    auto routine_getTrades = exchange.Go("GetTrades");
    
    auto ts1 = Unix() * 1000;
    auto ret1 = EventLoop(0);
    
    auto ts2 = Unix() * 1000;
    auto ret2 = EventLoop(0);
    
    auto ts3 = Unix() * 1000;
    auto ret3 = EventLoop(0);
    
    Log("The first concurrent task completed was:", _D(ts1), ret1);
    Log("The second concurrent task completed was:", _D(ts2), ret2);
    Log("The third concurrent task completed was:", _D(ts3), ret3);
    
    Ticker ticker;
    Depth depth;
    Trades trades;
    routine_getTicker.wait(ticker);
    routine_getDepth.wait(depth);
    routine_getTrades.wait(trades);
    
    Log("GetTicker:", ticker);
    Log("GetDepth:", depth);
    Log("GetTrades:", trades);
}

Fonctions intégrées

- Je ne sais pas.

_G(K, V), avec une fonction de dictionnaire global qui peut être enregistré, prend en charge à la fois le backtest et le bot. La structure des données estKVChaque bot possède une base de données distincte. Elle existe toujours après le redémarrage ou à la sortie du docker.Kdoit être une chaîne, qui n'est pas sensible aux majuscules.Vpeut être n'importe quelleJSONLorsque la fonction_G()est appelé et aucun paramètre n'est passé dans l'opération bot, la fonction_G()renvoie leIDdu bot actuel.

function main(){
    // Set a global variable num with a value of 1
    _G("num", 1)     
    // Change a global variable num with the value "ok"
    _G("num", "ok")    
    // Delete global variable num
    _G("num", null)
    // Return the value of the global variable num
    Log(_G("num"))
    // Delete all global variables
    _G(null)
    // Return bot ID 
    var robotId = _G()
}
def main():
    _G("num", 1)     
    _G("num", "ok")    
    _G("num", None)
    Log(_G("num"))
    _G(None)
    robotId = _G()
void main() {
    _G("num", 1);
    _G("num", "ok");
    _G("num", NULL);
    Log(_G("num"));
    _G(NULL);
    // does not support auto robotId = _G();
}

Note: le numéro de série Lorsque vous utilisez_GLa fonction de sauvegarde des données doit être utilisée de manière raisonnable en fonction de la mémoire et de l'espace disque dur du périphérique matériel et ne doit pas être utilisée à mauvais escient.débordement de mémoire problem.

_D ((Temps, Fmt)

_D(Timestamp, Fmt), renvoie les chaînes de temps correspondantes de l'horodatage spécifié. Valeur du paramètre:Timestampest un type numérique, en millisecondes.Fmtest un type de chaîne;Fmtpar défaut:yyyy-MM-dd hh:mm:ss; valeur renvoyée: type de chaîne. Il renvoie la chaîne d'horodatage spécifiée ((ms) et renvoie l'heure actuelle sans passer de paramètres; par exemple:_D()ou_D(1478570053241), dont le format par défaut estyyyy-MM-dd hh:mm:ss.

function main(){
    var time = _D()
    Log(time)
}
def main():
    strTime = _D()
    Log(strTime)
void main() {
    auto strTime = _D();
    Log(strTime);
}

Note: le numéro de série À utiliser_D()dans lePythonDans la stratégie, nous devons faire attention que les paramètres transmis sont des horodatages en seconde (les horodatages au niveau des millisecondes dans leJavaScriptetC ++les stratégies, et 1 seconde = 1000 millisecondes). Dans le bot, lorsque vous utilisez la fonction_D()pour analyser une chaîne d'heure avec un horodatage lisible, vous devez faire attention au fuseau horaire dans le système d'exploitation du programme docker._D()analyse un horodatage comme une chaîne de temps lisible basée sur l'heure du système de docker.

Par exemple, l'analyse d'un horodatage1574993606000avec code:

function main() {
    Log(_D(1574993606000))
}
def main():
    # Beijing time server runs: 2019-11-29 10:13:26, and the docker on another server in another region runs this code will get the results: 2019-11-29 02:13:26
    Log(_D(1574993606))
void main() {
    Log(_D(1574993606000));
}

_N ((Nombre, précision)

_N(Num, Precision), un numéro en virgule flottante formaté. Valeur du paramètre:Numest de type numérique;Precisionest de type entier. Retourne la valeur numérique.

Par exemple:_N(3.1415, 2)supprimera la valeur après deux décimales de3.1415et retourner3.14.

function main(){
    var i = 3.1415
    Log(i)
    var ii = _N(i, 2)
    Log(ii)
}
def main():
    i = 3.1415
    Log(i)
    ii = _N(i, 2)
    Log(ii)
void main() {
    auto i = 3.1415;
    Log(i);
    auto ii = _N(i, 2);
    Log(ii);
}

Si vous avez besoin de changer N chiffres à gauche de la virgule à 0, vous pouvez écrire:

function main(){
    var i = 1300
    Log(i)
    var ii = _N(i, -3)
    // Checking the log shows that it is 1000
    Log(ii)
}
def main():
    i = 1300
    Log(i)
    ii = _N(i, -3)
    Log(ii)
void main() {
    auto i = 1300;
    Log(i);
    auto ii = _N(i, -3);
    Log(ii);
}

Je suis désolé.

_C(function, args…)est une fonction de réessayage, utilisée pour la tolérance aux pannes des interfaces d'obtention d'informations sur le marché et d'acquisition d'ordres inachevés, etc.

L'interface appellera la fonction spécifiée en continu jusqu'à ce qu'elle renvoie avec succès (paramètrefunctionrenvoie une valeur nulle lors de l'appel de la fonction référencée oufalsePar exemple, si vous avez un problème de connexion,_ C(exchange. GetTicker), l'intervalle de réessayage par défaut est de 3 secondes, ce qui peut appeler la fonction_CDelay (...)pour régler l'intervalle de réessai, tel que_CDelay (1000)signifie fonction de changement_CRéessayez à 1 seconde.

Pour les fonctions suivantes:

  • exchange.GetTicker()
  • exchange.GetDepth()
  • exchange.GetTrades()
  • exchange.GetRecords()
  • exchange.GetAccount()
  • exchange.GetOrders()
  • exchange.GetOrder()
  • exchange.GetPosition()

Ils peuvent tous être appelés à faire la tolérance aux pannes par fonction_C(...). La fonction_C(function, args...)n'est pas limité à la tolérance à la défaillance des fonctions énumérées ci-dessus.functionest cité pas appelé, et faites attention qu'il est_C(exchange.GetTicker), pas_C(exchange.GetTicker()).

function main(){
    var ticker = _C(exchange.GetTicker)
    // Adjust _C() function's retry interval to 2 seconds
    _CDelay(2000)
    var depth = _C(exchange.GetDepth)
    Log(ticker)
    Log(depth)
}
def main():
    ticker = _C(exchange.GetTicker)
    _CDelay(2000)
    depth = _C(exchange.GetDepth)
    Log(ticker)
    Log(depth)
void main() {
    auto ticker = _C(exchange.GetTicker);
    _CDelay(2000);
    auto depth = _C(exchange.GetDepth);
    Log(ticker);
    Log(depth);
}

Pour les fonctions avec des paramètres, lorsque l'on utilise_C(...)pour faire une tolérance à la défaillance:

function main(){
    var records = _C(exchange.GetRecords, PERIOD_D1)
    Log(records)
}
def main():
    records = _C(exchange.GetRecords, PERIOD_D1)
    Log(records)
void main() {
    auto records = _C(exchange.GetRecords, PERIOD_D1);
    Log(records);
}

Il peut également être utilisé pour la manipulation de la tolérance à la défaillance des fonctions personnalisées:

var test = function(a, b){
    var time = new Date().getTime() / 1000
    if(time % b == 3){
        Log("Meet the criteria! ", "#FF0000")
        return true
    }
    Log("Retry!", "#FF0000")
    return false
}

function main(){
    var ret = _C(test, 1, 5)
    Log(ret)
}
import time
def test(a, b):
    ts = time.time()
    if ts % b == 3:
        Log("Meet the criteria!", "#FF0000")
        return True
    Log("Retry!", "#FF0000")
    return False

def main():
    ret = _C(test, 1, 5)
    Log(ret)
// C++ does not support this method for fault tolerance of custom functions.

_Cross ((Arr1, Arr2)

_Cross(Arr1, Arr2)renvoie le nombre de périodes de croisement des tableauxArr1etArr2Un nombre positif est la période de hausse, et un nombre négatif est la période de baisse, et 0 signifie la même chose que le prix actuel.

Vous pouvez simuler un ensemble de données pour tester la fonction_Cross(Arr1, Arr2):

// Fast line indicator
var arr1 = [1,2,3,4,5,6,8,8,9]
// Slow line indicator
var arr2 = [2,3,4,5,6,7,7,7,7]
function main(){
    Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
    Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
}
arr1 = [1,2,3,4,5,6,8,8,9]     
arr2 = [2,3,4,5,6,7,7,7,7]
def main():
    Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
    Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
void main() {
    vector<double> arr1 = {1,2,3,4,5,6,8,8,9};
    vector<double> arr2 = {2,3,4,5,6,7,7,7,7};
    Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2));
    Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1));
}

img

Visualiser les données simulées pour l'observation

img

Instructions spécifiques:Fonction intégrée _Analyse croisée et instructions

JSONParse (en anglais)

JSONParse(strJson), la fonction est utilisée pour analyser les chaînes JSON. Les chaînes JSON contenant de grands nombres peuvent être analysées correctement, et les grands nombres seront analysés en types de chaînes. Le système de backtesting ne prend pas en charge cette fonction.

function main() {
    let s1 = '{"num": 8754613216564987646512354656874651651358}'
    Log("JSON.parse:", JSON.parse(s1))    // JSON.parse: {"num":8.754613216564988e+39}
    Log("JSONParse:", JSONParse(s1))      // JSONParse:  {"num":"8754613216564987646512354656874651651358"}
    
    let s2 = '{"num": 123}'
    Log("JSON.parse:", JSON.parse(s2))    // JSON.parse: {"num":123}
    Log("JSONParse:", JSONParse(s2))      // JSONParse:  {"num":123}
}
import json

def main():
    s1 = '{"num": 8754613216564987646512354656874651651358}'
    Log("json.loads:", json.loads(s1))    # json.loads: map[num:8.754613216564987e+39]
    Log("JSONParse:", JSONParse(s1))      # JSONParse:  map[num:8754613216564987646512354656874651651358]
    
    s2 = '{"num": 123}'
    Log("json.loads:", json.loads(s2))    # json.loads: map[num:123]
    Log("JSONParse:", JSONParse(s2))      # JSONParse:  map[num:123]
void main() {
    auto s1 = "{\"num\":8754613216564987646512354656874651651358}";
    Log("json::parse:", json::parse(s1));
    // Log("JSONParse:", JSONParse(s1));   // The function is not supported
    
    auto s2 = "{\"num\":123}";
    Log("json::parse:", json::parse(s2));
    // Log("JSONParse:", JSONParse(s2));   // The function is not supported
}

Couleur personnalisée

Chaque chaîne de messages peut se terminer par une valeur RGB telle que#ff0000, qui représente la couleur de premier plan à afficher.#ff0000112233, les six dernières arrières représentent la couleur de fond.

function main() {
    Log("Red", "#FF0000")
}
def main():
    Log("Red", "#FF0000")
void main() {
    Log("Red", "#FF0000");
}

Informations sur le journal

Lorsque le bot est en cours d'exécution, l'information du journal est enregistrée dans la base de données du bot, qui adopte lesqlite3Les fichiers de base de données se trouvent dans l'appareil avec le programme docker, et l'emplacement exact des fichiers est dans le dictionnaire du programme docker (robotPar exemple: le fichier de base de données de bot avec ID130350est dans l'annuaire../logs/storage/130350 (..est le dictionnaire où le docker de larobotest situé), et le nom du fichier de la base de données est130350.db3.

Les journaux du système de backtest peuvent être téléchargés en cliquant sur [Télécharger le journal[ bouton en bas à droite de la page de backtest après la fin du backtest. Lorsque vous avez besoin de transférer le bot vers un docker sur un autre serveur, vous pouvez déplacer les fichiers de base de données du bot (fichiers de base de données avec l'extension db3) vers le serveur cible de transfert, et définir le nom du fichier à l'ID de bot correspondant sur la plateforme.

Le journal de bord

Log(message)signifie enregistrer un message dans la liste de journaux.messagepeut être de n'importe quel type. Si vous ajoutez le caractère@Après la chaîne, le message entrera dans la file d'attente et sera poussé vers le compte WeChat actuel de la plateforme de trading FMZ Quant, et le Email, Telegram et WebHook dans Push settings sera poussé (ouvrir les pages deTableau de bord, CompteetParamètres de pressionpar des ordonnances de fixation de liens).

Note: le numéro de série

  • Push n'est pas pris en charge dans Debug Tool.
  • Push n'est pas pris en charge dans le Backtest System.
function main() {
    Log("Hello FMZ Quant!@")
    Sleep(1000 * 5)
    // Add the string to #ff0000, print the log in red, and push the message
    Log("Hello, #ff0000@")
}
def main():
    Log("Hello FMZ Quant!@")
    Sleep(1000 * 5)
    Log("Hello, #ff0000@")
void main() {
    Log("Hello FMZ Quant!@");
    Sleep(1000 * 5);
    Log("Hello, #ff0000@");
}

Le WebHookPoussez:

Utilisez le programme de service DEMO écrit enGolang:

package main
import (
    "fmt"
    "net/http"
)

func Handle (w http.ResponseWriter, r *http.Request) {
    defer func() {
        fmt.Println("req:", *r)
    }()
}

func main () {
    fmt.Println("listen http://localhost:9090")
    http.HandleFunc("/data", Handle)
    http.ListenAndServe(":9090", nil)
}

RéglageWebHook: http://XXX.XX.XXX.XX:9090/data?data=Hello_FMZ

Après avoir exécuté le programme de service, exécutez la stratégie et appuyez sur l'information:

function main() {
    Log("msg", "@")
}
def main():
    Log("msg", "@")
void main() {
    Log("msg", "@");
}

Recevoir les informations de poussée, et le programme d'entretien imprime les informations:

listen http://localhost:9090
req: {GET /data?data=Hello_FMZ HTTP/1.1 1 1 map[User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.x.xxxx.xxx Safari/537.36] Accept-Encoding:[gzip]] {} <nil> 0 [] false 1XX.XX.X.XX:9090 map[] map[] <nil> map[] XXX.XX.XXX.XX:4xxx2 /data?data=Hello_FMZ <nil> <nil> <nil> 0xc420056300}

Imprimez lebase64image codéeLa fonctionLogPrend en charge l'impression des images codées enbase64, commence par`, et se termine par`, par exemple:

function main() {
    Log("`data:image/png;base64,AAAA`")
}
def main():
    Log("`data:image/png;base64,AAAA`")
void main() {
    Log("`data:image/png;base64,AAAA`");
}

Logsupporte l'impression dumatplotlib.pyplotles objets dePythondirectement, c'est-à-dire tant que les objets contiennentsavefigméthode, vous pouvez utiliserLogpour imprimer directement, par exemple:

import matplotlib.pyplot as plt 
def main(): 
    plt.plot([3,6,2,4,7,1]) 
    Log(plt)

Changement automatique de langue des journaux imprimésLa fonctionLogprend en charge le changement de langue; lorsque la fonction produit du texte, elle passe automatiquement à la langue correspondante selon le réglage de la langue sur la page de la plateforme.

function main() {
    Log("[trans]Chinese|abc[/trans]")
}
def main():
    Log("[trans]Chinese|abc[/trans]")
void main() {
    Log("[trans]Chinese|abc[/trans]");
}

Le montant de l'impôt sur les sociétés est calculé à partir du montant de l'impôt sur le capital.

LogProfit(Profit)enregistre la valeur du bénéfice, imprime la valeur du bénéfice et dessine une courbe de profit en fonction de la valeur du bénéfice.Résultatsest de type numérique.

Si la fonction se termine par le caractère&, il ne peut réaliser que le dessin du graphique des bénéfices, et non l'impression du journal des bénéfices, tels que:LogProfit(10, '&').

LogProfitReset (en anglais seulement)

LogProfitReset()efface tous les journaux de profit; vous pouvez prendre un paramètre de valeur entière pour spécifier le nombre d'éléments réservés.

function main() {
    // Print 30 points on the income chart, then reset, and only retain the last 10 points 
    for(var i = 0; i < 30; i++) {
        LogProfit(i)
        Sleep(500)
    }
    LogProfitReset(10)
}
def main():
    for i in range(30):
        LogProfit(i)
        Sleep(500)
    LogProfitReset(10)
void main() {
    for(int i = 0; i < 30; i++) {
        LogProfit(i);
        Sleep(500);
    }
    LogProfitReset(10);
}

LogStatus (Msg)

LogStatus(Msg), l'information n'est pas enregistrée dans la liste des journaux, seules les informations d'état actuelles du bot sont mises à jour; elles sont affichées au-dessus du journal et peuvent être appelées plusieurs fois pour mettre à jour l'état. Valeur du paramètre:Msgpeut être de n'importe quel type.

function main() {
    LogStatus('This is a normal status prompt')
    LogStatus('This is a status prompt in red font # ff0000')
    LogStatus('This is a multi-line status message \n I am the second line')
}
def main():
    LogStatus('This is a normal status prompt')
    LogStatus('This is a status prompt in red font # ff0000')
    LogStatus('This is a multi-line status message \nI am the second line')
void main() {
    LogStatus("This is a normal status prompt");
    LogStatus("This is a status prompt in red font # ff0000");
    LogStatus("This is a multi-line status message \nI am the second line");
}

LogStatus(Msg)supporte l'impressionbase64images codées, à commencer par`et finissent par`, tels que:LogStatus("`data:image/png;base64,AAAA`"). LogStatus(Msg)Il soutient l'importation directe dePythonC' est...matplotlib.pyplotl'objet, tant que l'objet contient lesavefigméthode, vous pouvez passer dans la fonctionLogStatus(Msg), tels que:

import matplotlib.pyplot as plt 
def main():
    plt.plot([3,6,2,4,7,1])
    LogStatus(plt) 

L'exemple de sortie de données dans la barre d'état:

function main() {
    var table = {type: 'table', title: 'Position Information', cols: ['Column1', 'Column2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
    // After the JSON order is serialized, add the character "`" on both sides, which is regarded as a complex message format (currently supporting tables)
    LogStatus('`' + JSON.stringify(table) + '`')                    
    // Table information can also appear in multiple lines
    LogStatus('First line message\n`' + JSON.stringify(table) + '`\nThird line message')
    // That supports multiple tables displayed at the same time, and that will be displayed in a group with TAB
    LogStatus('`' + JSON.stringify([table, table]) + '`')
    
    // You can also construct a button in the table, and the strategy uses "GetCommand" to receive the content of the cmd attribute                                
    var table = { 
        type: 'table', 
        title: 'Position operation', 
        cols: ['Column1', 'Column2', 'Action'], 
        rows: [ 
            ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': 'close position'}]
        ]
    }
    LogStatus('`' + JSON.stringify(table) + '`') 
    // Or create a separate button 
    LogStatus('`' + JSON.stringify({'type':'button', 'cmd': 'coverAll', 'name': 'close position'}) + '`') 
    // You can customize the button style (button attribute of bootstrap)
    LogStatus('`' + JSON.stringify({'type':'button', 'class': 'btn btn-xs btn-danger', 'cmd': 'coverAll', 'name': 'close position'}) + '`')
}
import json
def main():
    table = {"type": "table", "title": "Position Information", "cols": ["Column1", "Column2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]}
    LogStatus('`' + json.dumps(table) + '`')
    LogStatus('First line message\n`' + json.dumps(table) + '`\nThird line message')
    LogStatus('`' + json.dumps([table, table]) + '`')

    table = {
        "type" : "table", 
        "title" : "Position operation", 
        "cols" : ["Column1", "Column2", "Action"], 
        "rows" : [
            ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "close position"}]
        ] 
    }
    LogStatus('`' + json.dumps(table) + '`')
    LogStatus('`' + json.dumps({"type": "button", "cmd": "coverAll", "name": "close position"}) + '`')
    LogStatus('`' + json.dumps({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "close position"}) + '`')
void main() {
    json table = R"({"type": "table", "title": "Position Information", "cols": ["Column1", "Column2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
    LogStatus("`" + table.dump() + "`");
    LogStatus("First line message\n`" + table.dump() + "`\nThird line message");
    json arr = R"([])"_json;
    arr.push_back(table);
    arr.push_back(table);
    LogStatus("`" + arr.dump() + "`");

    table = R"({
        "type" : "table", 
        "title" : "Position operation", 
        "cols" : ["Column1", "Column2", "Action"], 
        "rows" : [
            ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "close position"}]
        ] 
    })"_json;
    LogStatus("`" + table.dump() + "`");
    LogStatus("`" + R"({"type": "button", "cmd": "coverAll", "name": "close position"})"_json.dump() + "`");
    LogStatus("`" + R"({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "close position"})"_json.dump() + "`");
}

Définir les fonctions de désactivation et de description des boutons de la barre d'état:

img

function main() {
    var table = {
        type: "table",
        title: "Test the disable and description functions of status bar buttons",
        cols: ["Column1", "Column2", "Column3"], 
        rows: []
    }
    var button1 = {"type": "button", "name": "button1", "cmd": "button1", "description": "This is the first button"}
    var button2 = {"type": "button", "name": "button2", "cmd": "button2", "description": "This is the second button, set to disabled", "disabled": true}
    var button3 = {"type": "button", "name": "button3", "cmd": "button3", "description": "This is the third button, set to enabled", "disabled": false}
    table.rows.push([button1, button2, button3])
    LogStatus("`" + JSON.stringify(table) + "`")
}
import json
def main():
    table = {
        "type": "table",
        "title": "Test the disable and description functions of status bar buttons",
        "cols": ["Column1", "Column2", "Column3"], 
        "rows": []
    }
    button1 = {"type": "button", "name": "button1", "cmd": "button1", "description": "This is the first button"}
    button2 = {"type": "button", "name": "button2", "cmd": "button2", "description": "This is the second button, set to disabled", "disabled": True}
    button3 = {"type": "button", "name": "button3", "cmd": "button3", "description": "This is the third button, set to enabled", "disabled": False}
    table["rows"].append([button1, button2, button3])
    LogStatus("`" + json.dumps(table) + "`")
void main() {
    json table = R"({
        "type": "table",
        "title": "Test the disable and description functions of status bar buttons",
        "cols": ["Column1", "Column2", "Column3"], 
        "rows": []
    })"_json;
    json button1 = R"({"type": "button", "name": "button1", "cmd": "button1", "description": "This is the first button"})"_json;
    json button2 = R"({"type": "button", "name": "button2", "cmd": "button2", "description": "This is the second button, set to disabled", "disabled": true})"_json;
    json button3 = R"({"type": "button", "name": "button3", "cmd": "button3", "description": "This is the third button, set to enabled", "disabled": false})"_json;
    json arr = R"([])"_json;
    arr.push_back(button1);
    arr.push_back(button2);
    arr.push_back(button3);
    table["rows"].push_back(arr);
    LogStatus("`" + table.dump() + "`");
}

Définir le style des boutons de la barre d'état:

img

function main() {
    var table = {
        type: "table",
        title: "status bar button style",
        cols: ["default", "raw", "success", "information", "warning", "danger"], 
        rows: [
            [
                {"type":"button", "class": "btn btn-xs btn-default", "name": "default"},
                {"type":"button", "class": "btn btn-xs btn-primary", "name": "raw"},
                {"type":"button", "class": "btn btn-xs btn-success", "name": "success"},
                {"type":"button", "class": "btn btn-xs btn-info", "name": "information"},
                {"type":"button", "class": "btn btn-xs btn-warning", "name": "warning"},
                {"type":"button", "class": "btn btn-xs btn-danger", "name": "danger"}
            ]
        ]
    }
    LogStatus("`" + JSON.stringify(table) + "`")
}
import json
def main():
    table = {
        "type": "table",
        "title": "status bar button style",
        "cols": ["default", "raw", "success", "information", "warning", "danger"], 
        "rows": [
            [
                {"type":"button", "class": "btn btn-xs btn-default", "name": "default"},
                {"type":"button", "class": "btn btn-xs btn-primary", "name": "raw"},
                {"type":"button", "class": "btn btn-xs btn-success", "name": "success"},
                {"type":"button", "class": "btn btn-xs btn-info", "name": "information"},
                {"type":"button", "class": "btn btn-xs btn-warning", "name": "warning"},
                {"type":"button", "class": "btn btn-xs btn-danger", "name": "danger"}
            ]
        ]
    }
    LogStatus("`" + json.dumps(table) + "`")
void main() {
    json table = R"({
        "type": "table",
        "title": "status bar button style",
        "cols": ["default", "raw", "success", "information", "warning", "danger"], 
        "rows": [
            [
                {"type":"button", "class": "btn btn-xs btn-default", "name": "default"},
                {"type":"button", "class": "btn btn-xs btn-primary", "name": "raw"},
                {"type":"button", "class": "btn btn-xs btn-success", "name": "success"},
                {"type":"button", "class": "btn btn-xs btn-info", "name": "information"},
                {"type":"button", "class": "btn btn-xs btn-warning", "name": "warning"},
                {"type":"button", "class": "btn btn-xs btn-danger", "name": "danger"}
            ]
        ]
    })"_json;
    LogStatus("`" + table.dump() + "`");
}

Combiner la fonctionGetCommand()pour construire la fonction interactive des boutons de la barre d'état:

function test1() {
    Log("Call a custom function")
}

function main() {
    while (true) {
        var table = {
            type: 'table',
            title: 'operation',
            cols: ['Column1', 'Column2', 'Action'],
            rows: [
                ['a', '1', {
                    'type': 'button',                       
                    'cmd': "CoverAll",                      
                    'name': 'close position'                           
                }],
                ['b', '1', {
                    'type': 'button',
                    'cmd': 10,                              
                    'name': 'Send value'
                }],
                ['c', '1', {
                    'type': 'button',
                    'cmd': _D(),                          
                    'name': 'Call a function'
                }],
                ['d', '1', {
                    'type': 'button',
                    'cmd': 'test1',       
                    'name': 'Call a custom function'
                }]
            ]
        }
        LogStatus(_D(), "\n", '`' + JSON.stringify(table) + '`')

        var str_cmd = GetCommand()
        if (str_cmd) {
            Log("Received interactive data str_cmd:", "Types of:", typeof(str_cmd), "Value:", str_cmd)
            if(str_cmd == "test1") {
                test1()
            }
        }

        Sleep(500)
    }
}
import json
def test1():
    Log("Call a custom function")

def main():
    while True:
        table = {
            "type": "table", 
            "title": "Operation", 
            "cols": ["Column1", "Column2", "Action"],
            "rows": [
                ["a", "1", {
                    "type": "button", 
                    "cmd": "CoverAll",
                    "name": "close position"
                }],
                ["b", "1", {
                    "type": "button",
                    "cmd": 10,
                    "name": "Send value" 
                }], 
                ["c", "1", {
                    "type": "button",
                    "cmd": _D(),
                    "name": "Call a function" 
                }],
                ["d", "1", {
                    "type": "button",
                    "cmd": "test1",
                    "name": "Call a custom function" 
                }]
            ]
        }

        LogStatus(_D(), "\n", "`" + json.dumps(table) + "`")
        str_cmd = GetCommand()
        if str_cmd:
            Log("Received interactive data str_cmd", "Types:", type(str_cmd), "Value:", str_cmd)
            if str_cmd == "test1":
                test1()
        Sleep(500)
void test1() {
    Log("Call a custom function");
}

void main() {
    while(true) {
        json table = R"({
            "type": "table", 
            "title": "Operation", 
            "cols": ["Column1", "Column2", "Action"],
            "rows": [
                ["a", "1", {
                    "type": "button", 
                    "cmd": "CoverAll",
                    "name": "close position"
                }],
                ["b", "1", {
                    "type": "button",
                    "cmd": 10,
                    "name": "Send value" 
                }], 
                ["c", "1", {
                    "type": "button",
                    "cmd": "",
                    "name": "Call a function" 
                }],
                ["d", "1", {
                    "type": "button",
                    "cmd": "test1",
                    "name": "Call a custom function" 
                }]
            ]
        })"_json;
        table["rows"][2][2]["cmd"] = _D();
        LogStatus(_D(), "\n", "`" + table.dump() + "`");
        auto str_cmd = GetCommand();
        if(str_cmd != "") {
            Log("Received interactive data str_cmd", "Type:", typeid(str_cmd).name(), "Value:", str_cmd);
            if(str_cmd == "test1") {
                test1();
            }
        }
        Sleep(500);
    }
}

Lors de la construction d'un bouton de barre d'état pour l'interaction, les données d'entrée sont également prises en charge, et la commande interactive est finalement capturée par leGetCommand()fonction. Pour ajouter uninputélément à la structure de données d'un bouton de commande dans la barre d'état, par exemple, ajouter"input": {"name": "Number of opening orders", "type": "number", "defValue": 1}à{"type": "button", "cmd": "open", "name": "open position"}, vous pouvez faire apparaître une boîte de dialogue avec un contrôle de boîte d'entrée lorsque vous cliquez sur le bouton (La valeur par défaut dans la boîte d'entrée est 1, qui est définie par les données defValue), et vous pouvez entrer des données à envoyer avec la commande du bouton. Par exemple, lors de l'exécution du code de test suivant, après avoir cliqué sur le bouton Open position, une boîte de dialogue avec une boîte d'entrée apparaît. Après avoir entré111dans la zone d'entrée et en cliquant sur OK, leGetCommandla fonction va capturer le message:open:111.

function main() {
    var tbl = {
        type: "table",
        title: "operation",
        cols: ["column 1", "column2"],
        rows: [
            ["Open position operation", {"type": "button", "cmd": "open", "name": "open position", "input": {"name": "number of opening positions", "type": "number", "defValue": 1}}],
            ["Close position operation", {"type": "button", "cmd": "coverAll", "name": "close all positions"}]
        ] 
    }

    LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")
    while (true) {
        var cmd = GetCommand()
        if (cmd) {
            Log("cmd:", cmd)
        }
        Sleep(1000)
    }
}
import json

def main():
    tbl = {
        "type": "table", 
        "title": "operation", 
        "cols": ["column 1", "column 2"],
        "rows": [
            ["Open position operation", {"type": "button", "cmd": "open", "name": "open position", "input": {"name": "number of opening positions", "type": "number", "defValue": 1}}],
            ["Close position operation", {"type": "button", "cmd": "coverAll", "name": "close all positions"}]
        ]
    }

    LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`")
    while True:
        cmd = GetCommand()
        if cmd:
            Log("cmd:", cmd)
        Sleep(1000)
void main() {
    json tbl = R"({
        "type": "table", 
        "title": "operation", 
        "cols": ["column 1", "column 2"],
        "rows": [
            ["Open position operation", {"type": "button", "cmd": "open", "name": "open position", "input": {"name": "number of opening positions", "type": "number", "defValue": 1}}],
            ["Close position operation", {"type": "button", "cmd": "coverAll", "name": "close all positions"}]
        ]
    })"_json;

    LogStatus(_D(), "\n", "`" + tbl.dump() + "`");
    while(true) {
        auto cmd = GetCommand();
        if(cmd != "") {
            Log("cmd:", cmd);
        }
        Sleep(1000);
    }
}

Combiner les cellules du tableau dessiné par leLogStatus(Msg)fonction:

  • Fusion horizontale

    function main() {
        var table = { 
            type: 'table', 
            title: 'position operation', 
            cols: ['Column1', 'Column2', 'Action'], 
            rows: [ 
                ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': 'close position'}]
            ]
        } 
        var ticker = exchange.GetTicker()
        // Add a row of data, merge the first and second cells, and output the ticker variable in the merged cell
        table.rows.push([{body : JSON.stringify(ticker), colspan : 2}, "abc"])    
        LogStatus('`' + JSON.stringify(table) + '`')
    }
    
    import json
    def main():
        table = {
            "type" : "table",
            "title" : "position operation",
            "cols" : ["Column1", "Column2", "Action"],
            "rows" : [
                ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "close position"}]
            ]
        }
        ticker = exchange.GetTicker()
        table["rows"].append([{"body": json.dumps(ticker), "colspan": 2}, "abc"])
        LogStatus("`" + json.dumps(table) + "`")
    
    void main() {
        json table = R"({
            "type" : "table",
            "title" : "position operation",
            "cols" : ["Column1", "Column2", "Action"],
            "rows" : [