|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* 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_RTmetrics.c
|
|
|
|
// marketmaker
|
|
|
|
//
|
|
|
|
|
|
|
|
struct LP_metricinfo
|
|
|
|
{
|
|
|
|
double metric;
|
|
|
|
double price,balance,minvol;
|
|
|
|
bits256 pubkey;
|
|
|
|
double maxvol;
|
|
|
|
int32_t ind,numutxos,age,pendingswaps;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define LP_NUMRT 1024
|
|
|
|
struct LP_RTmetrics_pendings
|
|
|
|
{
|
|
|
|
char refbase[128],refrel[128];
|
|
|
|
int64_t pending_kmdvalue[LP_NUMRT];
|
|
|
|
int32_t numswaps,numavoidtxids,numwhitelist,numblacklist,numpendings,pending_swaps[LP_NUMRT];
|
|
|
|
bits256 avoidtxids[8192],whitelist[LP_NUMRT],blacklist[LP_NUMRT],pending_pubkeys[LP_NUMRT];
|
|
|
|
} LP_RTmetrics;
|
|
|
|
|
|
|
|
int32_t LP_bits256_find(bits256 *list,int32_t num,bits256 val)
|
|
|
|
{
|
|
|
|
int32_t i;
|
|
|
|
if ( bits256_nonz(val) != 0 )
|
|
|
|
{
|
|
|
|
for (i=0; i<num; i++)
|
|
|
|
if ( bits256_cmp(list[i],val) == 0 )
|
|
|
|
return(i);
|
|
|
|
}
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_bits256_add(char *debugstr,bits256 *list,int32_t *nump,int32_t maxnum,bits256 val)
|
|
|
|
{
|
|
|
|
if ( bits256_nonz(val) != 0 && *nump < maxnum )
|
|
|
|
{
|
|
|
|
if ( LP_bits256_find(list,*nump,val) < 0 )
|
|
|
|
list[(*nump)++] = val;
|
|
|
|
return(*nump);
|
|
|
|
} else printf("%s[%d] overflow\n",debugstr,*nump);
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_RTmetrics_avoidadd(bits256 txid)
|
|
|
|
{
|
|
|
|
return(LP_bits256_add("LP_RTmetrics_avoidadd avoidtxids",LP_RTmetrics.avoidtxids,&LP_RTmetrics.numavoidtxids,(int32_t)(sizeof(LP_RTmetrics.avoidtxids)/sizeof(*LP_RTmetrics.avoidtxids)),txid));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_RTmetrics_whitelistadd(bits256 pubkey)
|
|
|
|
{
|
|
|
|
return(LP_bits256_add("LP_RTmetrics_whitelistadd whitelist",LP_RTmetrics.whitelist,&LP_RTmetrics.numwhitelist,(int32_t)(sizeof(LP_RTmetrics.whitelist)/sizeof(*LP_RTmetrics.whitelist)),pubkey));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_RTmetrics_blacklistadd(bits256 pubkey)
|
|
|
|
{
|
|
|
|
return(LP_bits256_add("LP_RTmetrics_blacklistadd blacklist",LP_RTmetrics.blacklist,&LP_RTmetrics.numblacklist,(int32_t)(sizeof(LP_RTmetrics.blacklist)/sizeof(*LP_RTmetrics.blacklist)),pubkey));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_RTmetrics_pendingswap(bits256 pubkey,int64_t kmdvalue)
|
|
|
|
{
|
|
|
|
int32_t ind;
|
|
|
|
if ( (ind= LP_bits256_add("LP_RTmetrics_pendingswap",LP_RTmetrics.pending_pubkeys,&LP_RTmetrics.numpendings,(int32_t)(sizeof(LP_RTmetrics.pending_pubkeys)/sizeof(*LP_RTmetrics.pending_pubkeys)),pubkey)) >= 0 )
|
|
|
|
{
|
|
|
|
LP_RTmetrics.pending_swaps[ind]++;
|
|
|
|
LP_RTmetrics.pending_kmdvalue[ind] += kmdvalue;
|
|
|
|
}
|
|
|
|
return(ind);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_RTmetrics_pendingswaps(bits256 pubkey)
|
|
|
|
{
|
|
|
|
int32_t ind;
|
|
|
|
if ( (ind= LP_bits256_find(LP_RTmetrics.pending_pubkeys,LP_RTmetrics.numpendings,pubkey)) >= 0 )
|
|
|
|
return(LP_RTmetrics.pending_swaps[ind]);
|
|
|
|
else return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_RTmetrics_avoidtxid(bits256 txid)
|
|
|
|
{
|
|
|
|
return(LP_bits256_find(LP_RTmetrics.avoidtxids,LP_RTmetrics.numavoidtxids,txid));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_RTmetrics_whitelisted(bits256 pubkey)
|
|
|
|
{
|
|
|
|
return(LP_bits256_find(LP_RTmetrics.whitelist,LP_RTmetrics.numwhitelist,pubkey));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_RTmetrics_blacklisted(bits256 pubkey)
|
|
|
|
{
|
|
|
|
return(LP_bits256_find(LP_RTmetrics.blacklist,LP_RTmetrics.numblacklist,pubkey));
|
|
|
|
}
|
|
|
|
|
|
|
|
void LP_RTmetrics_swapsinfo(char *refbase,char *refrel,cJSON *swaps,int32_t numswaps)
|
|
|
|
{
|
|
|
|
int32_t i; char *base,*rel,*retstr; cJSON *item,*swapjson; bits256 srcpub,destpub; uint64_t aliceid,basesatoshis,relsatoshis; uint32_t requestid,quoteid; double price;
|
|
|
|
for (i=0; i<numswaps; i++)
|
|
|
|
{
|
|
|
|
item = jitem(swaps,i);
|
|
|
|
if ( (base= jstr(item,"base")) == 0 )
|
|
|
|
base = "";
|
|
|
|
if ( (rel= jstr(item,"rel")) == 0 )
|
|
|
|
rel = "";
|
|
|
|
if ( strcmp(base,refbase) != 0 && strcmp(base,refrel) != 0 && strcmp(rel,refbase) != 0 && strcmp(rel,refrel) != 0 )
|
|
|
|
continue;
|
|
|
|
aliceid = j64bits(item,"aliceid");
|
|
|
|
basesatoshis = SATOSHIDEN * jdouble(item,"basevol");
|
|
|
|
srcpub = jbits256(item,"src");
|
|
|
|
relsatoshis = SATOSHIDEN * jdouble(item,"relvol");
|
|
|
|
destpub = jbits256(item,"dest");
|
|
|
|
price = jdouble(item,"price");
|
|
|
|
requestid = juint(item,"requestid");
|
|
|
|
quoteid = juint(item,"quoteid");
|
|
|
|
LP_RTmetrics_pendingswap(srcpub,LP_kmdvalue(base,basesatoshis));
|
|
|
|
LP_RTmetrics_pendingswap(destpub,LP_kmdvalue(rel,relsatoshis));
|
|
|
|
if ( 0 && (retstr= basilisk_swapentry(requestid,quoteid)) != 0 ) // no need for this
|
|
|
|
{
|
|
|
|
if ( (swapjson= cJSON_Parse(retstr)) != 0 )
|
|
|
|
{
|
|
|
|
LP_RTmetrics_avoidadd(jbits256(swapjson,"bobdeposit"));
|
|
|
|
LP_RTmetrics_avoidadd(jbits256(swapjson,"alicepayment"));
|
|
|
|
LP_RTmetrics_avoidadd(jbits256(swapjson,"bobpayment"));
|
|
|
|
LP_RTmetrics_avoidadd(jbits256(swapjson,"paymentspent"));
|
|
|
|
LP_RTmetrics_avoidadd(jbits256(swapjson,"Apaymentspent"));
|
|
|
|
LP_RTmetrics_avoidadd(jbits256(swapjson,"depositspent"));
|
|
|
|
free_json(swapjson);
|
|
|
|
}
|
|
|
|
free(retstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LP_RTmetrics_update(char *base,char *rel)
|
|
|
|
{
|
|
|
|
struct LP_pubkeyinfo *pubp,*tmp; uint32_t futuretime; int32_t i,numswaps; bits256 pubkey,zero; char *retstr; cJSON *statsjson,*swaps;
|
|
|
|
memset(&LP_RTmetrics,0,sizeof(LP_RTmetrics));
|
|
|
|
HASH_ITER(hh,LP_pubkeyinfos,pubp,tmp)
|
|
|
|
{
|
|
|
|
if ( pubp->istrusted > 0 )
|
|
|
|
LP_RTmetrics_whitelistadd(pubp->pubkey);
|
|
|
|
else if ( pubp->istrusted < 0 )
|
|
|
|
LP_RTmetrics_blacklistadd(pubp->pubkey);
|
|
|
|
pubp->swaps_kmdvalue = 0;
|
|
|
|
}
|
|
|
|
futuretime = (uint32_t)time(NULL) + 3600*100;
|
|
|
|
memset(zero.bytes,0,sizeof(zero));
|
|
|
|
if ( (retstr= LP_statslog_disp(100,futuretime,futuretime,"",zero)) != 0 )
|
|
|
|
{
|
|
|
|
if ( (statsjson= cJSON_Parse(retstr)) != 0 )
|
|
|
|
{
|
|
|
|
if ( (swaps= jarray(&numswaps,statsjson,"swaps")) != 0 )
|
|
|
|
{
|
|
|
|
//printf("LP_RTmetrics_update for (%s)\n",jprint(swaps,0));
|
|
|
|
if ( numswaps > 0 )
|
|
|
|
LP_RTmetrics_swapsinfo(base,rel,swaps,numswaps);
|
|
|
|
}
|
|
|
|
free_json(statsjson);
|
|
|
|
}
|
|
|
|
free(retstr);
|
|
|
|
}
|
|
|
|
for (i=0; i<LP_RTmetrics.numpendings; i++)
|
|
|
|
{
|
|
|
|
pubkey = LP_RTmetrics.pending_pubkeys[i];
|
|
|
|
if ( LP_RTmetrics.pending_swaps[i] > LP_MAXPENDING_SWAPS )
|
|
|
|
{
|
|
|
|
char str[65]; printf("%s has %d pending swaps! which is more than %d\n",bits256_str(str,pubkey),LP_RTmetrics.pending_swaps[i],LP_MAXPENDING_SWAPS);
|
|
|
|
LP_RTmetrics_blacklistadd(pubkey);
|
|
|
|
}
|
|
|
|
else if ( (pubp= LP_pubkeyfind(pubkey)) != 0 )
|
|
|
|
{
|
|
|
|
char str[65]; printf("%s has %d pending swaps %.8f kmdvalue\n",bits256_str(str,pubkey),LP_RTmetrics.pending_swaps[i],dstr(LP_RTmetrics.pending_kmdvalue[i]));
|
|
|
|
pubp->swaps_kmdvalue = LP_RTmetrics.pending_kmdvalue[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//printf("%d pubkeys have pending swaps, whitelist.%d blacklist.%d avoidtxids.%d\n",LP_RTmetrics.numpendings,LP_RTmetrics.numwhitelist,LP_RTmetrics.numblacklist,LP_RTmetrics.numavoidtxids);
|
|
|
|
}
|
|
|
|
|
|
|
|
double _LP_RTmetric_calc(struct LP_metricinfo *mp,double bestprice,double maxprice,double relvolume)
|
|
|
|
{
|
|
|
|
int32_t n; double metric,origmetric = (mp->price / bestprice);
|
|
|
|
metric = origmetric;
|
|
|
|
if ( mp->numutxos == 0 || relvolume == 0. || mp->maxvol == 0. || mp->balance == 0. )
|
|
|
|
{
|
|
|
|
//printf("skip i.%d as no info\n",mp->ind);
|
|
|
|
return(metric * 100.);
|
|
|
|
}
|
|
|
|
if ( relvolume < mp->minvol )
|
|
|
|
{
|
|
|
|
metric *= (mp->minvol / relvolume);
|
|
|
|
//printf("relvolume < minvol %.8f\n",(mp->minvol / relvolume));
|
|
|
|
}
|
|
|
|
else if ( relvolume > mp->maxvol )
|
|
|
|
{
|
|
|
|
metric *= (relvolume / mp->maxvol);
|
|
|
|
//printf("relvolume > minvol %.8f\n",(relvolume / mp->maxvol));
|
|
|
|
}
|
|
|
|
if ( relvolume < mp->balance/LP_MINVOL )
|
|
|
|
{
|
|
|
|
metric *= (mp->balance / relvolume);
|
|
|
|
//printf("relvolume < balance %.8f\n",(mp->balance / relvolume));
|
|
|
|
}
|
|
|
|
else if ( relvolume > mp->balance/mp->numutxos )
|
|
|
|
{
|
|
|
|
metric *= (relvolume / (mp->balance/mp->numutxos));
|
|
|
|
//printf("relvolume < ave %.8f\n",(relvolume / (mp->balance/mp->numutxos)));
|
|
|
|
}
|
|
|
|
if ( mp->age > LP_ORDERBOOK_DURATION*0.8 )
|
|
|
|
metric *= 2;
|
|
|
|
else if ( mp->age > 60 )
|
|
|
|
metric *= 1.03;
|
|
|
|
if ( (n= mp->pendingswaps) > 0 )
|
|
|
|
while ( n-- > 0 )
|
|
|
|
metric *= 1.1;
|
|
|
|
//if ( metric != origmetric )
|
|
|
|
printf("i.%d price %.8f orig %.8f -> %.8f relvol %.8f min %.8f max %.8f bal %.8f age.%d pend.%d\n",mp->ind,mp->price,origmetric,metric,relvolume,mp->minvol,mp->maxvol,mp->balance,mp->age,mp->pendingswaps);
|
|
|
|
return(metric);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LP_RTmetric_calc(struct LP_metricinfo *sortbuf,int32_t ind,cJSON *item,double bestprice,double maxprice,double relvolume,double prevdepth)
|
|
|
|
{
|
|
|
|
sortbuf[ind].pubkey = jbits256(item,"pubkey");
|
|
|
|
sortbuf[ind].price = jdouble(item,"price");
|
|
|
|
sortbuf[ind].maxvol = jdouble(item,"maxvolume");
|
|
|
|
sortbuf[ind].minvol = jdouble(item,"minvolume");
|
|
|
|
sortbuf[ind].balance = jdouble(item,"depth") - prevdepth;
|
|
|
|
sortbuf[ind].numutxos = juint(item,"numutxos");
|
|
|
|
sortbuf[ind].age = juint(item,"age");
|
|
|
|
sortbuf[ind].ind = ind;
|
|
|
|
sortbuf[ind].pendingswaps = LP_RTmetrics_pendingswaps(sortbuf[ind].pubkey);
|
|
|
|
sortbuf[ind].metric = _LP_RTmetric_calc(&sortbuf[ind],bestprice,maxprice,relvolume);
|
|
|
|
}
|
|
|
|
|
|
|
|
int _increasing_metrics(const void *a,const void *b)
|
|
|
|
{
|
|
|
|
#define ptr_a ((struct LP_metricinfo *)a)
|
|
|
|
#define ptr_b ((struct LP_metricinfo *)b)
|
|
|
|
if ( ptr_b->metric > ptr_a->metric )
|
|
|
|
return(-1);
|
|
|
|
else if ( ptr_b->metric < ptr_a->metric )
|
|
|
|
return(1);
|
|
|
|
return(0);
|
|
|
|
#undef ptr_a
|
|
|
|
#undef ptr_b
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *LP_RTmetrics_sort(char *base,char *rel,cJSON *rawasks,int32_t numasks,double maxprice,double relvolume)
|
|
|
|
{
|
|
|
|
cJSON *array=rawasks,*item; int32_t i,num,groupi; double price,prevdepth,bestprice; struct LP_metricinfo *sortbuf;
|
|
|
|
groupi = -1;
|
|
|
|
bestprice = 0.;
|
|
|
|
for (num=i=0; i<numasks; i++)
|
|
|
|
{
|
|
|
|
item = jitem(rawasks,i);
|
|
|
|
price = jdouble(item,"price");
|
|
|
|
if ( price > maxprice )
|
|
|
|
break;
|
|
|
|
if ( i == 0 )
|
|
|
|
bestprice = price;
|
|
|
|
else if ( price < bestprice*LP_RTMETRICS_TOPGROUP )
|
|
|
|
groupi = i;
|
|
|
|
num++;
|
|
|
|
}
|
|
|
|
if ( groupi > 0 )
|
|
|
|
{
|
|
|
|
sortbuf = calloc(groupi+1,sizeof(*sortbuf));
|
|
|
|
prevdepth = 0.;
|
|
|
|
for (i=0; i<=groupi; i++)
|
|
|
|
{
|
|
|
|
item = jitem(rawasks,i);
|
|
|
|
LP_RTmetric_calc(sortbuf,i,item,bestprice,maxprice,relvolume,prevdepth);
|
|
|
|
prevdepth = jdouble(item,"depth");
|
|
|
|
//printf("%.8f ",sortbuf[i].metric);
|
|
|
|
}
|
|
|
|
qsort(&sortbuf[0].metric,groupi+1,sizeof(*sortbuf),_increasing_metrics);
|
|
|
|
array = cJSON_CreateArray();
|
|
|
|
for (i=0; i<=groupi; i++)
|
|
|
|
{
|
|
|
|
printf("(%d <- %d %.3f) ",i,sortbuf[i].ind,sortbuf[i].metric);
|
|
|
|
item = jitem(rawasks,sortbuf[i].ind);
|
|
|
|
jaddi(array,jduplicate(item));
|
|
|
|
}
|
|
|
|
for (; i<numasks; i++)
|
|
|
|
jaddi(array,jduplicate(jitem(rawasks,i)));
|
|
|
|
printf("new ask order for %d of %d, capped at num.%d\n",groupi,numasks,num);
|
|
|
|
free(sortbuf);
|
|
|
|
}
|
|
|
|
return(array);
|
|
|
|
}
|