diff --git a/iguana/exchanges/LP_commands.c b/iguana/exchanges/LP_commands.c index 8dd2fc418..57718f917 100644 --- a/iguana/exchanges/LP_commands.c +++ b/iguana/exchanges/LP_commands.c @@ -103,9 +103,8 @@ enable(coin)\n\ disable(coin)\n\ inventory(coin)\n\ bestfit(rel, relvolume)\n\ -ordermatch(base, txfee=0, rel, desttxfee=0, price, relvolume=0, txid, vout, feetxid, feevout, duration=3600)\n\ -trade(price, timeout=10, duration=3600, )\n\ -autotrade(base, rel, price, relvolume, timeout=10, duration=3600)\n\ +buy(base, rel, price, relvolume, timeout=10, duration=3600)\n\ +sell(base, rel, price, basevolume, timeout=10, duration=3600)\n\ swapstatus()\n\ swapstatus(requestid, quoteid)\n\ public API:\n \ @@ -212,7 +211,7 @@ dividends(coin, height, )\n\ return(jprint(retjson,1)); } else return(clonestr("{\"error\":\"no price set\"}")); } - else if ( strcmp(method,"ordermatch") == 0 ) + /*else if ( strcmp(method,"ordermatch") == 0 ) { if ( price > SMALLVAL ) return(LP_ordermatch(base,j64bits(argjson,"txfee"),price,jdouble(argjson,"relvolume"),rel,jbits256(argjson,"txid"),jint(argjson,"vout"),jbits256(argjson,"feetxid"),jint(argjson,"feevout"),j64bits(argjson,"desttxfee"),jint(argjson,"duration"))); @@ -233,6 +232,20 @@ dividends(coin, height, )\n\ { return(LP_autotrade(ctx,myipaddr,pubsock,base,rel,price,jdouble(argjson,"relvolume"),jint(argjson,"timeout"),jint(argjson,"duration"))); } else return(clonestr("{\"error\":\"no price set\"}")); + }*/ + else if ( strcmp(method,"buy") == 0 ) + { + if ( price > SMALLVAL ) + { + return(LP_autobuy(ctx,myipaddr,pubsock,base,rel,price,jdouble(argjson,"relvolume"),jint(argjson,"timeout"),jint(argjson,"duration"),jstr(argjson,"gui"))); + } else return(clonestr("{\"error\":\"no price set\"}")); + } + else if ( strcmp(method,"sell") == 0 ) + { + if ( price > SMALLVAL ) + { + return(LP_autosell(ctx,myipaddr,pubsock,base,rel,price,jdouble(argjson,"basevolume"),jint(argjson,"timeout"),jint(argjson,"duration"))); + } else return(clonestr("{\"error\":\"no price set\"}")); } } else if ( rel != 0 && strcmp(method,"bestfit") == 0 ) diff --git a/iguana/exchanges/LP_ordermatch.c b/iguana/exchanges/LP_ordermatch.c index 255ac95ca..30d656dc5 100644 --- a/iguana/exchanges/LP_ordermatch.c +++ b/iguana/exchanges/LP_ordermatch.c @@ -572,6 +572,17 @@ int32_t LP_tradecommand(void *ctx,char *myipaddr,int32_t pubsock,cJSON *argjson, return(retval); } +char *LP_bestfit(char *rel,double relvolume) +{ + struct LP_utxoinfo *autxo; + if ( relvolume <= 0. || LP_priceinfofind(rel) == 0 ) + return(clonestr("{\"error\":\"invalid parameter\"}")); + if ( (autxo= LP_utxo_bestfit(rel,SATOSHIDEN * relvolume)) == 0 ) + return(clonestr("{\"error\":\"cant find utxo that is big enough\"}")); + return(jprint(LP_utxojson(autxo),1)); +} + +#ifdef oldway struct LP_utxoinfo *LP_bestutxo(double *ordermatchpricep,int64_t *bestsatoshisp,int64_t *bestdestsatoshisp,struct LP_utxoinfo *autxo,char *base,double maxprice,int32_t duration,uint64_t txfee,uint64_t desttxfee,uint64_t maxdestsatoshis) { int64_t satoshis,destsatoshis; uint64_t val,val2; bits256 txid,pubkey; char *obookstr; cJSON *orderbook,*asks,*item; struct LP_utxoinfo *butxo,*bestutxo = 0; int32_t i,n,j,vout,numasks; double bestmetric=0.,metric,vol,price,qprice,bestprice = 0.; struct LP_pubkeyinfo *pubp; @@ -701,16 +712,6 @@ struct LP_utxoinfo *LP_bestutxo(double *ordermatchpricep,int64_t *bestsatoshisp, return(bestutxo); } -char *LP_bestfit(char *rel,double relvolume) -{ - struct LP_utxoinfo *autxo; - if ( relvolume <= 0. || LP_priceinfofind(rel) == 0 ) - return(clonestr("{\"error\":\"invalid parameter\"}")); - if ( (autxo= LP_utxo_bestfit(rel,SATOSHIDEN * relvolume)) == 0 ) - return(clonestr("{\"error\":\"cant find utxo that is big enough\"}")); - return(jprint(LP_utxojson(autxo),1)); -} - char *LP_ordermatch(char *base,int64_t txfee,double maxprice,double maxvolume,char *rel,bits256 txid,int32_t vout,bits256 feetxid,int32_t feevout,int64_t desttxfee,int32_t duration) { struct LP_quoteinfo Q; int64_t bestsatoshis=0,bestdestsatoshis = 0; double ordermatchprice = 0.; struct LP_utxoinfo *autxo,*bestutxo; @@ -727,6 +728,37 @@ char *LP_ordermatch(char *base,int64_t txfee,double maxprice,double maxvolume,ch return(jprint(LP_quotejson(&Q),1)); } +char *LP_autotrade(void *ctx,char *myipaddr,int32_t mypubsock,char *base,char *rel,double maxprice,double relvolume,int32_t timeout,int32_t duration) +{ + uint64_t desttxfee,txfee; int64_t bestsatoshis=0,bestdestsatoshis=0; struct LP_utxoinfo *autxo,*butxo,*bestutxo = 0; double qprice,ordermatchprice=0.; struct LP_quoteinfo Q; + if ( duration <= 0 ) + duration = LP_ORDERBOOK_DURATION; + if ( timeout <= 0 ) + timeout = LP_AUTOTRADE_TIMEOUT; + if ( maxprice <= 0. || relvolume <= 0. || LP_priceinfofind(base) == 0 || LP_priceinfofind(rel) == 0 ) + return(clonestr("{\"error\":\"invalid parameter\"}")); + if ( (autxo= LP_utxo_bestfit(rel,SATOSHIDEN * relvolume)) == 0 ) + return(clonestr("{\"error\":\"cant find utxo that is big enough\"}")); + LP_txfees(&txfee,&desttxfee,base,rel); + if ( (bestutxo= LP_bestutxo(&ordermatchprice,&bestsatoshis,&bestdestsatoshis,autxo,base,maxprice,duration,txfee,desttxfee,SATOSHIDEN*relvolume)) == 0 || ordermatchprice == 0. || bestdestsatoshis == 0 ) + { + printf("bestutxo.%p ordermatchprice %.8f bestdestsatoshis %.8f\n",bestutxo,ordermatchprice,dstr(bestdestsatoshis)); + return(clonestr("{\"error\":\"cant find ordermatch utxo\"}")); + } + if ( LP_quoteinfoinit(&Q,bestutxo,rel,ordermatchprice,bestsatoshis,bestdestsatoshis) < 0 ) + return(clonestr("{\"error\":\"cant set ordermatch quote\"}")); + if ( LP_quotedestinfo(&Q,autxo->payment.txid,autxo->payment.vout,autxo->fee.txid,autxo->fee.vout,LP_mypub25519,autxo->coinaddr) < 0 ) + return(clonestr("{\"error\":\"cant set ordermatch quote info\"}")); + if ( (qprice= LP_quote_validate(&autxo,&butxo,&Q,0)) <= SMALLVAL ) + { + printf("quote validate error %.0f\n",qprice); + return(clonestr("{\"error\":\"quote validation error\"}")); + } + printf("do quote.(%s)\n",jprint(LP_quotejson(&Q),1)); + return(LP_trade(ctx,myipaddr,mypubsock,&Q,maxprice,timeout,duration)); +} +#endif + char *LP_trade(void *ctx,char *myipaddr,int32_t mypubsock,struct LP_quoteinfo *qp,double maxprice,int32_t timeout,int32_t duration) { struct LP_utxoinfo *bobutxo,*aliceutxo; cJSON *bestitem=0; int32_t DEXselector=0; uint32_t expiration; double price; struct LP_pubkeyinfo *pubp; @@ -784,9 +816,132 @@ char *LP_trade(void *ctx,char *myipaddr,int32_t mypubsock,struct LP_quoteinfo *q return(jprint(bestitem,0)); } -char *LP_autotrade(void *ctx,char *myipaddr,int32_t mypubsock,char *base,char *rel,double maxprice,double relvolume,int32_t timeout,int32_t duration) +int32_t LP_nearest_utxovalue(struct LP_address_utxo **utxos,int32_t n,uint64_t targetval) { - uint64_t desttxfee,txfee; int64_t bestsatoshis=0,bestdestsatoshis=0; struct LP_utxoinfo *autxo,*butxo,*bestutxo = 0; double qprice,ordermatchprice=0.; struct LP_quoteinfo Q; + int32_t i,mini = -1; int64_t dist; uint64_t mindist = (1LL << 60); + for (i=0; iU.value - targetval); + //printf("(%.8f %.8f %.8f).%d ",dstr(values[i]),dstr(dist),dstr(mindist),mini); + if ( dist >= 0 && dist < mindist ) + { + mini = i; + mindist = dist; + } + } + } + return(mini); +} + +struct LP_utxoinfo *LP_address_utxopair(struct LP_utxoinfo *utxo,struct LP_address_utxo **utxos,int32_t max,struct iguana_info *coin,char *coinaddr,uint64_t txfee,double relvolume,double price) +{ + struct LP_address *ap; uint64_t targetval; int32_t m,mini; struct LP_address_utxo *up,*up2; + if ( (ap= LP_addressfind(coin,coinaddr)) != 0 ) + { + if ( (m= LP_address_utxo_ptrs(utxos,max,ap)) > 1 ) + { + targetval = SATOSHIDEN * (relvolume / price) + 2*txfee; + if ( (mini= LP_nearest_utxovalue(utxos,m,targetval)) >= 0 ) + { + up = utxos[mini]; + utxos[mini] = 0; + targetval = (targetval / 8) * 9 + 2*txfee; + if ( (mini= LP_nearest_utxovalue(utxos,m,targetval)) >= 0 ) + { + if ( up != 0 && (up2= utxos[mini]) != 0 ) + { + safecopy(utxo->coin,coin->symbol,sizeof(utxo->coin)); + safecopy(utxo->coinaddr,coinaddr,sizeof(utxo->coinaddr)); + utxo->payment.txid = up->U.txid; + utxo->payment.vout = up->U.vout; + utxo->payment.value = up->U.value; + utxo->iambob = 1; + utxo->deposit.txid = up2->U.txid; + utxo->deposit.vout = up2->U.vout; + utxo->deposit.value = up2->U.value; + utxo->S.satoshis = SATOSHIDEN * (relvolume / price); + return(utxo); + } + } + } + } + } + return(0); +} + +struct LP_utxoinfo *LP_buyutxo(struct LP_utxoinfo *bestutxo,double *ordermatchpricep,int64_t *bestsatoshisp,int64_t *bestdestsatoshisp,struct LP_utxoinfo *autxo,char *base,double maxprice,int32_t duration,uint64_t txfee,uint64_t desttxfee,double relvolume,char *gui) +{ + bits256 pubkey; char *obookstr,coinaddr[64]; cJSON *orderbook,*asks,*item; int32_t i,n,numasks,max = 1000; struct LP_address_utxo **utxos; double minvol,maxvol,price; struct LP_pubkeyinfo *pubp; struct iguana_info *basecoin; + *ordermatchpricep = 0.; + *bestsatoshisp = *bestdestsatoshisp = 0; + basecoin = LP_coinfind(base); + if ( duration <= 0 ) + duration = LP_ORDERBOOK_DURATION; + if ( maxprice <= 0. || LP_priceinfofind(base) == 0 || basecoin == 0 ) + return(0); + utxos = calloc(max,sizeof(*utxos)); + LP_txfees(&txfee,&desttxfee,base,autxo->coin); + if ( (obookstr= LP_orderbook(base,autxo->coin,duration)) != 0 ) + { + if ( (orderbook= cJSON_Parse(obookstr)) != 0 ) + { + if ( (asks= jarray(&numasks,orderbook,"asks")) != 0 ) + { + for (i=0; i 0 && price <= maxprice ) + { + pubkey = jbits256(item,"pubkey"); + if ( bits256_cmp(pubkey,LP_mypub25519) != 0 && (pubp= LP_pubkeyadd(pubkey)) != 0 ) + { + if ( (n= jint(item,"numutxos")) > 1 ) + { + minvol = jdouble(item,"minvolume"); + maxvol = jdouble(item,"maxvolume"); + if ( relvolume >= minvol && relvolume <= maxvol ) + { + bitcoin_address(coinaddr,basecoin->taddr,basecoin->pubtype,pubp->rmd160,sizeof(pubp->rmd160)); + if ( (bestutxo= LP_address_utxopair(bestutxo,utxos,max,basecoin,coinaddr,txfee,relvolume,price)) != 0 ) + { + bestutxo->pubkey = pubp->pubkey; + safecopy(bestutxo->gui,gui,sizeof(bestutxo->gui)); + *bestsatoshisp = bestutxo->S.satoshis; + *ordermatchpricep = price; + *bestdestsatoshisp = autxo->S.satoshis; + printf("ordermatch %.8f %.8f %.8f\n",price,dstr(*bestsatoshisp),dstr(*bestdestsatoshisp)); + break; + } + } + } + } else printf("self trading or blacklisted peer\n"); + } + else + { + if ( i == 0 ) + printf("too expensive maxprice %.8f vs %.8f\n",maxprice,price); + break; + } + } + } + free_json(orderbook); + } + free(obookstr); + } + free(utxos); + if ( *ordermatchpricep == 0. || *bestdestsatoshisp == 0 ) + return(0); + int32_t changed; + LP_mypriceset(&changed,autxo->coin,base,1. / *ordermatchpricep); + return(bestutxo); +} + +char *LP_autobuy(void *ctx,char *myipaddr,int32_t mypubsock,char *base,char *rel,double maxprice,double relvolume,int32_t timeout,int32_t duration,char *gui) +{ + uint64_t desttxfee,txfee; int64_t bestsatoshis=0,bestdestsatoshis=0; struct LP_utxoinfo *autxo,*butxo,_best,*bestutxo = 0; double qprice,ordermatchprice=0.; struct LP_quoteinfo Q; if ( duration <= 0 ) duration = LP_ORDERBOOK_DURATION; if ( timeout <= 0 ) @@ -796,7 +951,39 @@ char *LP_autotrade(void *ctx,char *myipaddr,int32_t mypubsock,char *base,char *r if ( (autxo= LP_utxo_bestfit(rel,SATOSHIDEN * relvolume)) == 0 ) return(clonestr("{\"error\":\"cant find utxo that is big enough\"}")); LP_txfees(&txfee,&desttxfee,base,rel); - if ( (bestutxo= LP_bestutxo(&ordermatchprice,&bestsatoshis,&bestdestsatoshis,autxo,base,maxprice,duration,txfee,desttxfee,SATOSHIDEN*relvolume)) == 0 || ordermatchprice == 0. || bestdestsatoshis == 0 ) + memset(&_best,0,sizeof(_best)); + if ( (bestutxo= LP_buyutxo(&_best,&ordermatchprice,&bestsatoshis,&bestdestsatoshis,autxo,base,maxprice,duration,txfee,desttxfee,relvolume,gui)) == 0 || ordermatchprice == 0. || bestdestsatoshis == 0 ) + { + printf("bestutxo.%p ordermatchprice %.8f bestdestsatoshis %.8f\n",bestutxo,ordermatchprice,dstr(bestdestsatoshis)); + return(clonestr("{\"error\":\"cant find ordermatch utxo\"}")); + } + if ( LP_quoteinfoinit(&Q,bestutxo,rel,ordermatchprice,bestsatoshis,bestdestsatoshis) < 0 ) + return(clonestr("{\"error\":\"cant set ordermatch quote\"}")); + if ( LP_quotedestinfo(&Q,autxo->payment.txid,autxo->payment.vout,autxo->fee.txid,autxo->fee.vout,LP_mypub25519,autxo->coinaddr) < 0 ) + return(clonestr("{\"error\":\"cant set ordermatch quote info\"}")); + if ( (qprice= LP_quote_validate(&autxo,&butxo,&Q,0)) <= SMALLVAL ) + { + printf("quote validate error %.0f\n",qprice); + return(clonestr("{\"error\":\"quote validation error\"}")); + } + printf("do quote.(%s)\n",jprint(LP_quotejson(&Q),1)); + return(clonestr("{\"result\":\"success\"}")); + //return(LP_trade(ctx,myipaddr,mypubsock,&Q,maxprice,timeout,duration)); +} + +char *LP_autosell(void *ctx,char *myipaddr,int32_t mypubsock,char *base,char *rel,double maxprice,double basevolume,int32_t timeout,int32_t duration) +{ + uint64_t desttxfee,txfee; int64_t bestsatoshis=0,bestdestsatoshis=0; struct LP_utxoinfo *autxo,*butxo,*bestutxo = 0; double qprice,ordermatchprice=0.; struct LP_quoteinfo Q; + if ( duration <= 0 ) + duration = LP_ORDERBOOK_DURATION; + if ( timeout <= 0 ) + timeout = LP_AUTOTRADE_TIMEOUT; + if ( maxprice <= 0. || basevolume <= 0. || LP_priceinfofind(base) == 0 || LP_priceinfofind(rel) == 0 ) + return(clonestr("{\"error\":\"invalid parameter\"}")); + if ( (autxo= LP_utxo_bestfit(base,SATOSHIDEN * basevolume)) == 0 ) + return(clonestr("{\"error\":\"cant find utxo that is big enough\"}")); + LP_txfees(&txfee,&desttxfee,base,rel); + //if ( (bestutxo= LP_bestutxo(&ordermatchprice,&bestsatoshis,&bestdestsatoshis,autxo,base,maxprice,duration,txfee,desttxfee,SATOSHIDEN*basevolume*maxprice)) == 0 || ordermatchprice == 0. || bestdestsatoshis == 0 ) { printf("bestutxo.%p ordermatchprice %.8f bestdestsatoshis %.8f\n",bestutxo,ordermatchprice,dstr(bestdestsatoshis)); return(clonestr("{\"error\":\"cant find ordermatch utxo\"}")); diff --git a/iguana/exchanges/LP_portfolio.c b/iguana/exchanges/LP_portfolio.c index 15263a593..c7fcd1a04 100644 --- a/iguana/exchanges/LP_portfolio.c +++ b/iguana/exchanges/LP_portfolio.c @@ -437,7 +437,7 @@ int32_t LP_portfolio_trade(void *ctx,uint32_t *requestidp,uint32_t *quoteidp,str break; if ( LP_utxo_bestfit(sell->symbol,SATOSHIDEN * relvolume) != 0 ) { - if ( (retstr2= LP_autotrade(ctx,"127.0.0.1",-1,buy->symbol,sell->symbol,maxprice,relvolume,60,24*3600)) != 0 ) + if ( (retstr2= LP_autobuy(ctx,"127.0.0.1",-1,buy->symbol,sell->symbol,maxprice,relvolume,60,24*3600)) != 0 ) { if ( (retjson2= cJSON_Parse(retstr2)) != 0 ) { diff --git a/iguana/exchanges/LP_utxo.c b/iguana/exchanges/LP_utxo.c index 718c46673..cff304b07 100644 --- a/iguana/exchanges/LP_utxo.c +++ b/iguana/exchanges/LP_utxo.c @@ -131,6 +131,21 @@ int32_t LP_address_minmax(uint64_t *minp,uint64_t *maxp,struct LP_address *ap) return(n); } +int32_t LP_address_utxo_ptrs(struct LP_address_utxo **utxos,int32_t max,struct LP_address *ap) +{ + struct LP_address_utxo *up,*tmp; int32_t n = 0; + DL_FOREACH_SAFE(ap->utxos,up,tmp) + { + if ( up->spendheight <= 0 ) + { + utxos[n++] = up; + if ( n >= max ) + break; + } + } + return(n); +} + int32_t LP_address_utxoadd(struct iguana_info *coin,char *coinaddr,bits256 txid,int32_t vout,uint64_t value,int32_t height,int32_t spendheight) { struct LP_address *ap; struct LP_address_utxo *up,*tmp; int32_t flag,retval = 0; diff --git a/iguana/exchanges/buy b/iguana/exchanges/buy new file mode 100755 index 000000000..4bed65f19 --- /dev/null +++ b/iguana/exchanges/buy @@ -0,0 +1,2 @@ +source userpass +curl --url "http://127.0.0.1:7783" --data "{\"userpass\":\"$userpass\",\"method\":\"buy\",\"base\":\"KMD\",\"rel\":\"BTC\",\"relvolume\":0.005,\"price\":0.0005}" diff --git a/iguana/exchanges/install b/iguana/exchanges/install index 1b7a629d9..bda13d6ef 100755 --- a/iguana/exchanges/install +++ b/iguana/exchanges/install @@ -1,4 +1,4 @@ -cp electrum snapshot_balance snapshot_loop secretaddresses dividends snapshot goals goal portfolio autoprice deletemessages getmessages debug register registerall trade ordermatch bestfit orderbook autotrade client run_osx client_osx run coins disable enable myprice myprices getcoins getpeers getpeersIP getprices getutxos help inv setprice status utxos ../dexscripts +cp electrum snapshot_balance snapshot_loop secretaddresses dividends snapshot goals goal portfolio autoprice deletemessages getmessages debug register registerall buy sell bestfit orderbook client run_osx client_osx run coins disable enable myprice myprices getcoins getpeers getpeersIP getprices getutxos help inv setprice status utxos ../dexscripts cd ../dexscripts #cp ../exchanges/passphrase ../exchanges/userpass . echo you will need to have a passphrase file with your passphrase and userpass file with userpass value in dexscripts dir diff --git a/iguana/exchanges/sell b/iguana/exchanges/sell new file mode 100755 index 000000000..5f48ff299 --- /dev/null +++ b/iguana/exchanges/sell @@ -0,0 +1,2 @@ +source userpass +curl --url "http://127.0.0.1:7783" --data "{\"userpass\":\"$userpass\",\"method\":\"sell\",\"base\":\"KMD\",\"rel\":\"BTC\",\"basevolume\":10.0\"price\":0.0005}"