Browse Source

test

release/v0.1
jl777 9 years ago
parent
commit
71a0f81167
  1. 8
      README.md
  2. 48
      iguana/SuperNET_keys.c
  3. 22
      iguana/exchanges777.h
  4. 2
      iguana/iguana777.h
  5. 138
      iguana/iguana_exchanges.c
  6. 39
      iguana/iguana_instantdex.c
  7. 58
      iguana/iguana_tradebots.c

8
README.md

@ -100,3 +100,11 @@ postCall('{"agent":"iguana","method":"test"}'}
iguana_JSON("{\"agent\":\"iguana",\"method\":\"test\"}"); -> direct C function call
iguana can be invoked with a command line argument. if it is a name of a file, it will load it and check to see if it is valid JSON and if it is, it will use it. Otherwise the command line argument needs to be valid JSON to be used and it will process the JSON to initialize account passphrases, exchange apikeys, etc. A few special keys:
"wallet" -> passphrase used for the persistent privkey
"2fafile" -> secondary part (optional) for the persistent privkey
"numhelpers" -> number of helper threads (need at least 1)
"exchanges" -> { "name":"<name of exchange>", ... }
"apikey", "apisecret", "userid", "tradepassword" these are as expected
"pollgap" -> gap between each access to exchange for getting prices

48
iguana/SuperNET_keys.c

@ -43,30 +43,40 @@ bits256 SuperNET_wallet2priv(char *wallet2fname,bits256 wallethash)
char *SuperNET_parsemainargs(struct supernet_info *myinfo,bits256 *wallethashp,bits256 *wallet2privp,char *argjsonstr)
{
cJSON *json; uint8_t secretbuf[512]; char *wallet2fname,*coinargs=0,*secret;
bits256 wallethash,wallet2priv; int32_t len;
cJSON *exchanges,*json = 0; char *wallet2fname,*coinargs=0,*secret,*filestr;
long allocsize; bits256 wallethash,wallet2priv; int32_t n,len; uint8_t secretbuf[8192];
wallethash = wallet2priv = GENESIS_PRIVKEY;
if ( argjsonstr != 0 && (json= cJSON_Parse(argjsonstr)) != 0 )
if ( argjsonstr != 0 )
{
printf("ARGSTR.(%s)\n",argjsonstr);
if ( jobj(json,"numhelpers") != 0 )
IGUANA_NUMHELPERS = juint(json,"numhelpers");
if ( (secret= jstr(json,"wallet")) != 0 )
if ( (filestr= OS_filestr(&allocsize,argjsonstr)) != 0 )
{
len = (int32_t)strlen(secret);
if ( is_hexstr(secret,0) != 0 && len == 128 )
json = cJSON_Parse(filestr);
free(filestr);
}
if ( json != 0 || (json= cJSON_Parse(argjsonstr)) != 0 )
{
printf("ARGSTR.(%s)\n",argjsonstr);
if ( jobj(json,"numhelpers") != 0 )
IGUANA_NUMHELPERS = juint(json,"numhelpers");
if ( (secret= jstr(json,"wallet")) != 0 )
{
len >>= 1;
decode_hex(secretbuf,len,secret);
} else vcalc_sha256(0,secretbuf,(void *)secret,len), len = sizeof(bits256);
memcpy(wallethash.bytes,secretbuf,sizeof(wallethash));
//printf("wallethash.(%s)\n",bits256_str(str,wallethash));
if ( (wallet2fname= jstr(json,"2fafile")) != 0 )
wallet2priv = SuperNET_wallet2priv(wallet2fname,wallethash);
len = (int32_t)strlen(secret);
if ( is_hexstr(secret,0) != 0 && len == 128 )
{
len >>= 1;
decode_hex(secretbuf,len,secret);
} else vcalc_sha256(0,secretbuf,(void *)secret,len), len = sizeof(bits256);
memcpy(wallethash.bytes,secretbuf,sizeof(wallethash));
//printf("wallethash.(%s)\n",bits256_str(str,wallethash));
if ( (wallet2fname= jstr(json,"2fafile")) != 0 )
wallet2priv = SuperNET_wallet2priv(wallet2fname,wallethash);
}
if ( (exchanges= jarray(&n,json,"exchanges")) != 0 )
exchanges777_init(myinfo,exchanges,0);
if ( jobj(json,"coins") != 0 )
coinargs = argjsonstr;
free_json(json);
}
if ( jobj(json,"coins") != 0 )
coinargs = argjsonstr;
free_json(json);
}
*wallethashp = wallethash, *wallet2privp = wallet2priv;
return(coinargs);

22
iguana/exchanges777.h

@ -25,7 +25,7 @@
#define EXCHANGES777_DEFAULT_TIMEOUT 30
struct exchange_info;
struct exchange_quote { double price,volume; uint64_t orderid,offerNXT; uint32_t timestamp; };
struct exchange_quote { double price,volume; uint64_t orderid,offerNXT; uint32_t timestamp,val; };
struct exchange_funcs
{
@ -53,6 +53,24 @@ struct exchange_info
CURL *cHandle; queue_t requestQ,pricesQ,pendingQ[2],tradebotsQ;
};
struct instantdex_msghdr
{
struct acct777_sig sig __attribute__((packed));
char cmd[8];
uint8_t serialized[];
} __attribute__((packed));
struct exchange_request
{
struct queueitem DL;
cJSON *argjson; char **retstrp;
double price,volume,hbla,lastbid,lastask,commission;
uint64_t orderid; uint32_t timedout,expiration,dead,timestamp;
int32_t dir,depth,func,numbids,numasks;
char base[32],rel[32],destaddr[64],invert,allflag,dotrade;
struct exchange_quote bidasks[];
};
void *curl_post(void **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3);
char *InstantDEX_hexmsg(struct supernet_info *myinfo,void *data,int32_t len,char *remoteaddr);
char *instantdex_sendcmd(struct supernet_info *myinfo,cJSON *argjson,char *cmdstr,char *ipaddr,int32_t hops);
@ -61,6 +79,8 @@ struct exchange_info *exchanges777_info(char *exchangestr,int32_t sleepflag,cJSO
char *exchanges777_unmonitor(struct exchange_info *exchange,char *base,char *rel);
void tradebot_timeslice(struct exchange_info *exchange,void *bot);
char *exchanges777_Qtrade(struct exchange_info *exchange,char *base,char *rel,int32_t maxseconds,int32_t dotrade,int32_t dir,double price,double volume,cJSON *argjson);
struct exchange_request *exchanges777_baserelfind(struct exchange_info *exchange,char *base,char *rel,int32_t func);
void prices777_processprice(struct exchange_info *exchange,char *base,char *rel,struct exchange_quote *bidasks,int32_t maxdepth);
#endif

2
iguana/iguana777.h

@ -721,7 +721,7 @@ cJSON *iguana_voutjson(struct iguana_info *coin,struct iguana_msgvout *vout,int3
cJSON *iguana_vinjson(struct iguana_info *coin,struct iguana_msgvin *vin);
char *iguana_rawtxbytes(struct iguana_info *coin,cJSON *json,uint8_t *data,int32_t datalen);
int32_t iguana_send_VPNversion(struct iguana_info *coin,struct iguana_peer *addr,uint64_t myservices);
void exchanges777_init(int32_t sleepflag);
void exchanges777_init(struct supernet_info *myinfo,cJSON *exchanges,int32_t sleepflag);
extern queue_t bundlesQ;

138
iguana/iguana_exchanges.c

@ -19,17 +19,6 @@
#define EXCHANGE777_ISPENDING 2
#define EXCHANGE777_REQUEUE 3
struct exchange_request
{
struct queueitem DL;
cJSON *argjson; char **retstrp;
double price,volume,hbla,lastbid,lastask,commission;
uint64_t orderid; uint32_t timedout,expiration;
int32_t dir,depth,func,numbids,numasks;
char base[32],rel[32],destaddr[64],invert,allflag,dotrade;
struct exchange_quote bidasks[];
};
char *Exchange_names[] = { "bitfinex", "btc38", "bitstamp", "btce", "poloniex", "bittrex", "huobi", "coinbase", "okcoin", "lakebtc", "quadriga", "truefx", "ecb", "instaforex", "fxcm", "yahoo" };
struct exchange_info *Exchanges[sizeof(Exchange_names)/sizeof(*Exchange_names)];
@ -439,7 +428,7 @@ char *exchanges777_process(struct exchange_info *exchange,int32_t *retvalp,struc
*retvalp = EXCHANGE777_DONE;
switch ( req->func )
{
case 'Q':
case 'Q': case 'M':
memset(req->bidasks,0,req->depth * sizeof(*req->bidasks) * 2);
(*exchange->issue.price)(exchange,req->base,req->rel,req->bidasks,req->depth,req->commission,req->argjson);
retstr = exchanges777_orderbook_jsonstr(exchange,req->base,req->rel,req->bidasks,req->depth,req->invert,req->allflag);
@ -490,42 +479,51 @@ char *exchanges777_process(struct exchange_info *exchange,int32_t *retvalp,struc
void exchanges777_loop(void *ptr)
{
struct exchange_info *exchange = ptr;
int32_t flag,retval; struct exchange_request *req; char *retstr; void *bot;
int32_t flag,retval,i; struct exchange_request *req; char *retstr; void *bot;
while ( 1 )
{
flag = retval = 0;
retstr = 0;
if ( (req= queue_dequeue(&exchange->requestQ,0)) != 0 )
{
//printf("dequeued %s.%c\n",exchange->name,req->func);
retstr = exchanges777_process(exchange,&retval,req);
if ( retval == EXCHANGE777_DONE )
if ( req->dead == 0 )
{
if ( retstr != 0 )
//printf("dequeued %s.%c\n",exchange->name,req->func);
retstr = exchanges777_process(exchange,&retval,req);
if ( retval == EXCHANGE777_DONE )
{
if ( req->retstrp != 0 && req->timedout == 0 )
*req->retstrp = retstr;
else free(retstr);
if ( req->timedout != 0 )
printf("timedout.%u req finally finished at %u\n",req->timedout,(uint32_t)time(NULL));
if ( retstr != 0 )
{
if ( req->retstrp != 0 && req->timedout == 0 )
*req->retstrp = retstr;
else free(retstr);
if ( req->timedout != 0 )
printf("timedout.%u req finally finished at %u\n",req->timedout,(uint32_t)time(NULL));
}
free(req);
flag++;
}
free(req);
flag++;
}
else
{
if ( retstr != 0 )
free(retstr);
if ( retval == EXCHANGE777_ISPENDING )
queue_enqueue("Xpending",&exchange->pendingQ[0],&req->DL,0), flag++;
else if ( retval == EXCHANGE777_REQUEUE )
queue_enqueue("requeue",&exchange->requestQ,&req->DL,0);
else
{
printf("exchanges777_process: illegal retval.%d\n",retval);
free(req);
if ( retstr != 0 )
free(retstr);
if ( retval == EXCHANGE777_ISPENDING )
queue_enqueue("Xpending",&exchange->pendingQ[0],&req->DL,0), flag++;
else if ( retval == EXCHANGE777_REQUEUE )
queue_enqueue("requeue",&exchange->requestQ,&req->DL,0);
else
{
printf("exchanges777_process: illegal retval.%d\n",retval);
free(req);
}
}
}
else
{
if ( req->retstrp != 0 )
*req->retstrp = clonestr("{\"result\":\"request killed\"}");
free(req);
}
}
if ( (bot= queue_dequeue(&exchange->tradebotsQ,0)) != 0 )
tradebot_timeslice(exchange,bot);
@ -533,14 +531,29 @@ void exchanges777_loop(void *ptr)
{
if ( (req= queue_dequeue(&exchange->pricesQ,0)) != 0 )
{
if ( req->base[0] != 0 )
if ( req->dead == 0 )
{
if ( req->base[0] != 0 )
{
//printf("check %s pricesQ (%s %s)\n",exchange->name,req->base,req->rel);
req->timestamp = exchange->lastpoll = (uint32_t)time(NULL);
req->hbla = (*exchange->issue.price)(exchange,req->base,req->rel,req->bidasks,req->depth,req->commission,req->argjson);
for (i=req->numbids=0; i<req->depth; i++)
if ( req->bidasks[i << 1].price > SMALLVAL )
req->numbids++;
for (i=req->numasks=0; i<req->depth; i++)
if ( req->bidasks[(i << 1) + 1].price > SMALLVAL )
req->numasks++;
prices777_processprice(exchange,req->base,req->rel,req->bidasks,req->depth);
}
queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0);
}
else
{
//printf("check %s pricesQ (%s %s)\n",exchange->name,req->base,req->rel);
exchange->lastpoll = (uint32_t)time(NULL);
req->hbla = (*exchange->issue.price)(exchange,req->base,req->rel,req->bidasks,req->depth,req->commission,req->argjson);
prices777_processprice(exchange,req->base,req->rel,req->bidasks,req->depth);
if ( req->retstrp != 0 )
*req->retstrp = clonestr("{\"result\":\"request killed\"}");
free(req);
}
queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0);
}
}
if ( flag == 0 )
@ -548,19 +561,28 @@ void exchanges777_loop(void *ptr)
}
}
char *exchanges777_unmonitor(struct exchange_info *exchange,char *base,char *rel)
struct exchange_request *exchanges777_baserelfind(struct exchange_info *exchange,char *base,char *rel,int32_t func)
{
struct exchange_request PAD,*req; char *retstr = 0;
struct exchange_request PAD,*req,*retreq=0;
memset(&PAD,0,sizeof(PAD));
queue_enqueue("pricesQ",&exchange->pricesQ,&PAD.DL,0);
while ( (req= queue_dequeue(&exchange->pricesQ,0)) != 0 && req != &PAD )
{
if ( strcmp(base,req->base) == 0 || strcmp(rel,req->rel) == 0 )
{
printf("unmonitor.%s (%s %s)\n",exchange->name,base,rel);
free(req);
retstr = clonestr("{\"result\":\"unmonitored\"}");
} else queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0);
if ( ((strcmp(base,req->base) == 0 && strcmp(rel,req->rel) == 0) || (strcmp(rel,req->base) == 0 && strcmp(base,req->rel) == 0)) && (func < 0 || req->func == func) )
retreq = req;
else queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0);
}
return(retreq);
}
char *exchanges777_unmonitor(struct exchange_info *exchange,char *base,char *rel)
{
struct exchange_request *req; char *retstr = 0;
if ( (req= exchanges777_baserelfind(exchange,base,rel,'M')) != 0 )
{
printf("unmonitor.%s (%s %s)\n",exchange->name,base,rel);
req->dead = (uint32_t)time(NULL);
retstr = clonestr("{\"result\":\"mark priceQ entry as dead\"}");
}
if ( retstr == 0 )
retstr = clonestr("{\"error\":\"cant find base/rel pair to unmonitor\"}");
@ -619,7 +641,7 @@ char *exchanges777_Qprices(struct exchange_info *exchange,char *base,char *rel,i
return(exchanges777_submit(exchange,&retstr,req,'Q',maxseconds));
else
{
req->func = 'Q';
req->func = 'M';
queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0);
return(clonestr("{\"result\":\"start monitoring\"}"));
}
@ -665,6 +687,8 @@ struct exchange_info *exchange_create(char *exchangestr,cJSON *argjson)
huobi_funcs, lakebtc_funcs, quadriga_funcs, okcoin_funcs, coinbase_funcs, bitstamp_funcs
};
char *key,*secret,*userid,*tradepassword; struct exchange_info *exchange; int32_t i,exchangeid;
if ( exchangestr == 0 || exchangestr[0] == 0 )
return(0);
if ( (exchangeid= exchanges777_id(exchangestr)) < 0 )
{
printf("exchange_create: cant find.(%s)\n",exchangestr);
@ -725,9 +749,19 @@ struct exchange_info *exchanges777_info(char *exchangestr,int32_t sleepflag,cJSO
return(exchange);
}
void exchanges777_init(int32_t sleepflag)
void exchanges777_init(struct supernet_info *myinfo,cJSON *exchanges,int32_t sleepflag)
{
int32_t i; cJSON *argjson; bits256 instantdexhash;
int32_t i,n; cJSON *argjson,*item; bits256 instantdexhash; struct exchange_info *exchange;
if ( exchanges != 0 )
{
n = cJSON_GetArraySize(exchanges);
for (i=0; i<n; i++)
{
item = jitem(exchanges,i);
if ( (exchange= exchange_create(jstr(item,"name"),item)) != 0 )
myinfo->tradingexchanges[myinfo->numexchanges++] = exchange;
}
}
if ( 0 )
{
argjson = cJSON_CreateObject();

39
iguana/iguana_instantdex.c

@ -15,13 +15,6 @@
#include "exchanges777.h"
struct instantdex_msghdr
{
struct acct777_sig sig __attribute__((packed));
char cmd[8];
uint8_t serialized[];
} __attribute__((packed));
int32_t instantdex_rwdata(int32_t rwflag,uint64_t cmdbits,uint8_t *data,int32_t datalen)
{
// need to inplace serialize/deserialize here
@ -85,11 +78,22 @@ char *instantdex_sendcmd(struct supernet_info *myinfo,cJSON *argjson,char *cmdst
}
}
void instantdex_updatesources(int32_t dir,struct exchange_quote *quotes,int32_t numquotes)
{
int32_t i; struct exchange_quote *quote;
for (i=0; i<numquotes; i++)
{
quote = &quotes[i << 1];
}
}
char *instantdex_request(struct supernet_info *myinfo,struct instantdex_msghdr *msg,cJSON *argjson,char *remoteaddr,uint64_t signerbits,uint8_t *data,int32_t datalen)
{
char *base,*rel,*request; double volume; int32_t i; struct exchange_info *exchange;
char *base,*rel,*request,*str; double volume; int32_t i,num,depth = 30;
struct exchange_info *exchange; struct exchange_request *req,*active[64];
if ( argjson != 0 )
{
num = 0;
request = jstr(argjson,"request");
base = jstr(argjson,"base");
rel = jstr(argjson,"rel");
@ -100,9 +104,26 @@ char *instantdex_request(struct supernet_info *myinfo,struct instantdex_msghdr *
{
if ( (exchange= myinfo->tradingexchanges[i]) != 0 )
{
if ( (req= exchanges777_baserelfind(exchange,base,rel,'M')) == 0 )
{
if ( (str= exchanges777_Qprices(exchange,base,rel,30,1,depth,argjson,1,exchange->commission)) != 0 )
free(str);
req = exchanges777_baserelfind(exchange,base,rel,'M');
}
if ( req == 0 )
{
if ( (*exchange->issue.supports)(exchange,base,rel,argjson) != 0 )
printf("unexpected null req.(%s %s) %s\n",base,rel,exchange->name);
} else active[num++] = req;
}
}
for (i=0; i<num; i++)
{
if ( volume < 0. && active[i]->numbids > 0 )
instantdex_updatesources(1,active[i]->bidasks,active[i]->numbids);
else if ( volume > 0. && active[i]->numasks > 0 )
instantdex_updatesources(-1,&active[i]->bidasks[1],active[i]->numasks);
}
return(clonestr("{\"result\":\"reqprice response sent\"}"));
}
return(clonestr("{\"error\":\"request missing parameter\"}"));

58
iguana/iguana_tradebots.c

@ -86,7 +86,7 @@ cJSON *tradebot_json(struct supernet_info *myinfo,struct exchange_info *exchange
return(json);
}
struct tradebot_info *tradebot_find(struct supernet_info *myinfo,struct exchange_info *exchange,char *botname,cJSON *array)
struct tradebot_info *tradebot_find(struct supernet_info *myinfo,struct exchange_info *exchange,char *botname,cJSON *array,char *base,char *rel)
{
struct tradebot_info PAD,*bot,*retbot = 0;
memset(&PAD,0,sizeof(PAD));
@ -97,6 +97,8 @@ struct tradebot_info *tradebot_find(struct supernet_info *myinfo,struct exchange
retbot = bot;
if ( array != 0 )
jaddi(array,tradebot_json(myinfo,exchange,bot));
if ( base != 0 && rel != 0 && strcmp(base,bot->base) == 0 && strcmp(rel,bot->rel) == 0 )
retbot = bot;
queue_enqueue("tradebotsQ",&exchange->tradebotsQ,&bot->DL,0);
}
return(retbot);
@ -158,30 +160,6 @@ void tradebot_timeslice(struct exchange_info *exchange,void *_bot)
} else free(bot);
}
#include "../includes/iguana_apidefs.h"
THREE_STRINGS_AND_DOUBLE(tradebot,monitor,exchange,base,rel,commission)
{
int32_t allfields = 1,depth = 50; struct exchange_info *ptr;
if ( remoteaddr == 0 )
{
if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 )
return(exchanges777_Qprices(ptr,base,rel,30,allfields,depth,json,1,commission * .01));
else return(clonestr("{\"error\":\"couldnt find/create exchange info\"}"));
} else return(clonestr("{\"error\":\"tradebots only local usage!\"}"));
}
THREE_STRINGS(tradebot,unmonitor,exchange,base,rel)
{
struct exchange_info *ptr;
if ( remoteaddr == 0 )
{
if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 )
return(exchanges777_unmonitor(ptr,base,rel));
else return(clonestr("{\"error\":\"couldnt find/create exchange info\"}"));
} else return(clonestr("{\"error\":\"tradebots only local usage!\"}"));
}
char *tradebot_launch(struct supernet_info *myinfo,char *exchangestr,char *base,char *rel,int32_t dir,double price,double volume,int32_t duration,char *remoteaddr,cJSON *json)
{
struct exchange_info *exchange; char retbuf[1024]; struct tradebot_info *bot;
@ -206,7 +184,7 @@ char *tradebot_control(struct supernet_info *myinfo,char *exchangestr,char *boti
{
if ( (exchange= exchanges777_info(exchangestr,1,json,remoteaddr)) != 0 )
{
if ( (bot= tradebot_find(myinfo,exchange,botid,0)) != 0 )
if ( (bot= tradebot_find(myinfo,exchange,botid,0,0,0)) != 0 )
{
if ( control > 1 )
bot->pause = (uint32_t)time(NULL);
@ -220,6 +198,30 @@ char *tradebot_control(struct supernet_info *myinfo,char *exchangestr,char *boti
} else return(clonestr("{\"error\":\"tradebots only local usage!\"}"));
}
#include "../includes/iguana_apidefs.h"
THREE_STRINGS_AND_DOUBLE(tradebot,monitor,exchange,base,rel,commission)
{
int32_t allfields = 1,depth = 50; struct exchange_info *ptr;
if ( remoteaddr == 0 )
{
if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 )
return(exchanges777_Qprices(ptr,base,rel,30,allfields,depth,json,1,commission * .01));
else return(clonestr("{\"error\":\"couldnt find/create exchange info\"}"));
} else return(clonestr("{\"error\":\"tradebots only local usage!\"}"));
}
THREE_STRINGS(tradebot,unmonitor,exchange,base,rel)
{
struct exchange_info *ptr;
if ( remoteaddr == 0 )
{
if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 )
return(exchanges777_unmonitor(ptr,base,rel));
else return(clonestr("{\"error\":\"couldnt find/create exchange info\"}"));
} else return(clonestr("{\"error\":\"tradebots only local usage!\"}"));
}
THREE_STRINGS_AND_THREE_DOUBLES(tradebot,accumulate,exchange,base,rel,price,volume,duration)
{
return(tradebot_launch(myinfo,exchange,base,rel,1,price,volume,duration,remoteaddr,json));
@ -237,7 +239,7 @@ TWO_STRINGS(tradebot,status,exchange,botid)
{
if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 )
{
if ( (bot= tradebot_find(myinfo,ptr,botid,0)) != 0 )
if ( (bot= tradebot_find(myinfo,ptr,botid,0,0,0)) != 0 )
return(jprint(tradebot_json(myinfo,ptr,bot),1));
}
}
@ -251,7 +253,7 @@ STRING_ARG(tradebot,activebots,exchange)
{
if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 )
{
tradebot_find(myinfo,ptr,0,array);
tradebot_find(myinfo,ptr,0,array,0,0);
retjson = cJSON_CreateObject();
jaddstr(retjson,"result","success");
jadd(retjson,"tradebots",array);

Loading…
Cancel
Save