diff --git a/iguana/exchanges/LP_mpnet.c b/iguana/exchanges/LP_mpnet.c new file mode 100644 index 000000000..38d37c626 --- /dev/null +++ b/iguana/exchanges/LP_mpnet.c @@ -0,0 +1,1327 @@ + +/****************************************************************************** + * Copyright © 2014-2018 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. * + * * + ******************************************************************************/ +// +// LP_prices.c +// marketmaker +// + +struct LP_orderbookentry +{ + bits256 pubkey; + double price; + int64_t avesatoshis,maxsatoshis,depth,dynamictrust; + uint32_t timestamp; + int32_t numutxos; + char coinaddr[64]; +}; + +struct LP_priceinfo +{ + char symbol[68]; + uint64_t coinbits; + int32_t ind,pad; + double diagval,high[2],low[2],last[2],bid[2],ask[2]; + double relvals[LP_MAXPRICEINFOS]; + double myprices[2][LP_MAXPRICEINFOS]; + double minprices[LP_MAXPRICEINFOS]; // autoprice + double fixedprices[LP_MAXPRICEINFOS]; // fixedprices + double buymargins[LP_MAXPRICEINFOS]; + double sellmargins[LP_MAXPRICEINFOS]; + double offsets[LP_MAXPRICEINFOS]; + double factors[LP_MAXPRICEINFOS]; +} LP_priceinfos[LP_MAXPRICEINFOS]; +int32_t LP_numpriceinfos; + +struct LP_cacheinfo +{ + UT_hash_handle hh; + struct LP_quoteinfo Q; + uint8_t key[sizeof(bits256)+sizeof(uint64_t)*2+sizeof(int32_t)]; + double price; + uint32_t timestamp; +} *LP_cacheinfos; + +void LP_priceinfos_clear() +{ + int32_t i; struct LP_priceinfo *pp; + for (i=0; imyprices,0,sizeof(pp->myprices)); + memset(pp->minprices,0,sizeof(pp->minprices)); + memset(pp->fixedprices,0,sizeof(pp->fixedprices)); + memset(pp->buymargins,0,sizeof(pp->buymargins)); + memset(pp->sellmargins,0,sizeof(pp->sellmargins)); + memset(pp->offsets,0,sizeof(pp->offsets)); + memset(pp->factors,0,sizeof(pp->factors)); + } +} + +float LP_pubkey_price(int32_t *numutxosp,int64_t *avesatoshisp,int64_t *maxsatoshisp,struct LP_pubkey_info *pubp,uint32_t baseind,uint32_t relind) +{ + struct LP_pubkey_quote *pq,*tmp; int32_t scale; int64_t scale64; + *numutxosp = 0; + *avesatoshisp = *maxsatoshisp = 0; + DL_FOREACH_SAFE(pubp->quotes,pq,tmp) + { + if ( baseind == pq->baseind && relind == pq->relind ) + { + if ( (scale= pq->scale) == 0 ) + pq->scale = scale = 6; + scale64 = 1; + while ( scale > 0 ) + { + scale64 *= 10; + scale--; + } + *numutxosp = pq->numutxos; + *avesatoshisp = pq->aveutxo * scale64; + *maxsatoshisp = pq->maxutxo * scale64; + return(pq->price); + } + } + return(0); +} + +void LP_pubkey_update(struct LP_pubkey_info *pubp,uint32_t baseind,uint32_t relind,float price,int64_t balance,char *utxocoin,int32_t numutxos,int64_t minutxo,int64_t maxutxo) +{ + struct LP_pubkey_quote *pq,*tmp; int64_t aveutxo,scale64,ave64,max64; int32_t scale; + DL_FOREACH_SAFE(pubp->quotes,pq,tmp) + { + if ( baseind == pq->baseind && relind == pq->relind ) + break; + pq = 0; + } + if ( pq == 0 ) + { + pq = calloc(1,sizeof(*pq)); + pq->baseind = baseind; + pq->relind = relind; + pq->scale = 6; // millions of SATOSHIS, ie. 0.01 + DL_APPEND(pubp->quotes,pq); // already serialized as only path is via stats_JSON() + //printf("create pubp quotes %d/%d\n",baseind,relind); + } +//printf("%d/%d price %.8f balance %.8f %s num.%d min %.8f max %.8f\n",baseind,relind,price,dstr(balance),utxocoin,numutxos,dstr(minutxo),dstr(maxutxo)); + pq->price = price; + if ( utxocoin != 0 && utxocoin[0] != 0 ) + { + if ( (scale= pq->scale) == 0 ) + pq->scale = scale = 6; + scale64 = 1; + while ( scale > 0 ) + { + scale64 *= 10; + scale--; + } + if ( numutxos >= 256 ) + pq->numutxos = 255; + else pq->numutxos = numutxos; + aveutxo = (balance + (scale64>>1)) / numutxos; + if ( (ave64= (aveutxo / scale64)) >= (1LL << 32) ) + ave64 = (1LL << 32) - 1; + max64 = ((maxutxo + (scale64>>1)) / scale64); + if ( max64 >= (1LL << 32) ) + max64 = (1LL << 32) - 1; + pq->aveutxo = (uint32_t)ave64; + pq->maxutxo = (uint32_t)max64; + if ( 0 ) + { + printf("price %.8f base.%s rel.%s utxocoin.%s balance %.8f numutxos.%u %u scale64 = %llu, ave %llu, ave32 %u (%llu) max32 %u (%llu)\n",price,LP_priceinfos[baseind].symbol,LP_priceinfos[relind].symbol,utxocoin,dstr(balance),numutxos,pq->numutxos,(long long)scale64,(long long)aveutxo,pq->aveutxo,(long long)pq->aveutxo * scale64,pq->maxutxo,(long long)pq->maxutxo * scale64); + int64_t avesatoshis,maxsatoshis; + price = LP_pubkey_price(&numutxos,&avesatoshis,&maxsatoshis,pubp,baseind,relind); + printf("checkprice %.8f numutxos.%d ave %.8f max %.8f\n",price,numutxos,dstr(avesatoshis),dstr(maxsatoshis)); + } + } +} + +struct LP_priceinfo *LP_priceinfo(int32_t ind) +{ + if ( ind < 0 || ind >= LP_MAXPRICEINFOS ) + return(0); + else return(&LP_priceinfos[ind]); +} + +char *LP_priceinfostr(int32_t ind) +{ + if ( ind < 0 || ind >= LP_MAXPRICEINFOS ) + return("UNKNOWN"); + else return(LP_priceinfos[ind].symbol); +} + +int32_t LP_pricevalid(double price) +{ + if ( price > SMALLVAL && isnan(price) == 0 && price < SATOSHIDEN ) + return(1); + else return(0); +} + +struct LP_priceinfo *LP_priceinfofind(char *symbol) +{ + int32_t i; struct LP_priceinfo *pp; uint64_t coinbits; + if ( symbol == 0 || symbol[0] == 0 ) + return(0); + if ( LP_numpriceinfos > 0 ) + { + coinbits = stringbits(symbol); + pp = LP_priceinfos; + for (i=0; icoinbits == coinbits ) + return(pp); + } + return(0); +} + +int32_t LP_priceinfoind(char *symbol) +{ + struct LP_priceinfo *pp; + if ( (pp= LP_priceinfofind(symbol)) != 0 ) + return(pp->ind); + else return(-1); +} + +struct LP_priceinfo *LP_priceinfoptr(int32_t *indp,char *base,char *rel) +{ + struct LP_priceinfo *basepp,*relpp; + if ( (basepp= LP_priceinfofind(base)) != 0 && (relpp= LP_priceinfofind(rel)) != 0 ) + { + *indp = relpp->ind; + return(basepp); + } + else + { + *indp = -1; + return(0); + } +} + +int32_t LP_cachekey(uint8_t *key,char *base,char *rel,bits256 txid,int32_t vout) +{ + uint64_t basebits,relbits; int32_t offset = 0; + basebits = stringbits(base); + relbits = stringbits(rel); + memcpy(&key[offset],&basebits,sizeof(basebits)), offset += sizeof(basebits); + memcpy(&key[offset],&relbits,sizeof(relbits)), offset += sizeof(relbits); + memcpy(&key[offset],&txid,sizeof(txid)), offset += sizeof(txid); + memcpy(&key[offset],&vout,sizeof(vout)), offset += sizeof(vout); + return(offset); +} + +struct LP_cacheinfo *LP_cachefind(char *base,char *rel,bits256 txid,int32_t vout) +{ + struct LP_cacheinfo *ptr=0; uint8_t key[sizeof(bits256)+sizeof(uint64_t)*2+sizeof(vout)]; + if ( base == 0 || rel == 0 ) + return(0); + if ( LP_cachekey(key,base,rel,txid,vout) == sizeof(key) ) + { + portable_mutex_lock(&LP_cachemutex); + HASH_FIND(hh,LP_cacheinfos,key,sizeof(key),ptr); + portable_mutex_unlock(&LP_cachemutex); + } else printf("LP_cachefind keysize mismatch?\n"); + if ( 0 && ptr != 0 && ptr->timestamp != 0 && ptr->timestamp < time(NULL)-LP_CACHEDURATION ) + { + printf("expire price %.8f\n",ptr->price); + ptr->price = 0.; + ptr->timestamp = 0; + memset(&ptr->Q,0,sizeof(ptr->Q)); + } + return(ptr); +} + +struct LP_pubkey_info *LP_pubkey_rmd160find(uint8_t rmd160[20]) +{ + struct LP_pubkey_info *pubp=0,*tmp; + portable_mutex_lock(&LP_pubkeymutex); + HASH_ITER(hh,LP_pubkeyinfos,pubp,tmp) + { + if ( memcmp(rmd160,pubp->rmd160,sizeof(pubp->rmd160)) == 0 ) + break; + pubp = 0; + } + portable_mutex_unlock(&LP_pubkeymutex); + return(pubp); +} + +struct LP_address *_LP_addressfind(struct iguana_info *coin,char *coinaddr) +{ + uint8_t rmd160[20],addrtype; struct LP_address *ap; struct LP_pubkey_info *pubp; + HASH_FIND(hh,coin->addresses,coinaddr,strlen(coinaddr),ap); + if ( ap != 0 && bits256_nonz(ap->pubkey) == 0 ) + { + bitcoin_addr2rmd160(coin->symbol,coin->taddr,&addrtype,rmd160,coinaddr); + if ( (pubp= LP_pubkey_rmd160find(rmd160)) != 0 ) + { + ap->pubkey = pubp->pubkey; + memcpy(ap->pubsecp,pubp->pubsecp,sizeof(ap->pubsecp)); + } + } + return(ap); +} + +struct LP_address *_LP_addressadd(struct iguana_info *coin,char *coinaddr) +{ + uint8_t rmd160[20],addrtype; struct LP_address *ap; struct LP_pubkey_info *pubp; + ap = calloc(1,sizeof(*ap)); + safecopy(ap->coinaddr,coinaddr,sizeof(ap->coinaddr)); + bitcoin_addr2rmd160(coin->symbol,coin->taddr,&addrtype,rmd160,coinaddr); + if ( (pubp= LP_pubkey_rmd160find(rmd160)) != 0 ) + { + ap->pubkey = pubp->pubkey; + memcpy(ap->pubsecp,pubp->pubsecp,sizeof(ap->pubsecp)); + } + //printf("LP_ADDRESS %s ADD.(%s)\n",coin->symbol,coinaddr); + HASH_ADD_KEYPTR(hh,coin->addresses,ap->coinaddr,strlen(ap->coinaddr),ap); + return(ap); +} + +struct LP_pubkey_info *LP_pubkeyfind(bits256 pubkey) +{ + struct LP_pubkey_info *pubp=0; + portable_mutex_lock(&LP_pubkeymutex); + HASH_FIND(hh,LP_pubkeyinfos,&pubkey,sizeof(pubkey),pubp); + portable_mutex_unlock(&LP_pubkeymutex); + return(pubp); +} + +struct LP_pubkey_info *LP_pubkeyadd(bits256 pubkey) +{ + char str[65]; struct LP_pubkey_info *pubp=0; + portable_mutex_lock(&LP_pubkeymutex); + HASH_FIND(hh,LP_pubkeyinfos,&pubkey,sizeof(pubkey),pubp); + if ( pubp == 0 ) + { + pubp = calloc(1,sizeof(*pubp)); + pubp->pubkey = pubkey; + pubp->pairsock = -1; + if ( bits256_cmp(G.LP_mypub25519,pubkey) == 0 ) + { + memcpy(pubp->rmd160,G.LP_myrmd160,sizeof(pubp->rmd160)); + memcpy(pubp->pubsecp,G.LP_pubsecp,sizeof(pubp->pubsecp)); + } + HASH_ADD_KEYPTR(hh,LP_pubkeyinfos,&pubp->pubkey,sizeof(pubp->pubkey),pubp); + HASH_FIND(hh,LP_pubkeyinfos,&pubkey,sizeof(pubkey),pubp); + if ( pubp == 0 ) + printf("pubkeyadd find %s error after add\n",bits256_str(str,pubp->pubkey)); + } + portable_mutex_unlock(&LP_pubkeymutex); + return(pubp); +} + +int32_t LP_pubkey_istrusted(bits256 pubkey) +{ + struct LP_pubkey_info *pubp; + if ( (pubp= LP_pubkeyadd(pubkey)) != 0 ) + return(pubp->istrusted != 0); + return(0); +} + +char *LP_pubkey_trustset(bits256 pubkey,uint32_t trustval) +{ + struct LP_pubkey_info *pubp; + if ( (pubp= LP_pubkeyadd(pubkey)) != 0 ) + { + pubp->istrusted = trustval; + return(clonestr("{\"result\":\"success\"}")); + } + return(clonestr("{\"error\":\"pubkey not found\"}")); +} + +char *LP_pubkey_trusted() +{ + struct LP_pubkey_info *pubp,*tmp; cJSON *array = cJSON_CreateArray(); + HASH_ITER(hh,LP_pubkeyinfos,pubp,tmp) + { + if ( pubp->istrusted != 0 ) + jaddibits256(array,pubp->pubkey); + } + return(jprint(array,1)); +} + +int64_t LP_unspents_metric(struct iguana_info *coin,char *coinaddr) +{ + cJSON *array,*item; int32_t i,n; int64_t metric=0,total; + //LP_listunspent_both(coin->symbol,coinaddr,0); + if ( (array= LP_address_utxos(coin,coinaddr,1)) != 0 ) + { + total = 0; + if ( (n= cJSON_GetArraySize(array)) > 0 ) + { + for (i=0; imatrix[baseid][relid]; + if ( LP_pricevalid(price) > 0 ) + { + item = cJSON_CreateArray(); + jaddistr(item,base); + jaddistr(item,LP_priceinfos[relid].symbol); + jaddinum(item,price); + jaddi(array,item); + } + } + } + jaddbits256(obj,"pubkey",pubp->pubkey); + init_hexbytes_noT(hexstr,pubp->rmd160,sizeof(pubp->rmd160)); + jaddstr(obj,"rmd160",hexstr); + init_hexbytes_noT(hexstr2,pubp->pubsecp,sizeof(pubp->pubsecp)); + jaddstr(obj,"pubsecp",hexstr2); + init_hexbytes_noT(sigstr,pubp->sig,pubp->siglen); + jaddstr(obj,"sig",sigstr); + jaddnum(obj,"timestamp",pubp->timestamp); + jadd(obj,"asks",array); + if ( pubp->istrusted != 0 ) + jaddnum(obj,"istrusted",pubp->istrusted); + return(obj); +} + +char *LP_prices() +{ + struct LP_pubkey_info *pubp,*tmp; cJSON *array = cJSON_CreateArray(); + HASH_ITER(hh,LP_pubkeyinfos,pubp,tmp) + { + jaddi(array,LP_pubkeyjson(pubp)); + } + return(jprint(array,1)); +} + +double LP_pricecache(struct LP_quoteinfo *qp,char *base,char *rel,bits256 txid,int32_t vout) +{ + struct LP_cacheinfo *ptr; + if ( (ptr= LP_cachefind(base,rel,txid,vout)) != 0 ) + { + if ( qp != 0 ) + (*qp) = ptr->Q; + if ( ptr->price == 0. && ptr->Q.satoshis > ptr->Q.txfee ) + { + ptr->price = (double)ptr->Q.destsatoshis / (ptr->Q.satoshis - ptr->Q.txfee); + if ( LP_pricevalid(ptr->price) <= 0 ) + ptr->price = 0.; + printf("LP_pricecache: set %s/%s ptr->price %.8f\n",base,rel,ptr->price); + } + //printf(">>>>>>>>>> found %s/%s %.8f\n",base,rel,ptr->price); + return(ptr->price); + } + //char str[65]; printf("cachemiss %s/%s %s/v%d\n",base,rel,bits256_str(str,txid),vout); + return(0.); +} + +void LP_priceinfoupdate(char *base,char *rel,double price) +{ + struct LP_priceinfo *basepp,*relpp; + if ( LP_pricevalid(price) > 0 ) + { + if ( (basepp= LP_priceinfofind(base)) != 0 && (relpp= LP_priceinfofind(rel)) != 0 ) + { + dxblend(&basepp->relvals[relpp->ind],price,0.9); + dxblend(&relpp->relvals[basepp->ind],1. / price,0.9); + //basepp->relvals[relpp->ind] = price; + //relpp->relvals[basepp->ind] = 1. / price; + } + } +} + +double LP_myprice(int32_t iambob,double *bidp,double *askp,char *base,char *rel) +{ + struct LP_priceinfo *basepp,*relpp; double val; + *bidp = *askp = 0.; + if ( (basepp= LP_priceinfofind(base)) != 0 && (relpp= LP_priceinfofind(rel)) != 0 ) + { + *askp = basepp->myprices[iambob][relpp->ind]; + if ( LP_pricevalid(*askp) > 0 ) + { + val = relpp->myprices[iambob][basepp->ind]; + if ( LP_pricevalid(val) > 0 ) + { + *bidp = 1. / val; + return((*askp + *bidp) * 0.5); + } + else + { + *bidp = 0.; + return(*askp); + } + } + else + { + val = relpp->myprices[iambob][basepp->ind]; + if ( LP_pricevalid(val) > 0 ) + { + *bidp = 1. / val; + *askp = 0.; + return(*bidp); + } + } + } + return(0.); +} + +char *LP_myprices(int32_t iambob) +{ + int32_t baseid,relid; double bid,ask; char *base,*rel; cJSON *item,*array; + array = cJSON_CreateArray(); + for (baseid=0; baseid SMALLVAL ) + { + item = cJSON_CreateObject(); + jaddstr(item,"base",base); + jaddstr(item,"rel",rel); + jaddnum(item,"bid",bid); + jaddnum(item,"ask",ask); + jaddi(array,item); + } + } + } + return(jprint(array,1)); +} + +int32_t LP_mypriceset(int32_t iambob,int32_t *changedp,char *base,char *rel,double price) +{ + struct LP_priceinfo *basepp=0,*relpp=0; struct LP_pubkey_info *pubp; double minprice,maxprice,margin,buymargin,sellmargin; + *changedp = 0; + //if ( strcmp("DEX",base) == 0 || strcmp("DEX",rel) == 0 ) + // printf("%s/%s setprice %.8f\n",base,rel,price); + if ( base != 0 && rel != 0 && (basepp= LP_priceinfofind(base)) != 0 && (relpp= LP_priceinfofind(rel)) != 0 ) + { + + if ( price == 0. || fabs(basepp->myprices[iambob][relpp->ind] - price)/price > 0.001 ) + *changedp = 1; + if ( iambob != 0 ) + { + sellmargin = relpp->sellmargins[basepp->ind]; + buymargin = relpp->buymargins[basepp->ind]; + margin = (sellmargin + buymargin) * 0.5; + if ( price == 0. ) + { + relpp->minprices[basepp->ind] = 0.; + relpp->fixedprices[basepp->ind] = 0.; + relpp->buymargins[basepp->ind] = 0.; + relpp->sellmargins[basepp->ind] = 0.; + relpp->offsets[basepp->ind] = 0.; + relpp->factors[basepp->ind] = 0.; + LP_autoref_clear(base,rel); + margin = 0.; + } + else if ( (minprice= basepp->minprices[relpp->ind]) > SMALLVAL && price < minprice ) + { + //printf("%s/%s price %.8f less than minprice %.8f\n",base,rel,price,minprice); + price = minprice * (1. - margin); + } + else if ( (maxprice= relpp->minprices[basepp->ind]) > SMALLVAL ) + { + if ( price > (1. / maxprice) ) + { + //printf("%s/%s price %.8f less than maxprice %.8f, more than %.8f\n",base,rel,price,maxprice,1./maxprice); + price = (1. / maxprice) * (1. + margin); + } + } + } + /*else if ( basepp->myprices[relpp->ind] > SMALLVAL ) + { + price = (basepp->myprices[relpp->ind] * 0.9) + (0.1 * price); + }*/ + basepp->myprices[iambob][relpp->ind] = price; // ask + //printf("LP_mypriceset base.%s rel.%s <- price %.8f\n",base,rel,price); + //relpp->myprices[basepp->ind] = (1. / price); // bid, but best to do one dir at a time + if ( iambob != 0 && (pubp= LP_pubkeyadd(G.LP_mypub25519)) != 0 ) + { + pubp->timestamp = (uint32_t)time(NULL); + LP_pubkey_update(pubp,basepp->ind,relpp->ind,price,0,0,0,0,0); + //pubp->matrix[basepp->ind][relpp->ind] = price; + //pubp->timestamps[basepp->ind][relpp->ind] = pubp->timestamp; + //pubp->matrix[relpp->ind][basepp->ind] = (1. / price); + } + return(0); + } + printf("base.%s rel.%s %p %p price %.8f error case\n",base!=0?base:"",rel!=0?rel:"",basepp,relpp,price); + return(-1); +} + +double LP_price(int32_t iambob,char *base,char *rel) +{ + struct LP_priceinfo *basepp; int32_t relind; double price = 0.; + if ( (basepp= LP_priceinfoptr(&relind,base,rel)) != 0 ) + { + if ( (price= basepp->myprices[iambob][relind]) == 0. ) + { + price = basepp->relvals[relind]; + } + } + return(price); +} + +double LP_getmyprice(int32_t iambob,char *base,char *rel) +{ + struct LP_priceinfo *basepp; int32_t relind; double price = 0.; + if ( (basepp= LP_priceinfoptr(&relind,base,rel)) != 0 ) + { + if ( (price= basepp->myprices[iambob][relind]) == 0. ) + { + } + } + return(price); +} + +cJSON *LP_priceinfomatrix(int32_t iambob,int32_t usemyprices) +{ + int32_t i,j,n,m; double total,sum,val; struct LP_priceinfo *pp; uint32_t now; struct LP_cacheinfo *ptr,*tmp; cJSON *vectorjson = cJSON_CreateObject(); + now = (uint32_t)time(NULL); + HASH_ITER(hh,LP_cacheinfos,ptr,tmp) + { + if ( ptr->timestamp < now-3600*2 || ptr->price == 0. ) + continue; + LP_priceinfoupdate(ptr->Q.srccoin,ptr->Q.destcoin,ptr->price); + } + pp = LP_priceinfos; + total = m = 0; + for (i=0; idiagval = sum = n = 0; + for (j=0; jmyprices[iambob][j]) == 0. ) + val = pp->relvals[j]; + if ( val > SMALLVAL ) + { + sum += val; + n++; + } + } + if ( n > 0 ) + { + pp->diagval = sum / n; + total += pp->diagval, m++; + } + } + if ( m > 0 ) + { + pp = LP_priceinfos; + for (i=0; idiagval > SMALLVAL ) + { + pp->diagval /= total; + jaddnum(vectorjson,pp->symbol,pp->diagval); + } + } + } + return(vectorjson); +} + +struct LP_priceinfo *LP_priceinfoadd(char *symbol) +{ + struct LP_priceinfo *pp; cJSON *retjson; + if ( symbol == 0 ) + return(0); + if ( (pp= LP_priceinfofind(symbol)) != 0 ) + { + printf("%s already there\n",symbol); + return(pp); + } + if ( LP_numpriceinfos >= sizeof(LP_priceinfos)/sizeof(*LP_priceinfos) ) + { + printf("cant add any more priceinfos than %d\n",LP_numpriceinfos); + return(0); + } + pp = &LP_priceinfos[LP_numpriceinfos]; + memset(pp,0,sizeof(*pp)); + safecopy(pp->symbol,symbol,sizeof(pp->symbol)); + pp->coinbits = stringbits(symbol); + pp->ind = LP_numpriceinfos++; + //LP_numpriceinfos++; + if ( (retjson= LP_priceinfomatrix(1,0)) != 0 ) + free_json(retjson); + return(pp); +} + +struct LP_cacheinfo *LP_cacheadd(char *base,char *rel,bits256 txid,int32_t vout,double price,struct LP_quoteinfo *qp) +{ + char str[65]; struct LP_cacheinfo *ptr=0; + if ( base == 0 || rel == 0 ) + return(0); + if ( LP_pricevalid(price) > 0 ) + { + if ( (ptr= LP_cachefind(base,rel,txid,vout)) == 0 ) + { + ptr = calloc(1,sizeof(*ptr)); + if ( LP_cachekey(ptr->key,base,rel,txid,vout) == sizeof(ptr->key) ) + { + portable_mutex_lock(&LP_cachemutex); + HASH_ADD(hh,LP_cacheinfos,key,sizeof(ptr->key),ptr); + portable_mutex_unlock(&LP_cachemutex); + } else printf("LP_cacheadd keysize mismatch?\n"); + } + ptr->Q = *qp; + ptr->timestamp = (uint32_t)time(NULL); + if ( price != ptr->price ) + { + ptr->price = price; + LP_priceinfoupdate(base,rel,price); + printf("updated %s/v%d %s/%s %llu price %.8f\n",bits256_str(str,txid),vout,base,rel,(long long)qp->satoshis,price); + } else ptr->price = price; + } + return(ptr); +} + +static int _cmp_orderbook(const void *a,const void *b) +{ + int32_t retval = 0; +#define ptr_a (*(struct LP_orderbookentry **)a)->price +#define ptr_b (*(struct LP_orderbookentry **)b)->price + if ( ptr_b > ptr_a ) + retval = -1; + else if ( ptr_b < ptr_a ) + retval = 1; + else + { +#undef ptr_a +#undef ptr_b +#define ptr_a ((struct LP_orderbookentry *)a)->maxsatoshis +#define ptr_b ((struct LP_orderbookentry *)b)->maxsatoshis + if ( ptr_b > ptr_a ) + return(-1); + else if ( ptr_b < ptr_a ) + return(1); + } + // printf("%.8f vs %.8f -> %d\n",ptr_a,ptr_b,retval); + return(retval); +#undef ptr_a +#undef ptr_b +} + +static int _revcmp_orderbook(const void *a,const void *b) +{ + int32_t retval = 0; +#define ptr_a (*(struct LP_orderbookentry **)a)->price +#define ptr_b (*(struct LP_orderbookentry **)b)->price + if ( ptr_b > ptr_a ) + retval = 1; + else if ( ptr_b < ptr_a ) + retval = -1; + else + { +#undef ptr_a +#undef ptr_b +#define ptr_a ((struct LP_orderbookentry *)a)->maxsatoshis +#define ptr_b ((struct LP_orderbookentry *)b)->maxsatoshis + if ( ptr_b > ptr_a ) + return(-1); + else if ( ptr_b < ptr_a ) + return(1); + } + // printf("%.8f vs %.8f -> %d\n",ptr_a,ptr_b,retval); + return(retval); +#undef ptr_a +#undef ptr_b +} + +cJSON *LP_orderbookjson(char *symbol,struct LP_orderbookentry *op) +{ + cJSON *item = cJSON_CreateObject(); + if ( LP_pricevalid(op->price) > 0 ) + { + jaddstr(item,"coin",symbol); + jaddstr(item,"address",op->coinaddr); + jaddnum(item,"price",op->price); + jaddnum(item,"numutxos",op->numutxos); + jaddnum(item,"avevolume",dstr(op->avesatoshis)); + jaddnum(item,"maxvolume",dstr(op->maxsatoshis)); + jaddnum(item,"depth",dstr(op->depth)); + jaddbits256(item,"pubkey",op->pubkey); + jaddnum(item,"age",time(NULL)-op->timestamp); + jaddnum(item,"zcredits",dstr(op->dynamictrust)); + } + return(item); +} + +struct LP_orderbookentry *LP_orderbookentry(char *address,char *base,char *rel,double price,int32_t numutxos,int64_t avesatoshis,int64_t maxsatoshis,bits256 pubkey,uint32_t timestamp,int64_t balance,int64_t dynamictrust) +{ + struct LP_orderbookentry *op; + if ( (op= calloc(1,sizeof(*op))) != 0 ) + { + safecopy(op->coinaddr,address,sizeof(op->coinaddr)); + op->price = price; + op->numutxos = numutxos; + op->avesatoshis = avesatoshis; + op->maxsatoshis = maxsatoshis; + op->pubkey = pubkey; + op->timestamp = timestamp; + op->depth = balance; + op->dynamictrust = dynamictrust; + } + return(op); +} + +void LP_pubkeys_query() +{ + uint8_t zeroes[20]; bits256 zero; cJSON *reqjson; struct LP_pubkey_info *pubp=0,*tmp; + memset(zero.bytes,0,sizeof(zero)); + memset(zeroes,0,sizeof(zeroes)); + HASH_ITER(hh,LP_pubkeyinfos,pubp,tmp) + { + if ( memcmp(zeroes,pubp->rmd160,sizeof(pubp->rmd160)) == 0 && time(NULL) > pubp->lasttime+60 ) + { + pubp->lasttime = (uint32_t)time(NULL); + reqjson = cJSON_CreateObject(); + jaddstr(reqjson,"method","wantnotify"); + jaddbits256(reqjson,"pub",pubp->pubkey); + //printf("LP_pubkeys_query %s\n",jprint(reqjson,0)); + LP_reserved_msg(0,"","",zero,jprint(reqjson,1)); + } + } +} + +int32_t LP_orderbook_utxoentries(uint32_t now,int32_t polarity,char *base,char *rel,struct LP_orderbookentry *(**arrayp),int32_t num,int32_t cachednum,int32_t duration) +{ + char coinaddr[64]; uint8_t zeroes[20]; struct LP_pubkey_info *pubp=0,*tmp; struct LP_priceinfo *basepp; struct LP_orderbookentry *op; struct LP_address *ap; struct iguana_info *basecoin; uint32_t oldest; double price; int32_t baseid,relid,n; int64_t maxsatoshis,balance,avesatoshis; + if ( (basepp= LP_priceinfoptr(&relid,base,rel)) != 0 ) + baseid = basepp->ind; + else return(num); + if ( (basecoin= LP_coinfind(base)) == 0 ) + return(-1); + now = (uint32_t)time(NULL); + oldest = now - duration; + memset(zeroes,0,sizeof(zeroes)); + HASH_ITER(hh,LP_pubkeyinfos,pubp,tmp) + { + if ( memcmp(zeroes,pubp->rmd160,sizeof(pubp->rmd160)) == 0 ) + { + //printf("skip pubp since no rmd160\n"); + continue; + } + if ( pubp->timestamp < oldest ) + continue; + bitcoin_address(base,coinaddr,basecoin->taddr,basecoin->pubtype,pubp->pubsecp,33); + avesatoshis = maxsatoshis = n = 0; + ap = 0; + if ( (price= LP_pubkey_price(&n,&avesatoshis,&maxsatoshis,pubp,baseid,relid)) > SMALLVAL ) //pubp->matrix[baseid][relid]) > SMALLVAL )//&& pubp->timestamps[baseid][relid] >= oldest ) + { + balance = avesatoshis * n; + //if ( (ap= LP_addressfind(basecoin,coinaddr)) != 0 ) + { + //n = LP_address_minmax(&balance,&minsatoshis,&maxsatoshis,ap); + if ( polarity > 0 ) + { + balance *= price; + avesatoshis *= price; + maxsatoshis *= price; + } + //printf("%s/%s %s n.%d ap->n.%d %.8f\n",base,rel,coinaddr,n,ap->n,dstr(ap->total)); + } + if ( (op= LP_orderbookentry(coinaddr,base,rel,polarity > 0 ? price : 1./price,n,avesatoshis,maxsatoshis,pubp->pubkey,pubp->timestamp,balance,pubp->dynamictrust)) != 0 ) + { + *arrayp = realloc(*arrayp,sizeof(*(*arrayp)) * (num+1)); + (*arrayp)[num++] = op; + } + } + //printf("pubp.(%s) %.8f %p\n",coinaddr,price,ap); + } + return(num); +} + +char *LP_orderbook(char *base,char *rel,int32_t duration) +{ + uint32_t now,i; int64_t depth,askdepth=0,biddepth=0; struct LP_priceinfo *basepp=0,*relpp=0; struct LP_orderbookentry **bids = 0,**asks = 0; cJSON *retjson,*array; struct iguana_info *basecoin,*relcoin; int32_t n,numbids=0,numasks=0,cachenumbids,cachenumasks,baseid,relid,suppress_prefetch=0; + basecoin = LP_coinfind(base); + relcoin = LP_coinfind(rel); + if ( basecoin == 0 || relcoin == 0 ) + return(clonestr("{\"error\":\"base or rel not added\"}")); + if ( (basepp= LP_priceinfofind(base)) == 0 || (relpp= LP_priceinfofind(rel)) == 0 ) + return(clonestr("{\"error\":\"base or rel not added\"}")); + if ( duration <= 0 ) + { + if ( duration < 0 ) + suppress_prefetch = 1; + duration = LP_ORDERBOOK_DURATION; + } + //LP_pubkeys_query(); + baseid = basepp->ind; + relid = relpp->ind; + now = (uint32_t)time(NULL); + basecoin->obooktime = now; + relcoin->obooktime = now; + cachenumbids = numbids, cachenumasks = numasks; + //printf("start cache.(%d %d) numbids.%d numasks.%d\n",cachenumbids,cachenumasks,numbids,numasks); + numasks = LP_orderbook_utxoentries(now,1,base,rel,&asks,numasks,cachenumasks,duration); + numbids = LP_orderbook_utxoentries(now,-1,rel,base,&bids,numbids,cachenumbids,duration); + retjson = cJSON_CreateObject(); + array = cJSON_CreateArray(); + if ( numbids > 1 ) + { + qsort(bids,numbids,sizeof(*bids),_revcmp_orderbook); + depth = 0; + for (i=0; idepth; + bids[i]->depth = depth; + } + } + if ( numasks > 1 ) + { + qsort(asks,numasks,sizeof(*asks),_cmp_orderbook); + depth = 0; + for (i=0; idepth; + asks[i]->depth = depth; + } + } + for (i=n=0; idepth; + jaddi(array,LP_orderbookjson(rel,bids[i])); + if ( suppress_prefetch == 0 && n < 3 && bids[i]->numutxos == 0 ) + { + //printf("bid ping %s %s\n",rel,bids[i]->coinaddr); + LP_address(relcoin,bids[i]->coinaddr); + /*if ( 0 && relcoin->electrum == 0 ) + { + LP_listunspent_issue(rel,bids[i]->coinaddr,0); + //else if ( (tmpjson= LP_listunspent(rel,bids[i]->coinaddr)) != 0 ) + // free_json(tmpjson); + LP_listunspent_query(rel,bids[i]->coinaddr); + }*/ + n++; + } + if ( i == 0 ) + { + LP_priceinfoupdate(rel,base,1. / bids[i]->price); + //printf("update %s/%s %.8f [%.8f]\n",rel,base,1./bids[i]->price,bids[i]->price); + } + free(bids[i]); + bids[i] = 0; + } + if ( n > 0 && relcoin->lastmonitor > 3600 ) + relcoin->lastmonitor -= 3600; + jadd(retjson,"bids",array); + jaddnum(retjson,"numbids",numbids); + jaddnum(retjson,"biddepth",dstr(biddepth)); + array = cJSON_CreateArray(); + for (i=n=0; idepth; + jaddi(array,LP_orderbookjson(base,asks[i])); + if ( suppress_prefetch == 0 && n < 3 && asks[i]->numutxos == 0 ) + { + //printf("ask ping %s %s\n",base,asks[i]->coinaddr); + LP_address(basecoin,asks[i]->coinaddr); + /*if ( 0 && basecoin->electrum == 0 ) + { + LP_listunspent_issue(base,asks[i]->coinaddr,0); + //else if ( (tmpjson= LP_listunspent(base,asks[i]->coinaddr)) != 0 ) + // free_json(tmpjson); + LP_listunspent_query(base,asks[i]->coinaddr); + }*/ + n++; + } + if ( i == 0 ) + { + LP_priceinfoupdate(base,rel,asks[i]->price); + //printf("update %s/%s %.8f [%.8f]\n",base,rel,asks[i]->price,1./asks[i]->price); + } + free(asks[i]); + asks[i] = 0; + } + if ( n > 0 && basecoin->lastmonitor > 3600 ) + basecoin->lastmonitor -= 3600; + jadd(retjson,"asks",array); + jaddnum(retjson,"numasks",numasks); + jaddnum(retjson,"askdepth",dstr(askdepth)); + jaddstr(retjson,"base",base); + jaddstr(retjson,"rel",rel); + jaddnum(retjson,"timestamp",now); + jaddnum(retjson,"netid",G.netid); + if ( bids != 0 ) + free(bids); + if ( asks != 0 ) + free(asks); + return(jprint(retjson,1)); +} + +double LP_fomoprice(char *base,char *rel,double *relvolumep) +{ + char *retstr; cJSON *retjson,*asks,*item; int32_t i,numasks; double maxvol=0.,relvolume,biggest,price,fomoprice = 0.; + relvolume = *relvolumep; + if ( (retstr= LP_orderbook(base,rel,0)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (asks= jarray(&numasks,retjson,"asks")) != 0 && numasks > 0 ) + { + for (i=0; i maxvol ) + { + maxvol = biggest; + fomoprice = price; + } + printf("fomoprice (%.8f) i.%d %.8f vol %.8f [max %.8f @ %.8f]\n",relvolume,i,price,biggest,maxvol,fomoprice); + } + } + free_json(retjson); + } + free(retstr); + } + if ( maxvol > 0. && fomoprice > 0. ) + { + if ( maxvol < relvolume ) + relvolume = maxvol * 0.98; + fomoprice /= 0.95; + } else fomoprice = 0.; + *relvolumep = relvolume; + return(fomoprice); +} + +int64_t LP_KMDvalue(struct iguana_info *coin,int64_t balance) +{ + double price = 0.; int64_t KMDvalue=0; + if ( balance != 0 ) + { + if ( strcmp(coin->symbol,"KMD") == 0 ) + KMDvalue = balance; + else + { + if ( (price= LP_price(1,coin->symbol,"KMD")) > SMALLVAL ) + KMDvalue = price * balance; + } + } + return(KMDvalue); +} + +int64_t LP_kmdvalue(char *symbol,int64_t satoshis) +{ + struct iguana_info *coin; int64_t kmdvalue = 0; + if ( (coin= LP_coinfind(symbol)) != 0 ) + kmdvalue = LP_KMDvalue(coin,satoshis); + if ( kmdvalue == 0 ) + kmdvalue = satoshis; + return(kmdvalue); +} + +void LP_priceupdate(char *base,char *rel,double price,double avebid,double aveask,double highbid,double lowask,double PAXPRICES[32]) +{ + LP_priceinfoupdate(base,rel,price); +} + +void LP_pricefname(char *fname,char *base,char *rel) +{ + sprintf(fname,"%s/PRICES/%s_%s",GLOBAL_DBDIR,base,rel); + OS_compatible_path(fname); +} + +void LP_priceitemadd(cJSON *retarray,uint32_t timestamp,double avebid,double aveask,double highbid,double lowask) +{ + cJSON *item = cJSON_CreateArray(); + jaddinum(item,timestamp); + jaddinum(item,avebid); + jaddinum(item,aveask); + jaddinum(item,highbid); + jaddinum(item,lowask); + jaddi(retarray,item); +} + +cJSON *LP_pricearray(char *base,char *rel,uint32_t firsttime,uint32_t lasttime,int32_t timescale) +{ + cJSON *retarray; char askfname[1024],bidfname[1024]; int64_t bidprice64,askprice64; uint32_t bidnow,asknow,bidi,aski,lastbidi,lastaski; int32_t numbids,numasks; double bidemit,askemit,bidsum,asksum,bid,ask,highbid,lowbid,highask,lowask,bidemit2,askemit2; FILE *askfp=0,*bidfp=0; + if ( timescale <= 0 ) + timescale = 60; + if ( lasttime == 0 ) + lasttime = (uint32_t)-1; + LP_pricefname(askfname,base,rel); + LP_pricefname(bidfname,rel,base); + retarray = cJSON_CreateArray(); + lastbidi = lastaski = 0; + numbids = numasks = 0; + bidsum = asksum = askemit = bidemit = highbid = lowbid = highask = lowask = 0.; + if ( (bidfp= fopen(bidfname,"rb")) != 0 && (askfp= fopen(askfname,"rb")) != 0 ) + { + while ( bidfp != 0 || askfp != 0 ) + { + bidi = aski = 0; + bidemit = askemit = bidemit2 = askemit2 = 0.; + if ( bidfp != 0 && fread(&bidnow,1,sizeof(bidnow),bidfp) == sizeof(bidnow) && fread(&bidprice64,1,sizeof(bidprice64),bidfp) == sizeof(bidprice64) ) + { + //printf("bidnow.%u %.8f\n",bidnow,dstr(bidprice64)); + if ( bidnow != 0 && bidprice64 != 0 && bidnow >= firsttime && bidnow <= lasttime ) + { + bidi = bidnow / timescale; + if ( bidi != lastbidi ) + { + if ( bidsum != 0. && numbids != 0 ) + { + bidemit = bidsum / numbids; + bidemit2 = highbid; + } + bidsum = highbid = lowbid = 0.; + numbids = 0; + } + if ( (bid= 1. / dstr(bidprice64)) != 0. ) + { + if ( bid > highbid ) + highbid = bid; + if ( lowbid == 0. || bid < lowbid ) + lowbid = bid; + bidsum += bid; + numbids++; + //printf("bidi.%u num.%d %.8f [%.8f %.8f]\n",bidi,numbids,bid,lowbid,highbid); + } + } + } else fclose(bidfp), bidfp = 0; + if ( askfp != 0 && fread(&asknow,1,sizeof(asknow),askfp) == sizeof(asknow) && fread(&askprice64,1,sizeof(askprice64),askfp) == sizeof(askprice64) ) + { + //printf("asknow.%u %.8f\n",asknow,dstr(askprice64)); + if ( asknow != 0 && askprice64 != 0 && asknow >= firsttime && asknow <= lasttime ) + { + aski = asknow / timescale; + if ( aski != lastaski ) + { + if ( asksum != 0. && numasks != 0 ) + { + askemit = asksum / numasks; + askemit2 = lowask; + } + asksum = highask = lowask = 0.; + numasks = 0; + } + if ( (ask= dstr(askprice64)) != 0. ) + { + if ( ask > highask ) + highask = ask; + if ( lowask == 0. || ask < lowask ) + lowask = ask; + asksum += ask; + numasks++; + //printf("aski.%u num.%d %.8f [%.8f %.8f]\n",aski,numasks,ask,lowask,highask); + } + } + } else fclose(askfp), askfp = 0; + if ( bidemit != 0. || askemit != 0. ) + { + if ( bidemit != 0. && askemit != 0. && lastbidi == lastaski ) + { + LP_priceitemadd(retarray,lastbidi * timescale,bidemit,askemit,bidemit2,askemit2); + highbid = lowbid = highask = lowask = 0.; + } + else + { + if ( bidemit != 0. ) + { + printf("bidonly %.8f %.8f\n",bidemit,highbid); + LP_priceitemadd(retarray,lastbidi * timescale,bidemit,0.,bidemit2,0.); + highbid = lowbid = 0.; + } + if ( askemit != 0. ) + { + printf("askonly %.8f %.8f\n",askemit,lowask); + LP_priceitemadd(retarray,lastaski * timescale,0.,askemit,0.,askemit2); + highask = lowask = 0.; + } + } + } + if ( bidi != 0 ) + lastbidi = bidi; + if ( aski != 0 ) + lastaski = aski; + } + } else printf("couldnt open either %s %p or %s %p\n",bidfname,bidfp,askfname,askfp); + if ( bidfp != 0 ) + fclose(bidfp); + if ( askfp != 0 ) + fclose(askfp); + return(retarray); +} + +void LP_pricefeedupdate(bits256 pubkey,char *base,char *rel,double price,char *utxocoin,int32_t numrelutxos,int64_t balance,int64_t minutxo,int64_t maxutxo,int64_t unconfcredits) +{ + struct LP_priceinfo *basepp,*relpp; uint32_t now; int64_t price64; struct LP_pubkey_info *pubp; char str[65],fname[512]; FILE *fp; +//printf("check PRICEFEED UPDATE.(%s/%s) %.8f %s balance %.8f min %.8f max %.8f\n",base,rel,price,bits256_str(str,pubkey),dstr(balance),dstr(minutxo),dstr(maxutxo)); + if ( LP_pricevalid(price) > 0 && (basepp= LP_priceinfofind(base)) != 0 && (relpp= LP_priceinfofind(rel)) != 0 ) + { + //if ( (fp= basepp->fps[relpp->ind]) == 0 ) + { + LP_pricefname(fname,base,rel); + fp = OS_appendfile(fname); //basepp->fps[relpp->ind] = + } + if ( fp != 0 ) + { + now = (uint32_t)time(NULL); + price64 = price * SATOSHIDEN; + fwrite(&now,1,sizeof(now),fp); + fwrite(&price64,1,sizeof(price64),fp); + fclose(fp); + } + //if ( (fp= relpp->fps[basepp->ind]) == 0 ) + { + sprintf(fname,"%s/PRICES/%s_%s",GLOBAL_DBDIR,rel,base); + fp = OS_appendfile(fname); //relpp->fps[basepp->ind] = + } + if ( fp != 0 ) + { + now = (uint32_t)time(NULL); + price64 = (1. / price) * SATOSHIDEN; + fwrite(&now,1,sizeof(now),fp); + fwrite(&price64,1,sizeof(price64),fp); + fclose(fp); + } + if ( (pubp= LP_pubkeyadd(pubkey)) != 0 ) + { + if ( (LP_rand() % 1000) == 0 ) + printf("PRICEFEED UPDATE.(%-6s/%6s) %12.8f %s %12.8f\n",base,rel,price,bits256_str(str,pubkey),1./price); + if ( unconfcredits > pubp->unconfcredits ) + pubp->unconfcredits = unconfcredits; + pubp->timestamp = (uint32_t)time(NULL); + LP_pubkey_update(pubp,basepp->ind,relpp->ind,price,balance,utxocoin,numrelutxos,minutxo,maxutxo); + //pubp->depthinfo[basepp->ind][relpp->ind] = LP_depthinfo_compact(); + //if ( fabs(pubp->matrix[basepp->ind][relpp->ind] - price) > SMALLVAL ) + { + //pubp->matrix[basepp->ind][relpp->ind] = price; + //pubp->timestamps[basepp->ind][relpp->ind] = pubp->timestamp; + dxblend(&basepp->relvals[relpp->ind],price,0.9); + dxblend(&relpp->relvals[basepp->ind],1. / price,0.9); + } + } else printf("error finding pubkey entry %s, ok if rare\n",bits256_str(str,pubkey)); + } + //else if ( (rand() % 100) == 0 ) + // printf("error finding %s/%s %.8f\n",base,rel,price); +} + +double LP_CMCbtcprice(double *price_usdp,char *symbol) +{ + char *retstr; cJSON *ticker,*item; double price_btc = 0.; + *price_usdp = 0.; + if ( (retstr= cmc_ticker(symbol)) != 0 ) + { + if ( (ticker= cJSON_Parse(retstr)) != 0 ) + { + item = jitem(ticker,0); + price_btc = jdouble(item,"price_btc"); + *price_usdp = jdouble(item,"price_usd"); +//printf("%.8f item.(%s)\n",price_btc,jprint(item,0)); + free_json(ticker); + } + free(retstr); + } + return(price_btc); +} + +cJSON *LP_fundvalue(cJSON *argjson) +{ + cJSON *holdings,*item,*newitem,*array,*retjson; int32_t i,iter,n,missing=0; double usdprice,divisor,btcprice,balance,btcsum,KMDholdings,numKMD; struct iguana_info *coin; char *symbol,*coinaddr; int64_t fundvalue,KMDvalue = 0; + fundvalue = 0; + KMDholdings = btcsum = 0.; + array = cJSON_CreateArray(); + for (iter=0; iter<2; iter++) + { + if ( iter == 0 ) + holdings = jarray(&n,argjson,"holdings"); + else + { + if ( (coinaddr= jstr(argjson,"address")) != 0 ) + { + holdings = LP_balances(coinaddr); + n = cJSON_GetArraySize(holdings); + } else break; + } + if ( holdings != 0 ) + { + for (i=0; i SMALLVAL ) + { + newitem = cJSON_CreateObject(); + jaddstr(newitem,"coin",symbol); + jaddnum(newitem,"balance",balance); + if ( (coin= LP_coinfind(symbol)) != 0 && (KMDvalue= LP_KMDvalue(coin,SATOSHIDEN * balance)) > 0 ) + { + jaddnum(newitem,"KMD",dstr(KMDvalue)); + fundvalue += KMDvalue; + if ( strcmp(symbol,"KMD") == 0 ) + KMDholdings += dstr(KMDvalue); + } + else if ( iter == 0 && (btcprice= LP_CMCbtcprice(&usdprice,symbol)) > SMALLVAL ) + { + btcsum += btcprice * balance; + jaddnum(newitem,"BTC",btcprice * balance); + } + else jaddstr(newitem,"error","no price source"); + jaddi(array,newitem); + } else missing++; + } + } + } + retjson = cJSON_CreateObject(); + jaddstr(retjson,"result","success"); + jaddnum(retjson,"missing",missing); + jadd(retjson,"holdings",array); + btcprice = LP_CMCbtcprice(&usdprice,"komodo"); + divisor = jdouble(argjson,"divisor"); + jaddnum(retjson,"KMDholdings",KMDholdings); + if ( btcsum != 0 ) + { + if ( btcprice > SMALLVAL ) + { + numKMD = (btcsum / btcprice); + fundvalue += numKMD * SATOSHIDEN; + jaddnum(retjson,"KMD_BTC",btcprice); + jaddnum(retjson,"btcsum",btcsum); + numKMD += KMDholdings; + jaddnum(retjson,"btc2kmd",numKMD); + if ( divisor != 0 ) + { + jaddnum(retjson,"NAV_KMD",numKMD/divisor); + jaddnum(retjson,"NAV_BTC",(btcsum + (KMDholdings * btcprice))/divisor); + jaddnum(retjson,"NAV_USD",(usdprice * numKMD)/divisor); + } + } + } + jaddnum(retjson,"fundvalue",dstr(fundvalue)); + if ( divisor != 0 ) + { + jaddnum(retjson,"divisor",divisor); + numKMD = dstr(fundvalue); + jaddnum(retjson,"assetNAV_KMD",numKMD/divisor); + jaddnum(retjson,"assetNAV_BTC",(btcprice * numKMD)/divisor); + jaddnum(retjson,"assetNAV_USD",(usdprice * numKMD)/divisor); + } + return(retjson); +} + diff --git a/iguana/exchanges/LP_nativeDEX.c b/iguana/exchanges/LP_nativeDEX.c index 6765de92b..5ddc56f45 100644 --- a/iguana/exchanges/LP_nativeDEX.c +++ b/iguana/exchanges/LP_nativeDEX.c @@ -120,7 +120,7 @@ struct LP_globals uint64_t LP_skipstatus[10000]; uint16_t netid; uint8_t LP_myrmd160[20],LP_pubsecp[33]; - uint32_t LP_sessionid,counter; + uint32_t LP_sessionid,counter,mpnet; int32_t LP_IAMLP,LP_pendingswaps,USERPASS_COUNTER,LP_numprivkeys,initializing,waiting,LP_numskips; char seednode[64],USERPASS[65],USERPASS_WIFSTR[64],LP_myrmd160str[41],gui[65],LP_NXTaddr[64]; struct LP_privkey LP_privkeys[100]; @@ -172,6 +172,7 @@ char *blocktrail_listtransactions(char *symbol,char *coinaddr,int32_t num,int32_ #include "LP_mmjson.c" #include "LP_socket.c" +#include "LP_mpnet.c" #include "LP_secp.c" #include "LP_bitcoin.c" #include "LP_coins.c" @@ -480,6 +481,8 @@ int32_t LP_nanomsg_recvs(void *ctx) nonz += LP_sock_check("PULL",ctx,origipaddr,-1,LP_mypullsock,"127.0.0.1",1); } portable_mutex_unlock(&LP_nanorecvsmutex); + if ( G.mpnet != 0 ) + LP_mpnet_check(ctx,origipaddr,LP_mypubsock); return(nonz); } diff --git a/iguana/exchanges/LP_ordermatch.c b/iguana/exchanges/LP_ordermatch.c index 9163604bd..6ce1b93d9 100644 --- a/iguana/exchanges/LP_ordermatch.c +++ b/iguana/exchanges/LP_ordermatch.c @@ -550,7 +550,9 @@ int32_t LP_connectstartbob(void *ctx,int32_t pubsock,char *base,char *rel,double } if ( qp->mpnet != 0 && qp->fill != 0 && qp->gtc != 0 ) { - // send to mpnet + char *msg = jprint(reqjson,0); + LP_mpnet_send(msg); + free(msg); } free_json(reqjson); retval = 0; @@ -650,7 +652,10 @@ char *LP_trade(void *ctx,char *myipaddr,int32_t mypubsock,struct LP_quoteinfo *q } if ( qp->mpnet != 0 && qp->gtc != 0 && qp->fill != 0 ) { - // send to mpnet + cJSON *reqjson = LP_quotejson(qp); + char *msg = jprint(reqjson,1); + LP_mpnet_send(msg); + free(msg); } char str[65]; printf("LP_trade mpnet.%d fill.%d gtc.%d %s/%s %.8f vol %.8f dest.(%s) maxprice %.8f etomicdest.(%s) uuid.%s fill.%d gtc.%d\n",qp->mpnet,qp->fill,qp->gtc,qp->srccoin,qp->destcoin,dstr(qp->satoshis),dstr(qp->destsatoshis),bits256_str(str,LP_Alicedestpubkey),maxprice,qp->etomicdest,qp->uuidstr,qp->fill,qp->gtc); return(LP_recent_swaps(0,uuidstr)); @@ -1197,7 +1202,9 @@ printf("bob %s received REQUEST.(%s) mpnet.%d fill.%d gtc.%d\n",bits256_str(str, } if ( qp->mpnet != 0 && qp->gtc != 0 && qp->fill != 0 ) { - // send to mpnet + char *msg = jprint(reqjson,0); + LP_mpnet_send(msg); + free(msg); } free_json(reqjson); //printf("Send RESERVED id.%llu\n",(long long)qp->aliceid); @@ -1795,7 +1802,7 @@ char *LP_autobuy(void *ctx,int32_t fomoflag,char *myipaddr,int32_t mypubsock,cha } } int32_t changed; - Q.mpnet = 0; + Q.mpnet = G.mpnet; Q.fill = fillflag; Q.gtc = gtcflag; LP_mypriceset(0,&changed,rel,base,1. / maxprice); diff --git a/iguana/exchanges/LP_signatures.c b/iguana/exchanges/LP_signatures.c index 15fb2b694..079dd43fe 100644 --- a/iguana/exchanges/LP_signatures.c +++ b/iguana/exchanges/LP_signatures.c @@ -739,9 +739,7 @@ void LP_query(void *ctx,char *myipaddr,int32_t mypubsock,char *method,struct LP_ } } if ( qp->mpnet != 0 && qp->gtc != 0 && qp->fill != 0 ) - { - // send to mpnet - } + LP_mpnet_send(msg); free(msg); }