/****************************************************************************** * 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. * * * ******************************************************************************/ // included from basilisk.c /* In order to provide liquidity from central exchanges, we need to issue balancing trades, however to do this properly, we need to know what the desired balance is. If unspecified, then a neutral balance is assumed. The liquidity_info interface is quite flexible, there is a single function liquidity_active() which returns non-zero if the LP node should respond. The model is that the liquidity_command() is used to configure the liquidity_active()'s response. In order for dynamic adaptiveness to work, the liquidity_command/liquidity_active needs to interact with the balancing. A simplistic default trio of functions are provided, but any level of complexity is possible with the liquidity interface. */ #define TRADEBOTS_NUMANSWERS 8 #define TRADEBOTS_NUMDECAYS 8 #define TRADEBOTS_RAWFEATURESINCR 7 #define TRADEBOTS_MAXPAIRS 1024 #define _OCAS_PLUS_INF (-log(0.0)) double OCAS_PLUS_INF,OCAS_NEG_INF; double Tradebots_decays[TRADEBOTS_NUMDECAYS] = { 0.5, 0.666, 0.8, 0.9, 0.95, 0.99, 0.995, 0.999 }; int32_t Tradebots_answergaps[TRADEBOTS_NUMANSWERS] = { 60, 60, 120, 120, 240, 240, 720, 720 }; struct tradebot_arbentry { char exchange[16]; double price,volume,profitmargin; uint32_t timestamp; }; struct tradebot_arbexchange { char name[16]; struct tradebot_arbentry trades[2]; }; struct tradebot_arbpair { char base[32],rel[32]; uint32_t lasttime,lastanswertime; FILE *fp; int32_t numexchanges,counter,btccounter,usdcounter,cnycounter,refc; double highbid,lowask,hblavolume,btcbid,btcask,btcvol,usdbid,usdask,usdvol,cnybid,cnyask,cnyvol; double bidaves[TRADEBOTS_NUMDECAYS],askaves[TRADEBOTS_NUMDECAYS]; double bidslopes[TRADEBOTS_NUMDECAYS],askslopes[TRADEBOTS_NUMDECAYS]; struct tradebot_arbexchange exchanges[16]; uint8_t dirmasks[2],slopedirs[2]; char *svmpairs[TRADEBOTS_MAXPAIRS][2]; int32_t RTgood[TRADEBOTS_NUMANSWERS],RTbad[TRADEBOTS_NUMANSWERS],numrawfeatures,numsvmfeatures,numpairs; float rawfeatures[TRADEBOTS_NUMANSWERS+64],prevrawfeatures[60 * TRADEBOTS_NUMANSWERS+64],*svms; // svms is coeffs vector[TRADEBOTS_NUMANSWERS] float RTpreds[TRADEBOTS_NUMANSWERS],svmpreds[TRADEBOTS_NUMANSWERS],answers[TRADEBOTS_NUMANSWERS]; }; struct tradebot_arbpair Arbpairs[TRADEBOTS_MAXPAIRS],*Pair_NXTBTC,*Pair_BTCUSD,*Pair_BTCCNY; int32_t Tradebot_numarbpairs; struct tradebot_arbpair *tradebots_arbpair_find(char *base,char *rel) { int32_t i; for (i=0; i0; i--) memcpy(&pair->prevrawfeatures[i*72],&pair->prevrawfeatures[(i-1)*72],sizeof(pair->rawfeatures)); memcpy(pair->prevrawfeatures,pair->rawfeatures,sizeof(pair->rawfeatures)); memset(pair->rawfeatures,0,sizeof(pair->rawfeatures)); if ( fabs(pair->highbid) < SMALLVAL || fabs(pair->lowask) < SMALLVAL ) return(-1); ave = _pairaved(pair->highbid,pair->lowask); timestamp = (uint32_t)time(NULL); n = TRADEBOTS_NUMANSWERS; memcpy(&pair->rawfeatures[TRADEBOTS_NUMANSWERS],×tamp,sizeof(*pair->rawfeatures)), n++; pair->rawfeatures[n++] = pair->highbid; pair->rawfeatures[n++] = pair->lowask; pair->rawfeatures[n++] = pair->hblavolume / ave; n = TRADEBOTS_RAWFEATURESINCR + TRADEBOTS_NUMANSWERS; for (i=0; ibidaves[i]) < SMALLVAL || fabs(pair->askaves[i]) < SMALLVAL ) return(-1); starti = n; pair->rawfeatures[n++] = (pair->bidaves[i] / ave) - 1.; pair->rawfeatures[n++] = (pair->askaves[i] / ave) - 1.; pair->rawfeatures[n++] = 10000. * (pair->bidslopes[i] / ave); pair->rawfeatures[n++] = 10000. * (pair->askslopes[i] / ave); if ( n < starti+TRADEBOTS_RAWFEATURESINCR ) n = starti+TRADEBOTS_RAWFEATURESINCR; } if ( pair->fp != 0 ) { if ( fwrite(pair->rawfeatures,1,sizeof(pair->rawfeatures),pair->fp) != sizeof(pair->rawfeatures) ) printf("fwrite error for %s/%s rawfeatures[%d]\n",pair->base,pair->rel,n); else fflush(pair->fp); } if ( n > sizeof(pair->rawfeatures)/sizeof(*pair->rawfeatures) ) { printf("n.%d too many for rawfeatures %d\n",n,(int32_t)(sizeof(pair->rawfeatures)/sizeof(*pair->rawfeatures))); exit(-1); } return(n); } uint32_t tradebots_featureset(double *highbidp,double *lowaskp,double *avep,double *volp,double *bidaves,double *askaves,double *bidslopes,double *askslopes,float *rawfeatures) { uint32_t timestamp; int32_t i,n,starti; memcpy(×tamp,&rawfeatures[TRADEBOTS_NUMANSWERS],sizeof(timestamp)); n = TRADEBOTS_NUMANSWERS + 1; *highbidp = rawfeatures[n++]; *lowaskp = rawfeatures[n++]; *avep = _pairaved(*highbidp,*lowaskp); *volp = rawfeatures[n++]; //printf("[%9.6f %9.6f] vol %f t.%u\n",*highbidp,*lowaskp,*volp,timestamp); n = TRADEBOTS_RAWFEATURESINCR + TRADEBOTS_NUMANSWERS; for (i=0; irefc = Tradebot_numarbpairs++; strcpy(pair->rel,rel); strcpy(pair->base,base); if ( strcmp(base,"NXT") == 0 && strcmp(rel,"BTC") == 0 ) Pair_NXTBTC = pair, printf("Pair_NXTBTC <- %p\n",pair); else if ( strcmp(base,"BTC") == 0 && strcmp(rel,"USD") == 0 ) Pair_BTCUSD = pair; else if ( strcmp(base,"BTC") == 0 && strcmp(rel,"CNY") == 0 ) Pair_BTCCNY = pair; sprintf(fname,"SVM/rawfeatures/%s_%s",base,rel); pair->fp = OS_appendfile(fname); if ( (ftell(pair->fp) % sizeof(pair->rawfeatures)) != 0 ) { printf("misalinged rawfeatures %d %d\n",(uint32_t)ftell(pair->fp),(uint32_t)(ftell(pair->fp) % sizeof(pair->rawfeatures))); } fseek(pair->fp,(ftell(pair->fp) / sizeof(pair->rawfeatures)) * sizeof(pair->rawfeatures) - sizeof(pair->rawfeatures),SEEK_SET); if ( fread(pair->rawfeatures,1,sizeof(pair->rawfeatures),pair->fp) == sizeof(pair->rawfeatures) ) { pair->lasttime = tradebots_featureset(&pair->highbid,&pair->lowask,&ave,&pair->hblavolume,pair->bidaves,pair->askaves,pair->bidslopes,pair->askslopes,pair->rawfeatures); printf("%s/%s [%.8f %.8f] %u\n",pair->base,pair->rel,pair->highbid,pair->lowask,pair->lasttime); } return(pair); } else return(0); } int32_t tradebots_expandrawfeatures(double *svmfeatures,float *rawfeatures,uint32_t reftimestamp,float *refrawfeatures) { double factor,highbid,lowask,ave,vol,bidaves[TRADEBOTS_NUMDECAYS],askaves[TRADEBOTS_NUMDECAYS],bidslopes[TRADEBOTS_NUMDECAYS],askslopes[TRADEBOTS_NUMDECAYS]; double refhighbid,reflowask,refave,refvol,refbidaves[TRADEBOTS_NUMDECAYS],refaskaves[TRADEBOTS_NUMDECAYS],refbidslopes[TRADEBOTS_NUMDECAYS],refaskslopes[TRADEBOTS_NUMDECAYS]; uint32_t timestamp; int32_t i,j,starti,n = 0; tradebots_featureset(&refhighbid,&reflowask,&refave,&refvol,refbidaves,refaskaves,refbidslopes,refaskslopes,refrawfeatures); timestamp = tradebots_featureset(&highbid,&lowask,&ave,&vol,bidaves,askaves,bidslopes,askslopes,rawfeatures); if ( timestamp == 0 || reftimestamp == 0 || timestamp >= reftimestamp+60 ) { //printf("tradebots_expandrawfeatures: timestamp.%u vs reftimestamp.%u\n",timestamp,reftimestamp); return(-1); } factor = sqrt(reftimestamp - timestamp); if ( factor > 60. ) factor = 60.; else if ( factor < 1. ) factor = 1.; factor = 1. / factor; if ( refhighbid == 0. || highbid == 0. || lowask == 0. || reflowask == 0. ) { //printf("tradebots_expandrawfeatures: (%f %f) ref (%f %f)\n",highbid,lowask,refhighbid,reflowask); return(-1); } svmfeatures[n++] = highbid; svmfeatures[n++] = (highbid / ave) - 1.; svmfeatures[n++] = lowask; svmfeatures[n++] = (lowask / ave) - 1.; svmfeatures[n++] = (lowask - highbid); svmfeatures[n++] = (lowask - highbid) / ave; svmfeatures[n++] = vol; starti = n; svmfeatures[n++] = refhighbid; svmfeatures[n++] = (refhighbid / refave) - 1.; svmfeatures[n++] = reflowask; svmfeatures[n++] = (reflowask / refave) - 1.; svmfeatures[n++] = (reflowask - refhighbid); svmfeatures[n++] = (reflowask - refhighbid) / refave; svmfeatures[n++] = refvol; for (i=0; i SMALLVAL ) { for (i=starti; i 0. ) svmfeatures[i] = cbrt(svmfeatures[i]); else svmfeatures[i] = -cbrt(-svmfeatures[i]); } } return(n); } int32_t tradebots_calcsvmfeatures(double *svmfeatures,struct tradebot_arbpair *pair,float *rawfeatures,float *prevrawfeatures) { int32_t i,j,n,numpairfeatures,flag; struct tradebot_arbpair *ptr; uint32_t reftimestamp; memcpy(&reftimestamp,&rawfeatures[TRADEBOTS_NUMANSWERS],sizeof(reftimestamp)); if ( reftimestamp == 0 ) { printf("reftimestamp.%u is illegal\n",reftimestamp); return(-1); } numpairfeatures = n = tradebots_expandrawfeatures(svmfeatures,prevrawfeatures,reftimestamp,rawfeatures); if ( n <= 0 ) return(-1); for (i=0; i<60; i++,n+=numpairfeatures) tradebots_expandrawfeatures(&svmfeatures[n],&prevrawfeatures[i*72],reftimestamp,rawfeatures); if ( 0 && pair->numsvmfeatures != (1+pair->numpairs)*n ) { for (i=0; inumpairs; i++) // need to do lookups { flag = -1; if ( (ptr= tradebots_arbpair_find(pair->svmpairs[i][0],pair->svmpairs[i][1])) != 0 ) flag = tradebots_expandrawfeatures(&svmfeatures[n],ptr->rawfeatures,reftimestamp,rawfeatures); if ( flag < 0 ) { for (j=0; jsvms != 0 ) { for (i=n=0; inumsvmfeatures; i++) { feature = svmfeatures[i]; for (j=0; jsvms[n++]; } } return(n); } void tradebots_calcanswers(struct tradebot_arbpair *pair) { double highbid,lowask,futurebid,futureask,ave,vol,bidaves[TRADEBOTS_NUMDECAYS],askaves[TRADEBOTS_NUMDECAYS],bidslopes[TRADEBOTS_NUMDECAYS],askslopes[TRADEBOTS_NUMDECAYS]; float rawfeatures[sizeof(pair->rawfeatures)/sizeof(*pair->rawfeatures)],futuremin=0,futuremax=0,minval=0,maxval=0,*hblas = 0; uint32_t timestamp,firsttime = 0; long fpos,savepos; int32_t flag,i,iter,j,ind,maxi=0; OCAS_PLUS_INF = _OCAS_PLUS_INF; OCAS_NEG_INF = -_OCAS_PLUS_INF; if ( pair->fp != 0 ) { for (iter=0; iter<2; iter++) { rewind(pair->fp); fpos = 0; while ( fread(rawfeatures,1,sizeof(pair->rawfeatures),pair->fp) == sizeof(pair->rawfeatures) ) { savepos = ftell(pair->fp); timestamp = tradebots_featureset(&highbid,&lowask,&ave,&vol,bidaves,askaves,bidslopes,askslopes,rawfeatures); //printf("timestamp.%u firsttime.%u\n",timestamp,firsttime); if ( timestamp == 0 ) continue; if ( firsttime == 0 ) { firsttime = timestamp; maxi = (int32_t)((time(NULL) - firsttime) / 60 + 1); hblas = calloc(maxi,sizeof(*hblas)*2); printf("HBLAS[%d] allocated\n",maxi); } if ( (i= (timestamp - firsttime)/60) >= 0 && i < maxi ) { if ( iter == 0 ) { if ( hblas[i << 1] == 0 ) { hblas[i << 1] = highbid; hblas[(i << 1) + 1] = lowask; } else { _xblend(&hblas[i << 1],highbid,0.5); _xblend(&hblas[(i << 1) + 1],lowask,0.5); } } else { highbid = hblas[i << 1]; lowask = hblas[(i << 1) + 1]; if ( fabs(highbid) > SMALLVAL && fabs(lowask) > SMALLVAL ) { memset(pair->answers,0,sizeof(pair->answers)); flag = 0; for (j=0; janswers[j] = _pairaved(futuremax,futuremin) - _pairaved(minval,maxval); else { futurebid = hblas[ind << 1]; futureask = hblas[(ind << 1) + 1]; minval = MIN(highbid,lowask); maxval = MAX(highbid,lowask); futuremin = MIN(futurebid,futureask); futuremax = MAX(futurebid,futureask); if ( futuremin > maxval ) { if ( futuremax < minval ) printf("%s/%s A%d: highly volatile minmax.(%f %f) -> (%f %f) %d of %d\n",pair->base,pair->rel,j,minval,maxval,futuremin,futuremax,i,maxi); else { pair->answers[j] = (futuremin - maxval); flag++; } } else if ( futuremax < minval ) pair->answers[j] = (futuremax - minval), flag++; //printf("i.%d j.%d gap.%d ind.%d answer %9.6f (%f %f) -> (%f %f)\n",i,j,Tradebots_answergaps[j],ind,pair->answers[j],minval,maxval,futuremin,futuremax); } } } if ( flag != 0 ) { fseek(pair->fp,fpos,SEEK_SET); if ( fwrite(pair->answers,1,sizeof(pair->answers),pair->fp) != sizeof(pair->answers) ) printf("error writing answers for %s/%s t%u i.%d of %d\n",pair->base,pair->rel,timestamp,i,maxi); else if ( 0 ) { for (j=0; janswers[j]); printf("%s/%s answers %d of %d\n",pair->base,pair->rel,i,maxi); } fseek(pair->fp,savepos,SEEK_SET); } } } } fpos = ftell(pair->fp); } if ( iter == 0 ) { if ( hblas == 0 ) break; highbid = hblas[0]; lowask = hblas[1]; for (i=1; i SMALLVAL && fabs(hblas[(i << 1) + 1]) > SMALLVAL ) { highbid = hblas[i << 1]; lowask = hblas[(i << 1) + 1]; } else { hblas[i << 1] = highbid; hblas[(i << 1) + 1] = lowask; } //printf("%9.6f ",_pairaved(highbid,lowask)); } //printf("maxi.%d\n",maxi); } } if ( hblas != 0 ) free(hblas); } if ( pair->fp != 0 && (ftell(pair->fp) % sizeof(pair->rawfeatures)) != 0 ) printf("ERROR: %s/%s not on feature boundary\n",pair->base,pair->rel); } double get_yval(double *answerp,int32_t selector,int32_t ind,int32_t refc,int32_t answerind) { float answer; struct tradebot_arbpair *pair; long savepos; pair = &Arbpairs[refc]; if ( pair->fp != 0 ) { savepos = ftell(pair->fp); fseek(pair->fp,ind*sizeof(pair->rawfeatures)+answerind*sizeof(*pair->rawfeatures),SEEK_SET); if ( fread(&answer,1,sizeof(answer),pair->fp) != sizeof(answer) ) answer = 0; fseek(pair->fp,savepos,SEEK_SET); if ( isnan(answer) != 0 ) return(0); answer /= 10.; /*if ( answer > .01 ) answer = .01; else if ( answer < -.01 ) answer = -.01; if ( answerp != 0 ) *answerp = answer; */ if ( answer > 0. ) answer = sqrt(answer); else answer = -sqrt(-answer); *answerp = answer; /*if ( answer > 0. ) return(1.); else if ( answer < 0. ) return(-1.);*/ return(answer); } return(0.); } float *get_features(int32_t numfeatures,int32_t refc,int32_t ind) { struct tradebot_arbpair *pair; long savepos; int32_t i,n; double svmfeatures[32768]; float rawfeatures[sizeof(pair->rawfeatures)],prevrawfeatures[60 * sizeof(pair->rawfeatures)],*svmf=0; pair = &Arbpairs[refc]; pair->numsvmfeatures = numfeatures; if ( pair->fp != 0 && ind > 61 ) { savepos = ftell(pair->fp); fseek(pair->fp,(ind-60)*sizeof(pair->rawfeatures),SEEK_SET); if ( fread(prevrawfeatures,60,sizeof(pair->rawfeatures),pair->fp) == sizeof(pair->rawfeatures) && fread(&rawfeatures,1,sizeof(pair->rawfeatures),pair->fp) == sizeof(pair->rawfeatures) ) { n = tradebots_calcsvmfeatures(svmfeatures,pair,rawfeatures,prevrawfeatures); //int32_t nonz; for (i=nonz=0; i %d\n",ind,n); if ( n != pair->numsvmfeatures ) { //printf("unexpected numsvmfeatures refc.%d ind.%d %d vs %d\n",refc,ind,n,pair->numsvmfeatures); return(0); } svmf = calloc(n,sizeof(*svmf)); for (i=0; ifp,savepos,SEEK_SET); } return(svmf); } /*double set_ocas_model(int refc,int answerind,double *W,double W0,int numfeatures,int firstweekind,int len,int bad,double dist,double predabs,int posA,int negA,double answerabs,double aveanswer) { int32_t i,nonz=0; for (i=0; i 0 ) { bestmodel = W; if ( bestmodel != 0 ) { for (j=0; j<=numfeatures; j++) { if ( bestmodel[j] != 0 ) nonz++; oldW[j] = W[j]; } if ( nonz != 0 ) return(bestmodel[numfeatures]); } } return(0.); } double set_ocas_model(int refc,int answerind,double *W,double W0,int numfeatures,int firstweekind,int len,int bad,double dist,double predabs,int posA,int negA,double answerabs,double aveanswer) { double perc = (100. * (double)(len - bad)) / len; #ifndef DISABLE_EXISTINGMODEL int32_t _posA,_negA; double *tmpW,fileperc; tmpW = calloc(numfeatures+2,sizeof(*tmpW)); if ( (fileperc= load_model(&_posA,&_negA,tmpW,refc,answerind,numfeatures)) > perc ) { if ( (_posA+_negA) != 0 && _posA >= posA && _negA >= negA ) { memcpy(W,tmpW,sizeof(*W)*numfeatures); printf("%s/%s.A%02d numfeatures.%d posA.%d negA.%d saved model %f is better than %f +A%d -A%d\n",Arbpairs[refc].base,Arbpairs[refc].rel,answerind,numfeatures,_posA,_negA,fileperc,perc,posA,negA); W0 = tmpW[numfeatures]; free(tmpW); return(tmpW[numfeatures]); } } free(tmpW); #endif save_model(refc,answerind,W,numfeatures,W0,perc,posA,negA); return(W0); } #ifndef _WIN #include "tradebots_SVM.h" #endif static char *assetids[][2] = { { "12071612744977229797", "UNITY" }, { "15344649963748848799", "DEX" }, { "6883271355794806507", "PANGEA" }, { "17911762572811467637", "JUMBLR" }, { "17083334802666450484", "BET" }, { "13476425053110940554", "CRYPTO" }, { "6932037131189568014", "HODL" }, { "3006420581923704757", "SHARK" }, { "17571711292785902558", "BOTS" }, { "10524562908394749924", "MGW" }, }; uint64_t NXT_assetidfind(char *base) { int32_t i; for (i=0; iexchange[0] == 0 ) strcpy(arb->exchange,exchange); if ( strcmp(arb->exchange,exchange) == 0 ) { arb->price = price; arb->volume = volume; arb->timestamp = timestamp; arb->profitmargin = profitmargin; } else printf("mismatched arbexchange? (%s vs %s)\n",arb->exchange,exchange); } struct tradebot_arbexchange *tradebots_arbexchange_find(struct tradebot_arbpair *pair,char *exchange) { int32_t i; if ( pair->numexchanges > sizeof(pair->exchanges)/sizeof(*pair->exchanges) ) { printf("data corruption pair.%p %s %s/%s numexchanges.%d\n",pair,exchange,pair->base,pair->rel,pair->numexchanges); return(0); } for (i=0; inumexchanges; i++) if ( strcmp(pair->exchanges[i].name,exchange) == 0 ) return(&pair->exchanges[i]); return(0); } struct tradebot_arbexchange *tradebots_arbexchange_create(struct tradebot_arbpair *pair,char *exchange) { if ( pair->numexchanges < sizeof(pair->exchanges)/sizeof(*pair->exchanges) ) { strcpy(pair->exchanges[pair->numexchanges].name,exchange); return(&pair->exchanges[pair->numexchanges++]); } else return(0); } void tradebot_arbcandidate(struct supernet_info *myinfo,char *exchange,int32_t tradedir,char *base,char *rel,double price,double volume,uint32_t timestamp,double profitmargin) { int32_t i,offset,flag; double highbid,lowask,lastbid,lastask,arbval; uint32_t now; struct tradebot_arbentry *bid,*ask; struct tradebot_arbexchange *arbex; struct tradebot_arbpair *pair = 0; if ( strcmp(rel,"BTC") != 0 && strcmp(rel,"NXT") != 0 && strcmp(rel,"USD") != 0 && strcmp(rel,"CNY") != 0 ) { printf("reject non-BTC arbcandidate (%s/%s)\n",base,rel); return; } offset = (tradedir > 0) ? 0 : 1; if ( (pair= tradebots_arbpair_find(base,rel)) == 0 ) pair = tradebots_arbpair_create(base,rel); if ( pair == 0 ) { printf("cant get pair for %s %s/%s\n",exchange,base,rel); return; } if ( (arbex= tradebots_arbexchange_find(pair,exchange)) == 0 ) arbex = tradebots_arbexchange_create(pair,exchange); if ( arbex != 0 ) { //printf("cand.%d %16s %s %12.6f (%5s/%-5s) at %12.8f profit %.03f\n",pair->numexchanges,exchange,tradedir<0?"ask":"bid",volume,base,rel,price,profitmargin); tradebot_arbentry(&arbex->trades[offset],exchange,price,volume,timestamp,profitmargin); bid = ask = 0; pair->highbid = pair->lowask = highbid = lowask = 0.; now = (uint32_t)time(NULL); //if ( pair->numexchanges >= 2 ) { for (i=0; inumexchanges; i++) { arbex = &pair->exchanges[i]; if ( arbex->trades[0].price != 0. && (highbid == 0. || arbex->trades[0].price >= highbid) ) { bid = &arbex->trades[0]; if ( now > bid->timestamp+30 ) bid->price = 0.; else highbid = bid->price; } if ( arbex->trades[1].price != 0. && (lowask == 0. || arbex->trades[1].price <= lowask) ) { ask = &arbex->trades[1]; if ( now > ask->timestamp+30 ) ask->price = 0.; else lowask = ask->price; } //printf("%p %s %s %f %f -> %p %p %f %f (%f %f)\n",pair,pair->base,arbex->name,arbex->trades[0].price,arbex->trades[1].price,bid,ask,highbid,lowask,pair->highbid,pair->lowask); } flag = 0; if ( Pair_NXTBTC != 0 && pair->btccounter != Pair_NXTBTC->counter ) flag |= 1; if ( Pair_BTCUSD != 0 && pair->usdcounter != Pair_BTCUSD->counter ) flag |= 2; if ( Pair_BTCCNY != 0 && pair->cnycounter != Pair_BTCCNY->counter ) flag |= 4; //printf("%s %s/%s flag.%d (%d %d) %p %p\n",exchange,base,rel,flag,pair->btccounter,Pair_NXTBTC!=0?Pair_NXTBTC->counter:-1,bid,ask); if ( bid != 0 && ask != 0 && bid->price != 0. && ask->price != 0 && (now - bid->timestamp) < 30 && (now - ask->timestamp) < 30 && (fabs(bid->price - pair->highbid) > SMALLVAL || fabs(ask->price - pair->lowask) > SMALLVAL || (strcmp(pair->rel,"NXT") == 0 && flag != 0)) ) { pair->counter++; pair->hblavolume = volume = MIN(bid->volume,ask->volume); arbval = lastbid = lastask = 0.; memset(pair->dirmasks,0,sizeof(pair->dirmasks)); memset(pair->slopedirs,0,sizeof(pair->slopedirs)); if ( strcmp(pair->rel,"NXT") == 0 ) { if ( Pair_NXTBTC != 0 && Pair_NXTBTC->highbid != 0. && Pair_NXTBTC->lowask != 0. ) { pair->btccounter = Pair_NXTBTC->counter; pair->btcbid = highbid * Pair_NXTBTC->highbid; pair->btcask = lowask * Pair_NXTBTC->lowask; pair->btcvol = volume * _pairaved(pair->btcbid,pair->btcask); } } else if ( strcmp(pair->rel,"BTC") == 0 ) { pair->btcbid = highbid; pair->btcask = lowask; pair->btcvol = volume; } if ( strcmp(pair->rel,"USD") == 0 ) { pair->usdbid = highbid; pair->usdask = lowask; pair->usdvol = volume; } if ( strcmp(pair->rel,"CNY") == 0 ) { pair->cnybid = highbid; pair->cnyask = lowask; pair->cnyvol = volume; } if ( pair->btcbid != 0. && pair->btcask != 0. ) { if ( strcmp(pair->rel,"USD") != 0 && Pair_BTCUSD != 0 && Pair_BTCUSD->highbid != 0. && Pair_BTCUSD->lowask != 0. ) { pair->usdcounter = Pair_BTCUSD->counter; pair->usdbid = pair->btcbid * Pair_BTCUSD->highbid; pair->usdask = pair->btcask * Pair_BTCUSD->lowask; pair->usdvol = pair->btcvol * _pairaved(pair->usdbid,pair->usdask); } if ( strcmp(pair->rel,"CNY") != 0 && Pair_BTCCNY != 0 && Pair_BTCCNY->highbid != 0. && Pair_BTCCNY->lowask != 0. ) { pair->cnycounter = Pair_BTCCNY->counter; pair->cnybid = pair->btcbid * Pair_BTCCNY->highbid; pair->cnyask = pair->btcask * Pair_BTCCNY->lowask; pair->cnyvol = pair->btcvol * _pairaved(pair->cnybid,pair->cnyask); } } for (i=0; ibidslopes[i]= dxblend(&pair->bidaves[i],highbid,Tradebots_decays[i])) > 0. ) pair->slopedirs[0] |= (1 << i); if ( (pair->askslopes[i]= dxblend(&pair->askaves[i],lowask,Tradebots_decays[i])) > 0. ) pair->slopedirs[1] |= (1 << i); lastbid = pair->bidaves[i]; lastask = pair->askaves[i]; //printf("(%.8f %.8f) ",lastbid,lastask); } for (i=0; i lastbid ) pair->dirmasks[0] |= (1 << i); if ( lowask > lastask ) pair->dirmasks[1] |= (1 << i); } else { if ( pair->bidaves[i-1] > lastbid ) pair->dirmasks[0] |= (1 << i); if ( pair->askaves[i-1] > lastask ) pair->dirmasks[1] |= (1 << i); } } //printf("%12.6f %7s/%-3s %8s %14.8f %8s %14.8f spread %6.2f%% %02x:%02x %02x:%02x %d\n",volume,base,rel,bid->exchange,highbid,ask->exchange,lowask,100.*(lowask-highbid)/_pairaved(highbid,lowask),pair->dirmasks[0],pair->slopedirs[0],pair->dirmasks[1],pair->slopedirs[1],pair->counter); //printf("BTC.(%.8f %.8f) %.8f %.8f USD.(%.4f %.4f) CNY.(%.3f %.3f)\n",pair->btcbid,pair->btcask,Pair_BTCUSD!=0?Pair_BTCUSD->highbid:0,Pair_BTCUSD!=0?Pair_BTCUSD->lowask:0,pair->usdbid,pair->usdask,pair->cnybid,pair->cnyask); } if ( highbid != 0 ) pair->highbid = highbid; if ( lowask != 0 ) pair->lowask = lowask; //printf(">>>>>>> %s (%s/%s) BTC %.8f %.8f v%f counter.%d btc.%d (%d)\n",exchange,pair->base,pair->rel,pair->btcbid,pair->btcask,pair->btcvol,pair->counter,pair->btccounter,Pair_NXTBTC!=0?Pair_NXTBTC->counter:-1); if ( bid != 0 && ask != 0 && highbid != 0. && lowask != 0. && highbid > lowask && strcmp(bid->exchange,ask->exchange) != 0 && strcmp(rel,"BTC") == 0 ) { volume = MIN(bid->volume,ask->volume); if ( volume*_pairaved(highbid,lowask) > 0.1 ) volume = 0.1 / _pairaved(highbid,lowask); if ( highbid * (1. - bid->profitmargin) > lowask * (1. + ask->profitmargin) ) { arbval = highbid * (1. - bid->profitmargin) - lowask * (1. + ask->profitmargin); printf(">>>>>>>> FOUND ARB %s/%s highbid.%s %.8f lowask.%s %.8f volume %f (%.8f %.8f) %.4f%%\n",pair->base,pair->rel,bid->exchange,bid->price,ask->exchange,ask->price,volume,highbid,lowask,100.*(highbid-lowask)/_pairaved(highbid,lowask)); InstantDEX_buy(myinfo,0,0,0,ask->exchange,pair->base,"BTC",ask->price,volume,1); InstantDEX_sell(myinfo,0,0,0,bid->exchange,pair->base,"BTC",bid->price,volume,1); printf("finished trades %s/%s volume %f\n",pair->base,pair->rel,volume); } } if ( pair->counter > TRADEBOTS_NUMDECAYS ) { if ( pair->lasttime != time(NULL) ) { if ( (pair->numrawfeatures= tradebots_calcrawfeatures(pair)) > 0 ) { if ( pair->numsvmfeatures != 0 ) { if ( myinfo->svmfeatures == 0 ) myinfo->svmfeatures = calloc(sizeof(*myinfo->svmfeatures),pair->numsvmfeatures); if ( tradebots_calcsvmfeatures(myinfo->svmfeatures,pair,pair->rawfeatures,pair->prevrawfeatures) > 0 ) tradebots_calcpreds(pair->RTpreds,pair,myinfo->svmfeatures); } } pair->lasttime = (uint32_t)time(NULL); } if ( 0 && time(NULL) > pair->lastanswertime+3600 ) { tradebots_calcanswers(pair); pair->lastanswertime = (uint32_t)time(NULL); } } } } } cJSON *linfo_json(struct liquidity_info *li) { cJSON *item = cJSON_CreateObject(); jaddstr(item,"base",li->base); jaddstr(item,"rel",li->rel); if ( li->exchange[0] != 0 ) jaddstr(item,"exchange",li->exchange); if ( li->assetid != 0 ) jadd64bits(item,"assetid",li->assetid); if ( li->profit != 0. ) jaddnum(item,"profitmargin",li->profit); if ( li->refprice != 0. ) jaddnum(item,"refprice",li->refprice); if ( li->bid != 0. ) jaddnum(item,"bid",li->bid); if ( li->ask != 0. ) jaddnum(item,"ask",li->ask); if ( li->minvol != 0. ) jaddnum(item,"minvol",li->minvol); if ( li->maxvol != 0. ) jaddnum(item,"maxvol",li->maxvol); if ( li->totalvol != 0. ) jaddnum(item,"totalvol",li->totalvol); return(item); } void _default_liquidity_command(struct supernet_info *myinfo,char *base,bits256 hash,cJSON *vals) { struct liquidity_info li,refli; int32_t i; char *exchange,*relstr,numstr[32]; if ( (exchange= jstr(vals,"exchange")) == 0 ) exchange = "DEX"; else if ( strcmp(exchange,"*") == 0 ) exchange = ""; else if ( exchanges777_find(exchange) == 0 ) { printf("cant find exchange.(%s)\n",exchange); return; } if ( (relstr= jstr(vals,"rel")) == 0 ) relstr = "BTC"; if ( base == 0 || base[0] == 0 ) base = jstr(vals,"base"); if ( base == 0 || base[0] == 0 ) return; memset(&li,0,sizeof(li)); safecopy(li.base,base,sizeof(li.base)); safecopy(li.rel,relstr,sizeof(li.rel)); strncpy(li.exchange,exchange,sizeof(li.exchange)); li.profit = jdouble(vals,"profit"); li.refprice = jdouble(vals,"refprice"); li.bid = jdouble(vals,"bid"); li.ask = jdouble(vals,"ask"); if ( (li.minvol= jdouble(vals,"minvol")) <= 0. ) li.minvol = (strcmp("BTC",base) == 0) ? 0.0001 : 0.001; if ( strcmp(li.base,"KMD") == 0 && strcmp(li.rel,"BTC") == 0 && li.minvol > 100. ) li.minvol = 100.; if ( (li.maxvol= jdouble(vals,"maxvol")) < li.minvol ) li.maxvol = li.minvol; if ( (li.totalvol= jdouble(vals,"total")) < li.maxvol ) li.totalvol = li.maxvol; if ( strcmp("NXT",li.rel) == 0 ) li.assetid = NXT_assetidfind(base); else if ( strcmp("UNITY",base) == 0 ) li.assetid = NXT_assetidfind(base); if ( strcmp(li.base,"BTC") == 0 && strcmp("USD",li.rel) != 0 && strcmp("CNY",li.rel) != 0 ) { printf("unsupported base BTC (%s/%s)\n",li.base,li.rel); return; } if ( strcmp(li.base,"BTC") != 0 && strcmp("BTC",li.rel) != 0 && strcmp(li.base,"NXT") != 0 && strcmp("NXT",li.rel) != 0 && strcmp(li.base,"USD") != 0 && strcmp("USD",li.rel) != 0 && strcmp(li.base,"CNY") != 0 && strcmp("CNY",li.rel) != 0 && strcmp(li.exchange,"DEX") != 0 ) // filter out most invalids { printf("unsupported base/rel %s/%s\n",li.base,li.rel); return; } for (i=0; ilinfos)/sizeof(*myinfo->linfos); i++) { refli = myinfo->linfos[i]; if ( strcmp(li.rel,refli.base) == 0 && strcmp(li.base,refli.rel) == 0 ) { /*li = refli; strcpy(li.base,refli.base); strcpy(li.rel,refli.rel); if ( fabs(li.refprice) > SMALLVAL ) li.refprice = (1. / li.refprice); else li.refprice = 0.; li.dir = -li.dir; myinfo->linfos[i] = li;*/ printf("cant Set rev linfo[%d] (%s/%s) %.6f %.8f already have (%s/%s)\n",i,li.rel,li.base,li.profit,li.refprice,refli.base,refli.rel); return; } else if ( refli.base[0] == 0 || (strcmp(li.base,refli.base) == 0 && strcmp(li.rel,refli.rel) == 0 && strcmp(li.exchange,refli.exchange) == 0) ) { if ( refli.base[0] == 0 && li.exchange[0] != 0 && strcmp(li.exchange,"DEX") != 0 ) { if ( strcmp("NXT",li.rel) == 0 && li.assetid != 0 ) { sprintf(numstr,"%llu",(long long)li.assetid); printf("monitor %s %s\n",li.rel,numstr); tradebot_monitor(myinfo,0,0,0,li.exchange,numstr,li.rel,0.); } else tradebot_monitor(myinfo,0,0,0,li.exchange,li.base,li.rel,0.); } myinfo->linfos[i] = li; printf("Set linfo[%d] %s (%s/%s) profitmargin %.6f bid %.8f ask %.8f minvol %.6f maxvol %.6f ref %.8f <- (%s)\n",i,li.exchange,li.base,li.rel,li.profit,li.bid,li.ask,li.minvol,li.maxvol,li.refprice,jprint(vals,0)); return; } } printf("ERROR: too many linfos %d\n",i); } int32_t _default_volume_ok(struct supernet_info *myinfo,struct liquidity_info *li,int32_t dir,double volume) { printf("minvol %f maxvol %f vs volume %f\n",li->minvol,li->maxvol,volume); if ( (li->minvol == 0 || volume >= li->minvol) && (li->maxvol == 0 || volume <= li->maxvol) ) return(0); else return(-1); } double _default_liquidity_active(struct supernet_info *myinfo,double *refpricep,char *exchange,char *base,char *rel,double destvolume) { int32_t i,dir; struct liquidity_info refli; *refpricep = 0.; for (i=0; ilinfos)/sizeof(*myinfo->linfos); i++) { refli = myinfo->linfos[i]; if ( refli.base[0] == 0 ) continue; if ( strcmp(base,refli.base) == 0 && strcmp(rel,refli.rel) == 0 ) dir = 1; else if ( strcmp(rel,refli.base) == 0 && strcmp(base,refli.rel) == 0 ) dir = -1; else continue; if ( exchange[0] != 0 && refli.exchange[0] != 0 && strcmp(exchange,refli.exchange) != 0 ) { printf("continue %s %s/%s [%d] dir.%d vs %s %s/%s\n",exchange,base,rel,i,dir,refli.exchange,refli.base,refli.rel); continue; } if ( _default_volume_ok(myinfo,&refli,dir,destvolume) == 0 ) { if ( refli.profit != 0. ) *refpricep = refli.refprice; else if ( dir > 0 ) *refpricep = refli.bid; else if ( dir < 0 ) *refpricep = refli.ask; printf(">>>>>>>> %s %s/%s [%d] dir.%d vs %s/%s -> %.8f margin %f\n",exchange,base,rel,i,dir,refli.base,refli.rel,*refpricep,refli.profit); return(refli.profit); } break; } return(0.); } struct liquidity_info *_default_lifind(struct supernet_info *myinfo,int32_t *dirp,char *base,char *rel) { struct liquidity_info *li; int32_t i; *dirp = 0; for (i=0; ilinfos)/sizeof(*myinfo->linfos); i++) { li = &myinfo->linfos[i]; if ( strcmp(li->base,base) == 0 && strcmp(li->rel,rel) == 0 ) { *dirp = 1; return(li); } else if ( strcmp(li->base,rel) == 0 && strcmp(li->rel,base) == 0 ) { *dirp = -1; return(li); } } return(0); } void _default_swap_balancingtrade(struct supernet_info *myinfo,struct basilisk_swap *swap,int32_t iambob) { // update balance, compare to target balance, issue balancing trade via central exchanges, if needed struct liquidity_info *li; double vol,price,volume,srcamount,destamount,profitmargin,dir=0.,dotrade=1.; char base[64],rel[64]; int32_t idir; char *tradestr=0; cJSON *tradejson; srcamount = swap->I.req.srcamount; destamount = swap->I.req.destamount; profitmargin = (double)swap->I.req.profitmargin / 1000000.; if ( srcamount <= SMALLVAL || destamount <= SMALLVAL ) { printf("illegal amount for balancing %f %f\n",srcamount,destamount); return; } if ( (li= _default_lifind(myinfo,&idir,swap->I.req.src,swap->I.req.dest)) != 0 ) { if ( idir < 0 ) vol = 1. / ((double)destamount / SATOSHIDEN); else vol = ((double)srcamount / SATOSHIDEN); li->totalvol -= vol; if ( li->totalvol <= 0. || (li->minvol != 0. && li->totalvol < li->minvol) ) li->minvol = li->maxvol = li->totalvol = 0.; printf("li.(%s/%s) totalvol %f after -= %f minmax.(%f %f)\n",li->base,li->rel,li->totalvol,vol,li->minvol,li->maxvol); } strcpy(rel,"BTC"); if ( strcmp(swap->I.req.src,"BTC") == 0 ) { strcpy(base,swap->I.req.dest); price = (srcamount / destamount); volume = destamount / SATOSHIDEN; dir = -1.; } else if ( strcmp(swap->I.req.dest,"BTC") == 0 ) { strcpy(base,swap->I.req.src); price = (destamount / srcamount); volume = srcamount / SATOSHIDEN; dir = 1.; } else { printf("only BTC trades can be balanced, not (%s/%s)\n",swap->I.req.src,swap->I.req.dest); return; } if ( iambob != 0 ) { if ( myinfo->IAMLP != 0 ) { printf("BOB: price %f * vol %f -> %s newprice %f margin %.2f%%\n",price,volume,dir < 0. ? "buy" : "sell",price + dir * price * profitmargin,100*profitmargin); if ( dir < 0. ) tradestr = InstantDEX_buy(myinfo,0,0,0,"bittrex",base,rel,price,volume,dotrade); else tradestr = InstantDEX_sell(myinfo,0,0,0,"bittrex",base,rel,price,volume,dotrade); } } else { if ( myinfo->IAMLP != 0 ) { printf("ALICE: price %f * vol %f -> %s newprice %f margin %.2f%%\n",price,volume,dir > 0. ? "buy" : "sell",price - dir * price * profitmargin,100*profitmargin); if ( dir > 0. ) tradestr = InstantDEX_buy(myinfo,0,0,0,"bittrex",base,rel,price,volume,dotrade); else tradestr = InstantDEX_sell(myinfo,0,0,0,"bittrex",base,rel,price,volume,dotrade); } } if ( tradestr != 0 ) { if ( (tradejson= cJSON_Parse(tradestr)) != 0 ) { if ( jobj(tradejson,"error") == 0 ) // balancing is opposite trade tradebot_pendingadd(myinfo,tradejson,swap->I.req.dest,destamount,swap->I.req.src,srcamount); else free_json(tradejson); } free(tradestr); } } void tradebot_swap_balancingtrade(struct supernet_info *myinfo,struct basilisk_swap *swap,int32_t iambob) { printf("balancing trade\n"); return; if ( swap->balancingtrade == 0 ) _default_swap_balancingtrade(myinfo,swap,iambob); else (*swap->balancingtrade)(myinfo,swap,iambob); } void tradebot_liquidity_command(struct supernet_info *myinfo,char *base,bits256 hash,cJSON *vals) { // processed in LIFO manner which allows to override existing command if ( myinfo->liquidity_command == 0 ) _default_liquidity_command(myinfo,base,hash,vals); else (*myinfo->liquidity_command)(myinfo,base,hash,vals); } double tradebot_liquidity_active(struct supernet_info *myinfo,double *refpricep,char *exchange,char *base,char *rel,double destvolume) { if ( myinfo->liquidity_active == 0 ) return(_default_liquidity_active(myinfo,refpricep,exchange,base,rel,destvolume)); else return((*myinfo->liquidity_active)(myinfo,refpricep,exchange,base,rel,destvolume)); } // struct exchange_quote { uint64_t satoshis,orderid,offerNXT,exchangebits; double price,volume; uint32_t timestamp,val; }; void tradebots_processprices(struct supernet_info *myinfo,struct exchange_info *exchange,char *base,char *rel,struct exchange_quote *bidasks,int32_t numbids,int32_t numasks) { double price,profitmargin=0.,volume; struct tradebot_arbpair *pair; if ( strcmp(rel,"NXT") == 0 && strcmp(base,"BTC") != 0 && (base= NXT_assetnamefind(base)) == 0 ) { //printf("reject %s %s/%s\n",exchange,base,rel); return; } else if ( strcmp(base,"NXT") == 0 && strcmp(rel,"BTC") != 0 && (rel= NXT_assetnamefind(rel)) == 0 ) { //printf("reject %s %s/%s\n",exchange,base,rel); return; } printf("%s %s/%s bids.%d asks.%d\n",exchange->name,base,rel,numbids,numasks); if ( numbids > 0 && (volume= bidasks[0].volume) > 0. && (profitmargin= tradebot_liquidity_active(myinfo,&price,exchange->name,base,rel,volume)) > 0. ) { if ( price == 0. ) price = bidasks[0].price; tradebot_arbcandidate(myinfo,exchange->name,1,base,rel,price,volume,(uint32_t)time(NULL),profitmargin); } if ( numasks > 0 && (volume= bidasks[1].volume) > 0. && (profitmargin= tradebot_liquidity_active(myinfo,&price,exchange->name,rel,base,volume)) > 0. ) { if ( price == 0. ) price = bidasks[1].price; tradebot_arbcandidate(myinfo,exchange->name,-1,base,rel,price,volume,(uint32_t)time(NULL),profitmargin); } if ( (pair= tradebots_arbpair_find(base,rel)) == 0 ) pair = tradebots_arbpair_create(base,rel); if ( pair != 0 ) { if ( strcmp(rel,"NXT") == 0 ) { if ( pair->btcbid != 0. && pair->btcask != 0. ) { tradebot_arbcandidate(myinfo,"arb",1,base,"BTC",pair->btcbid,pair->btcvol,(uint32_t)time(NULL),profitmargin); tradebot_arbcandidate(myinfo,"arb",-1,base,"BTC",pair->btcask,pair->btcvol,(uint32_t)time(NULL),profitmargin); } } if ( strcmp(rel,"USD") != 0 && pair->usdbid != 0. && pair->usdask != 0. ) { tradebot_arbcandidate(myinfo,"arb",1,base,"USD",pair->usdbid,pair->usdvol,(uint32_t)time(NULL),profitmargin); tradebot_arbcandidate(myinfo,"arb",-1,base,"USD",pair->usdask,pair->usdvol,(uint32_t)time(NULL),profitmargin); } if ( strcmp(rel,"CNY") != 0 && pair->cnybid != 0. && pair->cnyask != 0. ) { tradebot_arbcandidate(myinfo,"arb",1,base,"CNY",pair->cnybid,pair->cnyvol,(uint32_t)time(NULL),profitmargin); tradebot_arbcandidate(myinfo,"arb",-1,base,"CNY",pair->cnyask,pair->cnyvol,(uint32_t)time(NULL),profitmargin); } } }