You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

537 lines
18 KiB

8 years ago
/******************************************************************************
* Copyright © 2014-2017 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
//
8 years ago
struct LP_orderbookentry { bits256 txid,txid2,pubkey; double price; uint64_t basesatoshis; int32_t vout,vout2; };
8 years ago
8 years ago
#define LP_MAXPRICEINFOS 64
8 years ago
struct LP_priceinfo
{
char symbol[16];
uint64_t coinbits;
int32_t ind,pad;
double diagval;
8 years ago
double relvals[LP_MAXPRICEINFOS];
double myprices[LP_MAXPRICEINFOS];
} LP_priceinfos[LP_MAXPRICEINFOS];
8 years ago
int32_t LP_numpriceinfos;
8 years ago
8 years ago
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;
8 years ago
struct LP_pubkeyinfo
{
UT_hash_handle hh;
bits256 pubkey;
double matrix[LP_MAXPRICEINFOS][LP_MAXPRICEINFOS];
} *LP_pubkeyinfos;
8 years ago
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 ( 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);
}
8 years ago
struct LP_pubkeyinfo *LP_pubkeyfind(bits256 pubkey)
{
struct LP_pubkeyinfo *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_pubkeyinfo *LP_pubkeyadd(bits256 pubkey)
{
struct LP_pubkeyinfo *pubp=0;
if ( (pubp= LP_pubkeyfind(pubkey)) == 0 )
{
portable_mutex_lock(&LP_pubkeymutex);
8 years ago
pubp = calloc(1,sizeof(*pubp));
pubp->pubkey = pubkey;
8 years ago
HASH_ADD_KEYPTR(hh,LP_pubkeyinfos,&pubp->pubkey,sizeof(pubp->pubkey),pubp);
8 years ago
portable_mutex_unlock(&LP_pubkeymutex);
if ( (pubp= LP_pubkeyfind(pubkey)) == 0 )
printf("pubkeyadd find error after add\n");
}
return(pubp);
}
8 years ago
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 != 0 )
{
8 years ago
printf("LP_pricecache: null ptr->price? ");
8 years ago
ptr->price = (double)ptr->Q.destsatoshis / ptr->Q.satoshis;
}
//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.);
}
8 years ago
struct LP_priceinfo *LP_priceinfofind(char *symbol)
{
int32_t i; struct LP_priceinfo *pp; uint64_t coinbits;
if ( LP_numpriceinfos > 0 )
{
coinbits = stringbits(symbol);
pp = LP_priceinfos;
for (i=0; i<LP_numpriceinfos; i++,pp++)
if ( pp->coinbits == coinbits )
return(pp);
}
return(0);
}
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);
}
}
void LP_priceinfoupdate(char *base,char *rel,double price)
{
struct LP_priceinfo *basepp,*relpp;
if ( (basepp= LP_priceinfofind(base)) != 0 && (relpp= LP_priceinfofind(rel)) != 0 )
{
8 years ago
//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;
8 years ago
}
}
double LP_myprice(double *bidp,double *askp,char *base,char *rel)
{
8 years ago
struct LP_priceinfo *basepp,*relpp; double val;
*bidp = *askp = 0.;
8 years ago
if ( (basepp= LP_priceinfofind(base)) != 0 && (relpp= LP_priceinfofind(rel)) != 0 )
{
8 years ago
if ( (*askp= basepp->myprices[relpp->ind]) > SMALLVAL )
8 years ago
{
8 years ago
if ( (val= relpp->myprices[basepp->ind]) > SMALLVAL )
8 years ago
{
*bidp = 1. / val;
8 years ago
} else *bidp = *askp * 0.99;
return((*askp + *bidp) * 0.5);
}
else
{
8 years ago
if ( (val= relpp->myprices[basepp->ind]) > SMALLVAL )
8 years ago
{
*bidp = 1. / val;
*askp = *bidp / 0.99;
8 years ago
}
}
}
return(0.);
8 years ago
}
int32_t LP_mypriceset(char *base,char *rel,double price)
{
struct LP_priceinfo *basepp,*relpp;
8 years ago
if ( price > SMALLVAL && (basepp= LP_priceinfofind(base)) != 0 && (relpp= LP_priceinfofind(rel)) != 0 )
8 years ago
{
8 years ago
basepp->myprices[relpp->ind] = price; // ask
relpp->myprices[basepp->ind] = (1. / price); // bid
8 years ago
return(0);
} else return(-1);
}
double LP_price(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[relind]) == 0. )
price = basepp->relvals[relind];
}
return(price);
}
cJSON *LP_priceinfomatrix(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)
{
8 years ago
if ( ptr->timestamp < now-3600*2 || ptr->price == 0. )
8 years ago
continue;
LP_priceinfoupdate(ptr->Q.srccoin,ptr->Q.destcoin,ptr->price);
}
pp = LP_priceinfos;
total = m = 0;
for (i=0; i<LP_numpriceinfos; i++,pp++)
{
pp->diagval = sum = n = 0;
for (j=0; j<LP_numpriceinfos; j++)
{
if ( usemyprices == 0 || (val= pp->myprices[j]) == 0. )
val = pp->relvals[j];
8 years ago
if ( val > SMALLVAL )
8 years ago
{
sum += val;
n++;
}
}
if ( n > 0 )
{
pp->diagval = sum / n;
total += pp->diagval, m++;
}
}
if ( m > 0 )
{
pp = LP_priceinfos;
for (i=0; i<LP_numpriceinfos; i++,pp++)
{
8 years ago
if ( pp->diagval > SMALLVAL )
{
pp->diagval /= total;
jaddnum(vectorjson,pp->symbol,pp->diagval);
}
8 years ago
}
}
return(vectorjson);
}
struct LP_priceinfo *LP_priceinfoadd(char *symbol)
{
8 years ago
struct LP_priceinfo *pp; cJSON *retjson;
8 years ago
if ( symbol == 0 )
return(0);
8 years ago
if ( LP_numpriceinfos >= sizeof(LP_priceinfos)/sizeof(*LP_priceinfos) )
{
printf("cant add any more priceinfos\n");
return(0);
}
8 years ago
pp = &LP_priceinfos[LP_numpriceinfos];
memset(pp,0,sizeof(*pp));
safecopy(pp->symbol,symbol,sizeof(pp->symbol));
pp->coinbits = stringbits(symbol);
8 years ago
pp->ind = LP_numpriceinfos++;
8 years ago
LP_numpriceinfos++;
if ( (retjson= LP_priceinfomatrix(0)) != 0 )
free_json(retjson);
return(pp);
}
8 years ago
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 ( (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");
}
8 years ago
ptr->Q = *qp;
ptr->timestamp = (uint32_t)time(NULL);
8 years ago
if ( price != ptr->price )
{
8 years ago
ptr->price = price;
LP_priceinfoupdate(base,rel,price);
8 years ago
printf("updated %s/v%d %s/%s %llu price %.8f\n",bits256_str(str,txid),vout,base,rel,(long long)qp->satoshis,price);
8 years ago
} else ptr->price = price;
8 years ago
return(ptr);
}
static int _cmp_orderbook(const void *a,const void *b)
{
8 years ago
int32_t retval = 0;
8 years ago
#define ptr_a (*(struct LP_orderbookentry **)a)->price
#define ptr_b (*(struct LP_orderbookentry **)b)->price
8 years ago
if ( ptr_b > ptr_a )
8 years ago
retval = 1;
8 years ago
else if ( ptr_b < ptr_a )
8 years ago
retval = -1;
8 years ago
else
8 years ago
{
#undef ptr_a
#undef ptr_b
8 years ago
#define ptr_a ((struct LP_orderbookentry *)a)->basesatoshis
#define ptr_b ((struct LP_orderbookentry *)b)->basesatoshis
8 years ago
if ( ptr_b > ptr_a )
return(1);
else if ( ptr_b < ptr_a )
return(-1);
8 years ago
}
// printf("%.8f vs %.8f -> %d\n",ptr_a,ptr_b,retval);
8 years ago
return(retval);
8 years ago
#undef ptr_a
#undef ptr_b
}
static int _cmp_orderbookrev(const void *a,const void *b)
{
return(-_cmp_orderbook(a,b));
}
8 years ago
cJSON *LP_orderbookjson(struct LP_orderbookentry *op)
8 years ago
{
8 years ago
cJSON *item = cJSON_CreateObject();
if ( op->price > SMALLVAL )
8 years ago
{
8 years ago
jaddnum(item,"price",op->price );
jaddnum(item,"volume",dstr(op->basesatoshis));
jaddbits256(item,"txid",op->txid);
jaddnum(item,"vout",op->vout);
8 years ago
jaddbits256(item,"pubkey",op->pubkey);
8 years ago
}
return(item);
}
8 years ago
struct LP_orderbookentry *LP_orderbookentry(char *base,char *rel,bits256 txid,int32_t vout,bits256 txid2,int32_t vout2,double price,uint64_t basesatoshis,bits256 pubkey)
8 years ago
{
struct LP_orderbookentry *op;
if ( (op= calloc(1,sizeof(*op))) != 0 )
{
op->txid = txid;
op->vout = vout;
8 years ago
op->txid2 = txid2;
op->vout2 = vout2;
8 years ago
op->price = price;
op->basesatoshis = basesatoshis;
8 years ago
op->pubkey = pubkey;
8 years ago
}
return(op);
}
int32_t LP_orderbookfind(struct LP_orderbookentry **array,int32_t num,bits256 txid,int32_t vout)
{
int32_t i;
for (i=0; i<num; i++)
8 years ago
if ( (array[i]->vout == vout && bits256_cmp(array[i]->txid,txid) == 0) || (array[i]->vout2 == vout && bits256_cmp(array[i]->txid2,txid) == 0) )
8 years ago
return(i);
return(-1);
}
8 years ago
int32_t LP_orderbook_utxoentries(uint32_t now,char *base,char *rel,double price,struct LP_orderbookentry *(**arrayp),int32_t num,int32_t cachednum,bits256 pubkey)
8 years ago
{
struct LP_utxoinfo *utxo,*tmp; struct LP_peerinfo *peer,*ptmp; char *retstr; cJSON *retjson; struct LP_orderbookentry *op; uint64_t basesatoshis;
HASH_ITER(hh,LP_utxoinfos[1],utxo,tmp)
{
8 years ago
//char str[65],str2[65]; printf("check utxo.%s/v%d from %s\n",bits256_str(str,utxo->payment.txid),utxo->payment.vout,bits256_str(str2,utxo->pubkey));
8 years ago
if ( bits256_cmp(pubkey,utxo->pubkey) == 0 && strcmp(base,utxo->coin) == 0 && LP_isavailable(utxo) > 0 )
8 years ago
{
8 years ago
if ( LP_orderbookfind(*arrayp,cachednum,utxo->payment.txid,utxo->payment.vout) < 0 )
8 years ago
{
8 years ago
//char str[65]; printf("found utxo not in orderbook %s/v%d\n",bits256_str(str,utxo->payment.txid),utxo->payment.vout);
8 years ago
*arrayp = realloc(*arrayp,sizeof(*(*arrayp)) * (num+1));
8 years ago
basesatoshis = utxo->payment.value;
8 years ago
if ( (op= LP_orderbookentry(base,rel,utxo->payment.txid,utxo->payment.vout,utxo->deposit.txid,utxo->deposit.vout,price,basesatoshis,pubkey)) != 0 )
8 years ago
(*arrayp)[num++] = op;
8 years ago
if ( bits256_cmp(utxo->pubkey,LP_mypubkey) == 0 && utxo->T.lasttime == 0 )
{
HASH_ITER(hh,LP_peerinfos,peer,ptmp)
{
if ( (retstr= issue_LP_notifyutxo(peer->ipaddr,peer->port,utxo)) != 0 )
{
if ( (retjson= cJSON_Parse(retstr)) != 0 )
{
if ( jobj(retjson,"error") == 0 )
utxo->T.lasttime = (uint32_t)time(NULL);
free_json(retjson);
}
free(retstr);
}
if ( utxo->T.lasttime != 0 )
break;
}
}
}
}
}
return(num);
}
8 years ago
char *LP_orderbook(char *base,char *rel)
{
8 years ago
uint32_t now,i; double price; struct LP_priceinfo *basepp=0,*relpp=0; struct LP_pubkeyinfo *pubp,*ptmp; struct LP_cacheinfo *ptr,*tmp; struct LP_orderbookentry *op,**bids = 0,**asks = 0; cJSON *retjson,*array; int32_t numbids=0,numasks=0,cachenumbids,cachenumasks,baseid,relid;
8 years ago
if ( (basepp= LP_priceinfofind(base)) == 0 || (relpp= LP_priceinfofind(rel)) == 0 )
return(clonestr("{\"error\":\"base or rel not added\"}"));
baseid = basepp->ind;
relid = relpp->ind;
8 years ago
now = (uint32_t)time(NULL);
HASH_ITER(hh,LP_cacheinfos,ptr,tmp)
{
8 years ago
if ( ptr->timestamp < now-3600*2 || ptr->price == 0. )
8 years ago
continue;
if ( strcmp(ptr->Q.srccoin,base) == 0 && strcmp(ptr->Q.destcoin,rel) == 0 )
{
asks = realloc(asks,sizeof(*asks) * (numasks+1));
8 years ago
if ( (op= LP_orderbookentry(base,rel,ptr->Q.txid,ptr->Q.vout,ptr->Q.txid2,ptr->Q.vout2,ptr->price,ptr->Q.satoshis,ptr->Q.srchash)) != 0 )
8 years ago
asks[numasks++] = op;
8 years ago
}
else if ( strcmp(ptr->Q.srccoin,rel) == 0 && strcmp(ptr->Q.destcoin,base) == 0 )
{
bids = realloc(bids,sizeof(*bids) * (numbids+1));
8 years ago
if ( (op= LP_orderbookentry(base,rel,ptr->Q.txid,ptr->Q.vout,ptr->Q.txid2,ptr->Q.vout2,1./ptr->price,ptr->Q.satoshis,ptr->Q.srchash)) != 0 )
8 years ago
bids[numbids++] = op;
8 years ago
}
}
8 years ago
cachenumbids = numbids, cachenumasks = numasks;
8 years ago
//printf("start cache.(%d %d) numbids.%d numasks.%d\n",cachenumbids,cachenumasks,numbids,numasks);
8 years ago
HASH_ITER(hh,LP_pubkeyinfos,pubp,ptmp)
{
8 years ago
//char str[65]; printf("pubkey.(%s)\n",bits256_str(str,pubp->pubkey));
if ( (price= pubp->matrix[baseid][relid]) > SMALLVAL )
numasks = LP_orderbook_utxoentries(now,base,rel,price,&asks,numasks,cachenumasks,pubp->pubkey);
if ( (price= pubp->matrix[relid][baseid]) > SMALLVAL )
numbids = LP_orderbook_utxoentries(now,base,rel,1./price,&bids,numbids,cachenumbids,pubp->pubkey);
//printf("cache.(%d %d) numbids.%d numasks.%d\n",cachenumbids,cachenumasks,numbids,numasks);
8 years ago
}
8 years ago
retjson = cJSON_CreateObject();
array = cJSON_CreateArray();
if ( numbids > 1 )
8 years ago
qsort(bids,numbids,sizeof(*bids),_cmp_orderbookrev);
8 years ago
if ( numasks > 1 )
8 years ago
{
for (i=0; i<numasks; i++)
printf("%.8f ",asks[i]->price);
printf(" -> ");
8 years ago
qsort(asks,numasks,sizeof(*asks),_cmp_orderbookrev);
8 years ago
for (i=0; i<numasks; i++)
printf("%.8f ",asks[i]->price);
printf("sorted asks.%d\n",numasks);
}
8 years ago
for (i=0; i<numbids; i++)
{
jaddi(array,LP_orderbookjson(bids[i]));
free(bids[i]);
bids[i] = 0;
}
jadd(retjson,"bids",array);
jaddnum(retjson,"numbids",numbids);
array = cJSON_CreateArray();
8 years ago
for (i=0; i<numasks; i++)
8 years ago
{
jaddi(array,LP_orderbookjson(asks[i]));
free(asks[i]);
asks[i] = 0;
}
8 years ago
jadd(retjson,"asks",array);
8 years ago
jaddnum(retjson,"numasks",numasks);
8 years ago
jaddstr(retjson,"base",base);
jaddstr(retjson,"rel",rel);
jaddnum(retjson,"timestamp",now);
8 years ago
if ( bids != 0 )
free(bids);
if ( asks != 0 )
free(asks);
8 years ago
return(jprint(retjson,1));
}
8 years ago
8 years ago
char *LP_pricestr(char *base,char *rel,double origprice)
8 years ago
{
8 years ago
cJSON *retjson; double price = 0.;
8 years ago
if ( base != 0 && base[0] != 0 && rel != 0 && rel[0] != 0 )
8 years ago
{
8 years ago
price = LP_price(base,rel);
8 years ago
if ( origprice > SMALLVAL && origprice < price )
price = origprice;
}
if ( price > SMALLVAL )
8 years ago
{
retjson = cJSON_CreateObject();
jaddstr(retjson,"result","success");
8 years ago
jaddstr(retjson,"method","postprice");
jaddbits256(retjson,"pubkey",LP_mypubkey);
8 years ago
jaddstr(retjson,"base",base);
jaddstr(retjson,"rel",rel);
8 years ago
jaddnum(retjson,"price",price);
8 years ago
jadd(retjson,"theoretical",LP_priceinfomatrix(0));
jadd(retjson,"quotes",LP_priceinfomatrix(1));
8 years ago
return(jprint(retjson,1));
} else return(clonestr("{\"error\":\"cant find baserel pair\"}"));
}
8 years ago
void LP_priceupdate(char *base,char *rel,double price,double avebid,double aveask,double highbid,double lowask,double PAXPRICES[32])
8 years ago
{
8 years ago
LP_priceinfoupdate(base,rel,price);
8 years ago
}
8 years ago
void LP_pricefeedupdate(bits256 pubkey,char *base,char *rel,double price)
{
struct LP_priceinfo *basepp,*relpp; struct LP_pubkeyinfo *pubp; char str[65];
if ( price > SMALLVAL && (basepp= LP_priceinfofind(base)) != 0 && (relpp= LP_priceinfofind(rel)) != 0 )
{
8 years ago
printf("PRICEFEED UPDATE.(%s/%s) %.8f %s\n",base,rel,price,bits256_str(str,pubkey));
8 years ago
if ( (pubp= LP_pubkeyadd(pubkey)) != 0 )
pubp->matrix[basepp->ind][relpp->ind] = price;
else printf("error creating pubkey entry\n");
} else printf("error finding %s/%s %.8f\n",base,rel,price);
}
8 years ago