/****************************************************************************** * Copyright © 2014-2015 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * SuperNET software, including this file may be copied, modified, propagated * * or distributed except according to the terms contained in the LICENSE file * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ #ifndef xcode_quotes_h #define xcode_quotes_h #ifdef oldway int32_t make_jumpiQ(uint64_t refbaseid,uint64_t refrelid,int32_t flip,struct InstantDEX_quote *iQ,struct InstantDEX_quote *baseiQ,struct InstantDEX_quote *reliQ,char *gui,int32_t duration) { uint64_t baseamount,relamount,frombase,fromrel,tobase,torel; double vol; char exchange[64]; uint32_t timestamp; frombase = baseiQ->baseamount, fromrel = baseiQ->relamount; tobase = reliQ->baseamount, torel = reliQ->relamount; if ( make_jumpquote(refbaseid,refrelid,&baseamount,&relamount,&frombase,&fromrel,&tobase,&torel) == 0. ) return(0); if ( (timestamp= reliQ->timestamp) > baseiQ->timestamp ) timestamp = baseiQ->timestamp; iQ_exchangestr(exchange,iQ); create_InstantDEX_quote(iQ,timestamp,0,calc_quoteid(baseiQ) ^ calc_quoteid(reliQ),0.,0.,refbaseid,baseamount,refrelid,relamount,exchange,0,gui,baseiQ,reliQ,duration); if ( Debuglevel > 2 ) printf("jump%s: %f (%llu/%llu) %llu %llu (%f %f) %llu %llu\n",flip==0?"BID":"ASK",calc_price_volume(&vol,iQ->baseamount,iQ->relamount),(long long)baseamount,(long long)relamount,(long long)frombase,(long long)fromrel,calc_price_volume(&vol,frombase,fromrel),calc_price_volume(&vol,tobase,torel),(long long)tobase,(long long)torel); iQ->isask = flip; iQ->minperc = baseiQ->minperc; if ( reliQ->minperc > iQ->minperc ) iQ->minperc = reliQ->minperc; return(1); } #else struct InstantDEX_quote *AllQuotes; void clear_InstantDEX_quoteflags(struct InstantDEX_quote *iQ) { //duration:14,wallet:1,a:1,isask:1,expired:1,closed:1,swap:1,responded:1,matched:1,feepaid:1,automatch:1,pending:1,minperc:7; iQ->s.a = iQ->s.expired = iQ->s.swap = iQ->s.feepaid = 0; iQ->s.closed = iQ->s.pending = iQ->s.responded = iQ->s.matched = 0; } void cancel_InstantDEX_quote(struct InstantDEX_quote *iQ) { iQ->s.closed = 1; } int32_t InstantDEX_uncalcsize() { struct InstantDEX_quote iQ; return(sizeof(iQ.hh) + sizeof(iQ.s.quoteid) + sizeof(iQ.s.price) + sizeof(iQ.s.vol)); } int32_t iQcmp(struct InstantDEX_quote *iQA,struct InstantDEX_quote *iQB) { if ( iQA->s.isask == iQB->s.isask && iQA->s.baseid == iQB->s.baseid && iQA->s.relid == iQB->s.relid && iQA->s.baseamount == iQB->s.baseamount && iQA->s.relamount == iQB->s.relamount ) return(0); else if ( iQA->s.isask != iQB->s.isask && iQA->s.baseid == iQB->s.relid && iQA->s.relid == iQB->s.baseid && iQA->s.baseamount == iQB->s.relamount && iQA->s.relamount == iQB->s.baseamount ) return(0); return(-1); } uint64_t calc_txid(unsigned char *buf,int32_t len) { bits256 hash; vcalc_sha256(0,hash.bytes,buf,len); return(hash.txid); } uint64_t calc_quoteid(struct InstantDEX_quote *iQ) { struct InstantDEX_quote Q; if ( iQ == 0 ) return(0); if ( iQ->s.duration == 0 || iQ->s.duration > ORDERBOOK_EXPIRATION ) iQ->s.duration = ORDERBOOK_EXPIRATION; if ( iQ->s.quoteid == 0 ) { Q = *iQ; clear_InstantDEX_quoteflags(&Q); if ( Q.s.isask != 0 ) { Q.s.baseid = iQ->s.relid, Q.s.baseamount = iQ->s.relamount; Q.s.relid = iQ->s.baseid, Q.s.relamount = iQ->s.baseamount; Q.s.isask = Q.s.minperc = 0; } return(calc_txid((uint8_t *)((long)&Q + InstantDEX_uncalcsize()),sizeof(Q) - InstantDEX_uncalcsize())); } return(iQ->s.quoteid); } struct InstantDEX_quote *find_iQ(uint64_t quoteid) { struct InstantDEX_quote *iQ; HASH_FIND(hh,AllQuotes,"eid,sizeof(quoteid),iQ); return(iQ); } struct InstantDEX_quote *delete_iQ(uint64_t quoteid) { struct InstantDEX_quote *iQ; if ( (iQ= find_iQ(quoteid)) != 0 ) { HASH_DELETE(hh,AllQuotes,iQ); } return(iQ); } struct InstantDEX_quote *findquoteid(uint64_t quoteid,int32_t evenclosed) { struct InstantDEX_quote *iQ; if ( (iQ= find_iQ(quoteid)) != 0 ) { if ( evenclosed != 0 || iQ->s.closed == 0 ) { if ( calc_quoteid(iQ) == quoteid ) return(iQ); else printf("calc_quoteid %llu vs %llu\n",(long long)calc_quoteid(iQ),(long long)quoteid); } //else printf("quoteid.%llu closed.%d\n",(long long)quoteid,iQ->closed); } else printf("couldnt find %llu\n",(long long)quoteid); return(0); } int32_t cancelquote(char *NXTaddr,uint64_t quoteid) { struct InstantDEX_quote *iQ; if ( (iQ= findquoteid(quoteid,0)) != 0 && iQ->s.offerNXT == calc_nxt64bits(NXTaddr) && iQ->exchangeid == INSTANTDEX_EXCHANGEID ) { cancel_InstantDEX_quote(iQ); return(1); } return(0); } struct InstantDEX_quote *create_iQ(struct InstantDEX_quote *iQ,char *walletstr) { struct InstantDEX_quote *newiQ,*tmp; struct prices777 *prices; int32_t inverted; long len = 0; if ( walletstr != 0 && (len= strlen(walletstr)) > 0 ) iQ->s.wallet = 1, len++; calc_quoteid(iQ); printf("createiQ %llu/%llu %f %f quoteid.%llu offerNXT.%llu wallet.%d (%s)\n",(long long)iQ->s.baseid,(long long)iQ->s.relid,iQ->s.price,iQ->s.vol,(long long)iQ->s.quoteid,(long long)iQ->s.offerNXT,iQ->s.wallet,walletstr!=0?walletstr:""); if ( (newiQ= find_iQ(iQ->s.quoteid)) != 0 ) return(newiQ); newiQ = calloc(1,sizeof(*newiQ) + len); *newiQ = *iQ; if ( len != 0 ) memcpy(newiQ->walletstr,walletstr,len); HASH_ADD(hh,AllQuotes,s.quoteid,sizeof(newiQ->s.quoteid),newiQ); if ( (prices= prices777_find(&inverted,iQ->s.baseid,iQ->s.relid,INSTANTDEX_NAME)) != 0 ) prices->dirty++; { struct InstantDEX_quote *checkiQ; if ( (checkiQ= find_iQ(iQ->s.quoteid)) == 0 || iQcmp(iQ,checkiQ) != 0 )//memcmp((uint8_t *)((long)checkiQ + sizeof(checkiQ->hh) + sizeof(checkiQ->quoteid)),(uint8_t *)((long)iQ + sizeof(iQ->hh) + sizeof(iQ->quoteid)),sizeof(*iQ) - sizeof(iQ->hh) - sizeof(iQ->quoteid)) != 0 ) { int32_t i; for (i=(sizeof(iQ->hh) - sizeof(iQ->s.quoteid)); ihh) - sizeof(iQ->s.quoteid); i++) printf("%02x ",((uint8_t *)iQ)[i]); printf("iQ\n"); for (i=(sizeof(checkiQ->hh) + sizeof(checkiQ->s.quoteid)); ihh) - sizeof(checkiQ->s.quoteid); i++) printf("%02x ",((uint8_t *)checkiQ)[i]); printf("checkiQ\n"); printf("error finding iQ after adding %llu vs %llu\n",(long long)checkiQ->s.quoteid,(long long)iQ->s.quoteid); } } HASH_ITER(hh,AllQuotes,iQ,tmp) { if ( iQ->s.expired != 0 ) { printf("quoteid.%llu expired, purging\n",(long long)iQ->s.expired); delete_iQ(iQ->s.quoteid); } } return(newiQ); } #ifdef later cJSON *pangea_walletitem(cJSON *walletitem,struct coin777 *coin,int32_t rakemillis,int64_t bigblind,int64_t ante,int32_t minbuyin,int32_t maxbuyin) { char *addr; struct destbuf pubkey; if ( walletitem == 0 ) walletitem = cJSON_CreateObject(); //printf("call get_acct_coinaddr.%s (%s) (%s)\n",coin->name,coin->serverport,coin->userpass); if ( coin->pangeapubkey[0] == 0 || coin->pangeacoinaddr[0] == 0 ) { if ( strcmp("NXT",coin->name) == 0 ) { } else if ( (addr= get_acct_coinaddr(coin->pangeacoinaddr,coin->name,coin->serverport,coin->userpass,"pangea")) != 0 ) { //printf("get_pubkey\n"); get_pubkey(&pubkey,coin->name,coin->serverport,coin->userpass,coin->pangeacoinaddr); strcpy(coin->pangeapubkey,pubkey.buf); } } jaddstr(walletitem,"pubkey",coin->pangeapubkey); jaddstr(walletitem,"coinaddr",coin->pangeacoinaddr); jaddnum(walletitem,"rakemillis",rakemillis); jaddnum(walletitem,"minbuyin",minbuyin); jaddnum(walletitem,"maxbuyin",maxbuyin); jadd64bits(walletitem,"bigblind",bigblind); jadd64bits(walletitem,"ante",ante); return(walletitem); } cJSON *set_walletstr(cJSON *walletitem,char *walletstr,struct InstantDEX_quote *iQ) { char pubkeystr[128],pkhash[128],base[64],rel[64],fieldA[64],fieldB[64],fieldpkhash[64],*pubA,*pubB,*pkhashstr,*str,*exchangestr; struct coin777 *coin; int32_t flip = 0; if ( walletstr != 0 && walletitem == 0 ) walletitem = cJSON_Parse(walletstr); if ( walletitem == 0 ) walletitem = cJSON_CreateObject(); unstringbits(base,iQ->s.basebits), unstringbits(rel,iQ->s.relbits); flip = (iQ->s.offerNXT != IGUANA_MY64BITS); if ( strcmp(base,"NXT") != 0 ) coin = coin777_find(base,1); else if ( strcmp(rel,"NXT") != 0 ) coin = coin777_find(rel,1), flip ^= 1; else coin = 0; if ( coin != 0 ) { if ( (exchangestr= exchange_str(iQ->exchangeid)) != 0 && strcmp(exchangestr,"pangea") == 0 ) pangea_walletitem(walletitem,coin,iQ->s.minperc,iQ->s.baseamount,iQ->s.relamount,iQ->s.minbuyin,iQ->s.maxbuyin); else { //printf("START.(%s)\n",jprint(walletitem,0)); if ( (iQ->s.isask ^ flip) == 0 ) { sprintf(fieldA,"%spubA",coin->name); if ( (pubA= jstr(walletitem,fieldA)) != 0 ) cJSON_DeleteItemFromObject(walletitem,fieldA); jaddstr(walletitem,fieldA,coin->atomicsendpubkey); //printf("replaceA\n"); } else { sprintf(fieldB,"%spubB",coin->name); if ( (pubB= jstr(walletitem,fieldB)) != 0 ) cJSON_DeleteItemFromObject(walletitem,fieldB); jaddstr(walletitem,fieldB,coin->atomicrecvpubkey); sprintf(fieldpkhash,"%spkhash",coin->name); if ( (pkhashstr= jstr(walletitem,fieldpkhash)) != 0 ) cJSON_DeleteItemFromObject(walletitem,fieldpkhash); subatomic_pubkeyhash(pubkeystr,pkhash,coin,iQ->s.quoteid); jaddstr(walletitem,fieldpkhash,pkhash); //printf("replaceB\n"); } } str = jprint(walletitem,0); strcpy(walletstr,str); free(str); return(walletitem); } return(0); } #endif char *InstantDEX_str(char *walletstr,char *buf,int32_t extraflag,struct InstantDEX_quote *iQ) { cJSON *json; char _buf[4096],base[64],rel[64],*str; unstringbits(base,iQ->s.basebits), unstringbits(rel,iQ->s.relbits); if ( buf == 0 ) buf = _buf; sprintf(buf,"{\"quoteid\":\"%llu\",\"base\":\"%s\",\"baseid\":\"%llu\",\"baseamount\":\"%llu\",\"rel\":\"%s\",\"relid\":\"%llu\",\"relamount\":\"%llu\",\"price\":%.8f,\"volume\":%.8f,\"offerNXT\":\"%llu\",\"timestamp\":\"%u\",\"isask\":\"%u\",\"exchange\":\"%s\",\"gui\":\"%s\"}",(long long)iQ->s.quoteid,base,(long long)iQ->s.baseid,(long long)iQ->s.baseamount,rel,(long long)iQ->s.relid,(long long)iQ->s.relamount,iQ->s.price,iQ->s.vol,(long long)iQ->s.offerNXT,iQ->s.timestamp,iQ->s.isask,exchange_str(iQ->exchangeid),iQ->gui); if ( extraflag != 0 ) { sprintf(buf + strlen(buf) - 1,",\"plugin\":\"relay\",\"destplugin\":\"InstantDEX\",\"method\":\"busdata\",\"submethod\":\"%s\"}",(iQ->s.isask != 0) ? "ask" : "bid"); } //printf("InstantDEX_str.(%s)\n",buf); if ( (json= cJSON_Parse(buf)) != 0 ) { #ifdef later char _buf[4096],_walletstr[256],base[64],rel[64],*exchange,*str; cJSON *walletitem,*json; struct coin777 *coin; if ( walletstr == 0 ) { walletstr = _walletstr; walletstr[0] = 0; } if ( (exchange= exchange_str(iQ->exchangeid)) != 0 ) { coin = coin777_find(base,0); if ( strcmp(exchange,"wallet") == 0 ) walletitem = set_walletstr(0,walletstr,iQ); else if ( strcmp(exchange,"pangea") == 0 && walletstr[0] == 0 && coin != 0 ) walletitem = pangea_walletitem(0,coin,iQ->s.minperc,iQ->s.baseamount,iQ->s.relamount,iQ->s.minbuyin,iQ->s.maxbuyin); else walletitem = 0; if ( walletitem != 0 ) { jadd(json,"wallet",walletitem); strcpy(walletstr,jprint(walletitem,0)); } //printf("exchange.(%s) iswallet.%d (%s) base.(%s) coin.%p (%s)\n",exchange,iQ->s.wallet,walletstr,base,coin,jprint(json,0)); } else printf("InstantDEX_str cant find exchangeid.%d\n",iQ->exchangeid); #endif str = jprint(json,1); strcpy(buf,str); //printf("str.(%s) %p\n",buf,buf); free(str); } else printf("InstantDEX_str cant parse.(%s)\n",buf); if ( buf == _buf ) return(clonestr(buf)); else return(buf); } uint64_t _get_AEquote(char *str,uint64_t orderid) { cJSON *json; uint64_t nxt64bits = 0; char cmd[256],*jsonstr; sprintf(cmd,"requestType=get%sOrder&order=%llu",str,(long long)orderid); if ( (jsonstr= issue_NXTPOST(cmd)) != 0 ) { //printf("(%s) -> (%s)\n",cmd,jsonstr); if ( (json= cJSON_Parse(jsonstr)) != 0 ) { nxt64bits = get_API_nxt64bits(cJSON_GetObjectItem(json,"account")); free_json(json); } free(jsonstr); } return(nxt64bits); } char *cancel_NXTorderid(char *NXTaddr,char *nxtsecret,uint64_t orderid) { uint64_t nxt64bits; char cmd[1025],secret[8192],*str = "Bid",*retstr = 0; if ( (nxt64bits= _get_AEquote(str,orderid)) == 0 ) str = "Ask", nxt64bits = _get_AEquote(str,orderid); if ( nxt64bits == calc_nxt64bits(NXTaddr) ) { escape_code(secret,nxtsecret); sprintf(cmd,"requestType=cancel%sOrder&secretPhrase=%s&feeNQT=%lld&deadline=%d&order=%llu",str,secret,(long long)MIN_NQTFEE,DEFAULT_NXT_DEADLINE,(long long)orderid); retstr = issue_NXTPOST(cmd); //printf("(%s) -> (%s)\n",cmd,retstr); } return(retstr); } char *InstantDEX_cancelorder(cJSON *argjson,char *activenxt,char *secret,uint64_t orderid,uint64_t quoteid) { struct InstantDEX_quote *iQ; cJSON *json,*array,*item; char numstr[64],*retstr,*exchangestr; uint64_t quoteids[256]; int32_t i,exchangeid,n=0; struct exchange_info *exchange; if ( (exchangestr= jstr(argjson,"exchange")) != 0 && (exchange= find_exchange(&exchangeid,exchangestr)) != 0 ) { if ( exchange->issue.cancelorder != 0 ) { if ( (retstr= (*exchange->issue.cancelorder)(&exchange->cHandle,exchange,argjson,quoteid)) == 0 ) retstr = clonestr("{\"result\":\"nothing returned from exchange\"}"); return(retstr); } else return(clonestr("{\"error\":\"no cancelorder function\"}")); } memset(quoteids,0,sizeof(quoteids)); json = cJSON_CreateObject(), array = cJSON_CreateArray(); if ( quoteid != 0 ) quoteids[n++] = quoteid; //n += InstantDEX_quoteids(quoteids+n,orderid); for (i=0; is.offerNXT == calc_nxt64bits(activenxt) ) cancel_InstantDEX_quote(iQ); if ( (item= cJSON_Parse(retstr)) != 0 ) jaddi(array,item); free(retstr); } cancelquote(activenxt,quoteid); } if ( orderid != 0 ) { if ( cancelquote(activenxt,orderid) != 0 ) sprintf(numstr,"%llu",(long long)orderid), jaddstr(json,"ordercanceled",numstr); } return(jprint(json,1)); } char *InstantDEX_orderstatus(cJSON *argjson,uint64_t orderid,uint64_t quoteid) { struct InstantDEX_quote *iQ = 0; char *exchangestr,*str; struct exchange_info *exchange; int32_t exchangeid; if ( (exchangestr= jstr(argjson,"exchange")) != 0 && (exchange= find_exchange(&exchangeid,exchangestr)) != 0 ) { if ( exchange->issue.orderstatus != 0 ) { if ( (str= (*exchange->issue.orderstatus)(&exchange->cHandle,exchange,argjson,quoteid)) == 0 ) str = clonestr("{\"result\":\"nothing returned from exchange\"}"); return(str); } else return(clonestr("{\"error\":\"no orderstatus function\"}")); } if ( (iQ= find_iQ(orderid)) != 0 || (iQ= find_iQ(quoteid)) != 0 ) return(InstantDEX_str(0,0,0,iQ)); return(clonestr("{\"error\":\"couldnt find orderid\"}")); } char *InstantDEX_openorders(cJSON *argjson,char *NXTaddr,int32_t allorders) { struct InstantDEX_quote *iQ,*tmp; char buf[4096],*exchangestr,*jsonstr,*str; uint32_t now,duration; cJSON *json,*array,*item; uint64_t nxt64bits; struct exchange_info *exchange; int32_t exchangeid; if ( (exchangestr= jstr(argjson,"exchange")) != 0 && (exchange= find_exchange(&exchangeid,exchangestr)) != 0 ) { if ( exchange->issue.openorders != 0 ) { if ( (str= (*exchange->issue.openorders)(&exchange->cHandle,exchange,argjson)) == 0 ) str = clonestr("{\"result\":\"nothing returned from exchange\"}"); return(str); } else return(clonestr("{\"error\":\"no orderstatus function\"}")); } nxt64bits = calc_nxt64bits(NXTaddr); now = (uint32_t)time(NULL); json = cJSON_CreateObject(), array = cJSON_CreateArray(); HASH_ITER(hh,AllQuotes,iQ,tmp) { if ( (duration= iQ->s.duration) == 0 ) duration = ORDERBOOK_EXPIRATION; if ( iQ->s.timestamp > (now + duration) ) iQ->s.expired = iQ->s.closed = 1; if ( iQ->s.offerNXT == nxt64bits && (allorders != 0 || iQ->s.closed == 0) ) { if ( (jsonstr= InstantDEX_str(0,buf,0,iQ)) != 0 && (item= cJSON_Parse(jsonstr)) != 0 ) jaddi(array,item); } } jadd(json,"openorders",array); return(jprint(json,1)); } cJSON *InstantDEX_specialorders(uint64_t *quoteidp,uint64_t nxt64bits,char *base,char *special,uint64_t baseamount,int32_t addrtype) { struct InstantDEX_quote *iQ,*tmp; int32_t exchangeid; uint32_t i,n,now,duration,ismine = 0; uint64_t basebits; cJSON *item=0,*array = 0; char *coinaddr=0,*pubkey,checkaddr[128]; now = (uint32_t)time(NULL); basebits = stringbits(base); if ( special == 0 || find_exchange(&exchangeid,special) == 0 ) exchangeid = 0; n = 0; *quoteidp = 0; HASH_ITER(hh,AllQuotes,iQ,tmp) { //printf("iter Q.%llu b.%llu\n",(long long)iQ->s.quoteid,(long long)iQ->s.basebits); if ( (duration= iQ->s.duration) == 0 ) duration = ORDERBOOK_EXPIRATION; if ( iQ->s.timestamp > (now + duration) ) { iQ->s.expired = iQ->s.closed = 1; printf("expire order %llu\n",(long long)iQ->s.quoteid); continue; } if ( iQ->s.basebits == basebits && (exchangeid == 0 || iQ->exchangeid == exchangeid) ) { //printf("matched basebits\n"); if ( strcmp(special,"pangea") == 0 ) { checkaddr[0] = 0; if ( iQ->s.wallet != 0 && (item= cJSON_Parse(iQ->walletstr)) != 0 && (coinaddr= jstr(item,"coinaddr")) != 0 && coinaddr[0] != 0 && (pubkey= jstr(item,"pubkey")) != 0 && pubkey[0] != 0 ) btc_coinaddr(coinaddr,addrtype,pubkey); if ( item != 0 ) free_json(item); if ( coinaddr == 0 || strcmp(coinaddr,checkaddr) != 0 ) { printf("mismatched pangea coinaddr (%s) vs (%s) or baseamount %.8f vs %.8f\n",coinaddr,checkaddr,dstr(baseamount),dstr(iQ->s.baseamount)); continue; } } if ( n > 0 ) { for (i=0; is.offerNXT == j64bits(jitem(array,i),0) ) break; } //printf("found duplicate\n"); } else i = 0; if ( i == n ) { if ( iQ->s.offerNXT == nxt64bits ) { ismine = 1; if ( *quoteidp == 0 ) *quoteidp = iQ->s.quoteid; } if ( array == 0 ) array = cJSON_CreateArray(); jaddi64bits(array,iQ->s.offerNXT); //printf("add %llu\n",(long long)iQ->s.offerNXT); } } //else printf("quote.%llu basebits.%llu\n",(long long)iQ->s.quoteid,(long long)iQ->s.basebits); } if ( ismine == 0 ) free_json(array), array = 0; //printf("ismine.%d n.%d array.%d\n",ismine,n,array==0?0:cJSON_GetArraySize(array)); return(array); } int _decreasing_quotes(const void *a,const void *b) { #define order_a ((struct InstantDEX_quote *)a) #define order_b ((struct InstantDEX_quote *)b) if ( order_b->s.price > order_a->s.price ) return(1); else if ( order_b->s.price < order_a->s.price ) return(-1); return(0); #undef order_a #undef order_b } int _increasing_quotes(const void *a,const void *b) { #define order_a ((struct InstantDEX_quote *)a) #define order_b ((struct InstantDEX_quote *)b) if ( order_b->s.price > order_a->s.price ) return(-1); else if ( order_b->s.price < order_a->s.price ) return(1); return(0); #undef order_a #undef order_b } cJSON *prices777_orderjson(struct InstantDEX_quote *iQ) { cJSON *item = cJSON_CreateArray(); jaddinum(item,iQ->s.price); jaddinum(item,iQ->s.vol); jaddi64bits(item,iQ->s.quoteid); return(item); } cJSON *InstantDEX_orderbook(struct prices777 *prices) { struct InstantDEX_quote *ptr,iQ,*tmp,*askvals=0,*bidvals=0; cJSON *json,*bids,*asks; uint32_t now,duration; int32_t i,isask,iter,n,m,numbids,numasks,invert; json = cJSON_CreateObject(), bids = cJSON_CreateArray(), asks = cJSON_CreateArray(); now = (uint32_t)time(NULL); for (iter=numbids=numasks=n=m=0; iter<2; iter++) { HASH_ITER(hh,AllQuotes,ptr,tmp) { iQ = *ptr; if ( (duration= iQ.s.duration) == 0 ) duration = ORDERBOOK_EXPIRATION; if ( iQ.s.timestamp > (now + duration) ) { iQ.s.expired = iQ.s.closed = 1; continue; } if ( Debuglevel > 2 ) printf("iterate quote.%llu\n",(long long)iQ.s.quoteid); if ( prices777_equiv(ptr->s.baseid) == prices777_equiv(prices->baseid) && prices777_equiv(ptr->s.relid) == prices777_equiv(prices->relid) ) invert = 0; else if ( prices777_equiv(ptr->s.relid) == prices777_equiv(prices->baseid) && prices777_equiv(ptr->s.baseid) == prices777_equiv(prices->relid) ) invert = 1; else continue; if ( ptr->s.pending != 0 ) continue; isask = iQ.s.isask; if ( invert != 0 ) isask ^= 1; if ( invert != 0 ) { if ( iQ.s.price > SMALLVAL ) iQ.s.vol *= iQ.s.price, iQ.s.price = 1. / iQ.s.price; else iQ.s.price = prices777_price_volume(&iQ.s.vol,iQ.s.relamount,iQ.s.baseamount); } else if ( iQ.s.price <= SMALLVAL ) iQ.s.price = prices777_price_volume(&iQ.s.vol,iQ.s.baseamount,iQ.s.relamount); if ( iter == 0 ) { if ( isask != 0 ) numasks++; else numbids++; } else { if ( isask == 0 && n < numbids ) bidvals[n++] = iQ; else if ( isask != 0 && m < numasks ) askvals[m++] = iQ; } } if ( iter == 0 ) { if ( numbids > 0 ) bidvals = calloc(numbids,sizeof(*bidvals)); if ( numasks > 0 ) askvals = calloc(numasks,sizeof(*askvals)); } } if ( numbids > 0 ) { if ( n > 0 ) { qsort(bidvals,n,sizeof(*bidvals),_decreasing_quotes); for (i=0; i 0 ) { if ( m > 0 ) { qsort(askvals,m,sizeof(*askvals),_increasing_quotes); for (i=0; i (refvol * INSTANTDEX_MINVOLPERC) )//&& refvol > (vol * iQ->s.minperc * .01) ) { if ( vol < refvol ) metric = (vol / refvol); else metric = 1.; if ( dir > 0 && price < (refprice * (1. + INSTANTDEX_PRICESLIPPAGE) + SMALLVAL) ) metric *= (1. + (refprice - price)/refprice); else if ( dir < 0 && price > (refprice * (1. - INSTANTDEX_PRICESLIPPAGE) - SMALLVAL) ) metric *= (1. + (price - refprice)/refprice); else metric = 0.; if ( metric != 0. ) { printf("price %.8f vol %.8f | %.8f > %.8f? %.8f > %.8f?\n",price,vol,vol,(refvol * INSTANTDEX_MINVOLPERC),refvol,(vol * INSTANTDEX_MINVOLPERC)); printf("price %f against %f or %f\n",price,(refprice * (1. + INSTANTDEX_PRICESLIPPAGE) + SMALLVAL),(refprice * (1. - INSTANTDEX_PRICESLIPPAGE) - SMALLVAL)); printf("metric %f\n",metric); } } return(metric); } char *autofill(char *remoteaddr,struct InstantDEX_quote *refiQ,char *NXTaddr,char *NXTACCTSECRET) { double price,volume,revprice,revvol,metric,bestmetric = 0.; int32_t dir,inverted; uint64_t nxt64bits; char *retstr=0; struct InstantDEX_quote *iQ,*tmp,*bestiQ; struct prices777 *prices; uint32_t duration,now = (uint32_t)time(NULL); return(0); nxt64bits = calc_nxt64bits(NXTaddr); memset(&bestiQ,0,sizeof(bestiQ)); dir = (refiQ->s.isask != 0) ? -1 : 1; HASH_ITER(hh,AllQuotes,iQ,tmp) { if ( (duration= refiQ->s.duration) == 0 ) duration = ORDERBOOK_EXPIRATION; if ( iQ->s.timestamp > (now + duration) ) iQ->s.expired = iQ->s.closed = 1; if ( iQ->s.offerNXT == nxt64bits && iQ->s.closed == 0 && iQ->s.pending == 0 ) { if ( iQ->s.baseid == refiQ->s.baseid && iQ->s.relid == refiQ->s.relid && iQ->s.isask != refiQ->s.isask && (metric= ordermetric(iQ->s.price,iQ->s.vol,dir,refiQ->s.price,refiQ->s.vol)) > bestmetric ) { bestmetric = metric; bestiQ = iQ; } else if ( iQ->s.baseid == refiQ->s.relid && iQ->s.relid == refiQ->s.baseid && iQ->s.isask == refiQ->s.isask && iQ->s.price > SMALLVAL ) { revvol = (iQ->s.price * iQ->s.vol), revprice = (1. / iQ->s.price); if ( (metric= ordermetric(revprice,revvol,dir,refiQ->s.price,refiQ->s.vol)) > bestmetric ) { bestmetric = metric; bestiQ = iQ; } } } } if ( bestmetric > 0. ) { if ( (prices= prices777_find(&inverted,bestiQ->s.baseid,bestiQ->s.relid,exchange_str(bestiQ->exchangeid))) != 0 ) { printf("isask.%d %f %f -> bestmetric %f inverted.%d autofill dir.%d price %f vol %f\n",bestiQ->s.isask,bestiQ->s.price,bestiQ->s.vol,bestmetric,inverted,dir,refiQ->s.price,refiQ->s.vol); if ( bestiQ->s.isask != 0 ) dir = -1; else dir = 1; if ( inverted != 0 ) { dir *= -1; volume = (bestiQ->s.price * bestiQ->s.vol); price = 1. / bestiQ->s.price; printf("price inverted (%f %f) -> (%f %f)\n",bestiQ->s.price,bestiQ->s.vol,price,volume); } else price = bestiQ->s.price, volume = bestiQ->s.vol; retstr = prices777_trade(0,0,0,0,1,0,NXTaddr,NXTACCTSECRET,prices,dir,price,volume,bestiQ,0,bestiQ->s.quoteid,0); } } return(retstr); } char *automatch(struct prices777 *prices,int32_t dir,double refprice,double refvol,char *NXTaddr,char *NXTACCTSECRET) { int32_t i,n=0; struct prices777_order order,bestorder; char *retstr = 0; double metric,bestmetric = 0.; return(0); memset(&bestorder,0,sizeof(bestorder)); if ( dir > 0 ) n = prices->O.numasks; else if ( dir < 0 ) n = prices->O.numbids; if ( n > 0 ) { for (i=0; i 0) ? prices->O.book[MAX_GROUPS][i].ask : prices->O.book[MAX_GROUPS][i].bid; if ( (metric= ordermetric(order.s.price,order.s.vol,dir,refprice,refvol)) > bestmetric ) { bestmetric = metric; bestorder = order; } } } //printf("n.%d\n",n); if ( bestorder.source != 0 ) retstr = prices777_trade(0,0,0,0,1,0,NXTaddr,NXTACCTSECRET,bestorder.source,bestorder.s.isask!=0?-1:1,bestorder.s.price,bestorder.s.vol,0,&bestorder,bestorder.s.quoteid,0); return(retstr); } int offer_checkitem(struct pending_trade *pend,cJSON *item) { uint64_t quoteid; struct InstantDEX_quote *iQ; if ( (quoteid= j64bits(item,"quoteid")) != 0 && (iQ= find_iQ(quoteid)) != 0 && iQ->s.closed != 0 ) return(0); return(-1); } void trades_update() { #ifdef later int32_t iter; struct pending_trade *pend; for (iter=0; iter<2; iter++) { while ( (pend= queue_dequeue(&Pending_offersQ.pingpong[iter],0)) != 0 ) { if ( time(NULL) > pend->expiration ) { printf("now.%ld vs timestamp.%u vs expiration %u | ",(long)time(NULL),pend->timestamp,pend->expiration); printf("offer_statemachine %llu/%llu %d %f %f\n",(long long)pend->orderid,(long long)pend->quoteid,pend->dir,pend->price,pend->volume); //InstantDEX_history(1,pend,retstr); if ( pend->bot == 0 ) free_pending(pend); else pend->finishtime = (uint32_t)time(NULL); } else { printf("InstantDEX_update requeue %llu/%llu %d %f %f\n",(long long)pend->orderid,(long long)pend->quoteid,pend->dir,pend->price,pend->volume); queue_enqueue("requeue",&Pending_offersQ.pingpong[iter ^ 1],&pend->DL,0); } } } #endif } void InstantDEX_update(char *NXTaddr,char *NXTACCTSECRET) { int32_t dir; double price,volume; uint32_t now; char *retstr = 0; int32_t inverted; struct InstantDEX_quote *iQ,*tmp; struct prices777 *prices; uint64_t nxt64bits = calc_nxt64bits(NXTaddr); now = (uint32_t)time(NULL); HASH_ITER(hh,AllQuotes,iQ,tmp) { if ( iQ->s.timestamp > (now + ORDERBOOK_EXPIRATION) ) iQ->s.expired = iQ->s.closed = 1; if ( iQ->s.offerNXT == nxt64bits && iQ->s.closed == 0 && iQ->s.pending == 0 ) { if ( (prices= prices777_find(&inverted,iQ->s.baseid,iQ->s.relid,exchange_str(iQ->exchangeid))) != 0 ) { if ( iQ->s.isask != 0 ) dir = -1; else dir = 1; if ( inverted != 0 ) { dir *= -1; volume = (iQ->s.price * iQ->s.vol); price = 1. / iQ->s.price; printf("price inverted (%f %f) -> (%f %f)\n",iQ->s.price,iQ->s.vol,price,volume); } else price = iQ->s.price, volume = iQ->s.vol; if ( (retstr= automatch(prices,dir,price,volume,NXTaddr,NXTACCTSECRET)) != 0 ) { printf("automatched %s isask.%d %f %f (%s)\n",prices->contract,iQ->s.isask,iQ->s.price,iQ->s.vol,retstr); free(retstr); } } } } trades_update(); } int32_t is_specialexchange(char *exchangestr) { if ( strcmp(exchangestr,"InstantDEX") == 0 || strcmp(exchangestr,"jumblr") == 0 || strcmp(exchangestr,"pangea") == 0 || strcmp(exchangestr,"peggy") == 0 || strcmp(exchangestr,"wallet") == 0 || strcmp(exchangestr,"active") == 0 || strncmp(exchangestr,"basket",strlen("basket")) == 0 ) return(1); return(0); } char *InstantDEX_placebidask(char *remoteaddr,uint64_t orderid,char *exchangestr,char *name,char *base,char *rel,struct InstantDEX_quote *iQ,char *extra,char *secret,char *activenxt,cJSON *origjson) { struct exchange_info *exchange; cJSON *obj; char walletstr[256],*str,*retstr = 0; int32_t inverted,dir; struct prices777 *prices; double price,volume; if ( secret == 0 || activenxt == 0 ) { secret = IGUANA_NXTACCTSECRET; activenxt = IGUANA_NXTADDR; } //printf("placebidask.(%s)\n",jprint(origjson,0)); if ( (obj= jobj(origjson,"wallet")) != 0 ) { str = jprint(obj,1); safecopy(walletstr,str,sizeof(walletstr)); free(str), str = 0; } else walletstr[0] = 0; if ( exchangestr != 0 && (exchange= exchange_find(exchangestr)) != 0 ) iQ->exchangeid = exchange->exchangeid; if ( iQ->exchangeid < 0 || (exchangestr= exchange_str(iQ->exchangeid)) == 0 ) { printf("exchangestr.%s id.%d\n",exchangestr,iQ->exchangeid); return(clonestr("{\"error\":\"exchange not active, check SuperNET.conf exchanges array\"}\n")); } //printf("walletstr.(%s)\n",walletstr); if ( (prices= prices777_find(&inverted,iQ->s.baseid,iQ->s.relid,exchangestr)) == 0 ) prices = prices777_poll(exchangestr,name,base,iQ->s.baseid,rel,iQ->s.relid); if ( prices != 0 ) { price = iQ->s.price, volume = iQ->s.vol; if ( price < SMALLVAL || volume < SMALLVAL ) { printf("price %f volume %f error\n",price,volume); return(clonestr("{\"error\":\"prices777_trade invalid price or volume\"}\n")); } if ( iQ->s.isask != 0 ) dir = -1; else dir = 1; if ( inverted != 0 ) { dir *= -1; volume *= price; price = 1. / price; printf("price inverted (%f %f) -> (%f %f)\n",iQ->s.price,iQ->s.vol,price,volume); } //printf("dir.%d price %f vol %f isask.%d remoteaddr.%p\n",dir,price,volume,iQ->s.isask,remoteaddr); if ( remoteaddr == 0 ) { if ( is_specialexchange(exchangestr) == 0 ) return(prices777_trade(0,0,0,0,1,0,activenxt,secret,prices,dir,price,volume,iQ,0,iQ->s.quoteid,extra)); //printf("check automatch\n"); //if ( strcmp(exchangestr,"wallet") != 0 && strcmp(exchangestr,"jumblr") != 0 && strcmp(exchangestr,"pangea") != 0 && iQ->s.automatch != 0 && (SUPERNET.automatch & 1) != 0 && (retstr= automatch(prices,dir,volume,price,activenxt,secret)) != 0 ) // return(retstr); if ( strcmp(IGUANA_NXTACCTSECRET,secret) != 0 ) return(clonestr("{\"error\":\"cant do queued requests with non-default accounts\"}")); retstr = InstantDEX_str(walletstr,0,1,iQ); //printf("create_iQ.(%llu) quoteid.%llu walletstr.(%s) %p\n",(long long)iQ->s.offerNXT,(long long)iQ->s.quoteid,walletstr,walletstr); iQ = create_iQ(iQ,walletstr); printf("local got create_iQ.(%llu) quoteid.%llu wallet.(%s) baseamount %llu iswallet.%d\n",(long long)iQ->s.offerNXT,(long long)iQ->s.quoteid,walletstr,(long long)iQ->s.baseamount,iQ->s.wallet); prices777_InstantDEX(prices,MAX_DEPTH); queue_enqueue("InstantDEX",&InstantDEXQ,queueitem(retstr),0); } else { iQ = create_iQ(iQ,walletstr); if ( (retstr= autofill(remoteaddr,iQ,activenxt,secret)) == 0 ) { //printf("create_iQ.(%llu) quoteid.%llu\n",(long long)iQ->s.offerNXT,(long long)iQ->s.quoteid); if ( strcmp(IGUANA_NXTACCTSECRET,secret) != 0 ) return(clonestr("{\"error\":\"cant do queued requests with non-default accounts\"}")); prices777_InstantDEX(prices,MAX_DEPTH); printf("remote got create_iQ.(%llu) quoteid.%llu wallet.(%s) baseamount %llu\n",(long long)iQ->s.offerNXT,(long long)iQ->s.quoteid,walletstr,(long long)iQ->s.baseamount); } return(retstr); } } else printf("cant find prices\n"); if ( retstr == 0 ) retstr = clonestr("{\"error\":\"cant get prices ptr\"}"); return(retstr); } #endif #endif