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.
 
 
 
 
 
 

829 lines
42 KiB

/******************************************************************************
* Copyright © 2014-2016 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. *
* *
******************************************************************************/
#include "peggy.h"
uint64_t peggy_smooth_coeffs[PEGGY_NUMCOEFFS] = // numprimes.13
{
962714545, 962506087, 962158759, 961672710, 961048151, 960285354, 959384649, 958346426, 957171134, // x.8
955859283, 954411438, 952828225, 951110328, 949258485, 947273493, 945156207, 942907532, 940528434, // x.17
938019929, 935383089, 932619036, 929728945, 926714044, 923575608, 920314964, 916933485, 913432593, // x.26
909813756, 906078486, 902228342, 898264923, 894189872, 890004874, 885711650, 881311964, 876807614, // x.35
872200436, 867492300, 862685110, 857780804, 852781347, 847688737, 842505000, 837232189, 831872382, // x.44
826427681, 820900212, 815292123, 809605581, 803842772, 798005901, 792097186, 786118864, 780073180, // x.53
773962395, 767788778, 761554609, 755262175, 748913768, 742511686, 736058231, 729555707, 723006417, // x.62
716412665, 709776755, 703100984, 696387648, 689639036, 682857428, 676045100, 669204315, 662337327, // x.71
655446378, 648533696, 641601496, 634651978, 627687325, 620709702, 613721256, 606724115, 599720386, // x.80
592712154, 585701482, 578690411, 571680955, 564675105, 557674825, 550682053, 543698699, 536726645, // x.89
529767743, 522823816, 515896658, 508988029, 502099660, 495233249, 488390461, 481572928, 474782249, // x.98
468019988, 461287675, 454586804, 447918836, 441285195, 434687268, 428126409, 421603932, 415121117, // x.107
408679208, 402279408, 395922888, 389610779, 383344175, 377124134, 370951677, 364827785, 358753406, // x.116
352729449, 346756785, 340836251, 334968645, 329154729, 323395230, 317690838, 312042206, 306449955, // x.125
300914667, 295436891, 290017141, 284655897, 279353604, 274110676, 268927490, 263804394, 258741701, // x.134
253739694, 248798623, 243918709, 239100140, 234343077, 229647649, 225013957, 220442073, 215932043, // x.143
211483883, 207097585, 202773112, 198510404, 194309373, 190169909, 186091877, 182075118, 178119452, // x.152
174224676, 170390565, 166616873, 162903335, 159249664, 155655556, 152120688, 148644718, 145227287, // x.161
141868021, 138566528, 135322401, 132135218, 129004542, 125929924, 122910901, 119946997, 117037723, // x.170
114182582, 111381062, 108632643, 105936795, 103292978, 100700645, 98159238, 95668194, 93226942, // x.179
90834903, 88491495, 86196126, 83948203, 81747126, 79592292, 77483092, 75418916, 73399150, // x.188
71423178, 69490383, 67600142, 65751837, 63944844, 62178541, 60452305, 58765515, 57117547, // x.197
55507781, 53935597, 52400377, 50901505, 49438366, 48010349, 46616844, 45257246, 43930951, // x.206
42637360, 41375878, 40145912, 38946876, 37778185, 36639262, 35529533, 34448428, 33395384, // x.215
32369842, 31371249, 30399057, 29452725, 28531717, 27635503, 26763558, 25915365, 25090413, // x.224
24288196, 23508216, 22749980, 22013003, 21296806, 20600917, 19924870, 19268206, 18630475, // x.233
18011231, 17410035, 16826458, 16260073, 15710466, 15177224, 14659944, 14158231, 13671694, // x.242
13199950, 12742625, 12299348, 11869759, 11453500, 11050225, 10659590, 10281262, 9914910, // x.251
9560213, 9216856, 8884529, 8562931, 8251764, 7950739, 7659571, 7377984, 7105706, // x.260
6842471, 6588020, 6342099, 6104460, 5874861, 5653066, 5438844, 5231969, 5032221, // x.269
4839386, 4653254, 4473620, 4300287, 4133059, 3971747, 3816167, 3666139, 3521488, // x.278
3382043, 3247640, 3118115, 2993313, 2873079, 2757266, 2645728, 2538325, 2434919, // x.287
2335380, 2239575, 2147382, 2058677, 1973342, 1891262, 1812325, 1736424, 1663453, // x.296
1593311, 1525898, 1461118, 1398879, 1339091, 1281666, 1226519, 1173569, 1122736, // x.305
1073944, 1027117, 982185, 939076, 897725, 858065, 820033, 783568, 748612, // x.314
715108, 682999, 652233, 622759, 594527, 567488, 541597, 516808, 493079, // x.323
470368, 448635, 427841, 407948, 388921, 370725, 353326, 336692, 320792, // x.332
305596, 291075, 277202, 263950, 251292, 239204, 227663, 216646, 206130, // x.341
196094, 186517, 177381, 168667, 160356, 152430, 144874, 137671, 130806, // x.350
124264, 118031, 112093, 106437, 101050, 95921, 91039, 86391, 81968, // x.359
77759, 73755, 69945, 66322, 62877, 59602, 56488, 53528, 50716, // x.368
48043, 45505, 43093, 40803, 38629, 36564, 34604, 32745, 30980, // x.377
29305, 27717, 26211, 24782, 23428, 22144, 20927, 19774, 18681, // x.386
17646, 16665, 15737, 14857, 14025, 13237, 12491, 11786, 11118, // x.395
10487, 9890, 9325, 8791, 8287, 7810, 7359, 6933, 6531, // x.404
6151, 5792, 5453, 5133, 4831, 4547, 4278, 4024, 3785, // x.413
3560, 3347, 3147, 2958, 2779, 2612, 2454, 2305, 2164, // x.422
2032, 1908, 1791, 1681, 1577, 1480, 1388, 1302, 1221, // x.431
1145, 1073, 1006, 942, 883, 827, 775, 725, 679, // x.440
636, 595, 557, 521, 487, 456, 426, 399, 373, // x.449
348, 325, 304, 284, 265, 248, 231, 216, 202, // x.458
188, 175, 164, 153, 142, 133, 124, 115, 107, // x.467
100, 93, 87, 81, 75, 70, 65, 61, 56, // x.476
53, 49, 45, 42, 39, 36, 34, 31, 29, // x.485
27, 25, 23, 22, 20, 19, 17, 16, 15, // x.494
14, 13, 12, 11, 10, 9, 9, 8, 7, // x.503
7, 6, 6, 5, 5, 5, 4, 4, 4, // x.512
3, 3, 3, 3, 2, 2, 2, 2, 2, // x.521
2, 2, 1, 1, 1, 1, 1, 1, 1, // x.530
1, 1, 1, 1, 1, 1, 0, 0, // isum 100000000000
};
int32_t dailyrates[101] =
{
0, 27, 55, 82, 110, 137, 164, 192, 219, 246, 273, 300, 327, 355, 382, 409, 436, 463, 489, 516, 543, 570, 597, 624, 651, 677, 704, 731, 757, 784, 811, 837, 864, 890, 917, 943, 970, 996, 1023, 1049, 1076, 1102, 1128, 1155, 1181, 1207, 1233, 1259, 1286, 1312, 1338, 1364, 1390, 1416, 1442, 1468, 1494, 1520, 1546, 1572, 1598, 1624, 1649, 1675, 1701, 1727, 1752, 1778, 1804, 1830, 1855, 1881, 1906, 1932, 1957, 1983, 2008, 2034, 2059, 2085, 2110, 2136, 2161, 2186, 2212, 2237, 2262, 2287, 2313, 2338, 2363, 2388, 2413, 2438, 2463, 2488, 2513, 2538, 2563, 2588, 2613
};
int32_t peggy_setprice(struct peggy *PEG,struct price_resolution price,int32_t minute)
{
if ( PEG->name.hasprice != 0 )
{
if ( price.Pval > PRICE_RESOLUTION_MAXPVAL )
{
printf("peggy_setdayprice clip.%lld with %lld\n",(long long)price.Pval,(long long)PRICE_RESOLUTION_MAXPVAL);
price.Pval = PRICE_RESOLUTION_MAXPVAL;
}
else if ( price.Pval <= 0 )
{
printf("peggy_setdayprice illegal negative of zeroprice %lld %s\n",(long long)price.Pval,PEG->name.name);
price.Pval = 0;
}
if ( PEG->baseprices[minute] != 0 )
price.Pval = (uint32_t)(price.Pval + PEG->baseprices[minute]) >> 1;
else if ( minute > PEG->RTminute )
PEG->RTminute = minute;
PEG->baseprices[minute] = (uint32_t)price.Pval;
while ( --minute > 0 && PEG->baseprices[minute] == 0 )
PEG->baseprices[minute] = (uint32_t)price.Pval;
}
return(minute);
}
struct price_resolution peggy_shortprice(struct peggy *PEG,struct price_resolution price)
{
struct price_resolution shortprice;
memset(&shortprice,0,sizeof(shortprice));
if ( price.Pval != 0 )
shortprice.Pval = ((PRICE_RESOLUTION * PEG->genesisprice.Pval) / price.Pval);
return(shortprice);
}
struct price_resolution peggy_price(struct peggy *PEG,int32_t minute)
{
struct price_resolution relprice,price;
memset(&price,0,sizeof(price));
if ( minute == PEG->RTminute )
minute--;
while ( (price.Pval= PEG->baseprices[minute]) == 0 && minute >= 0 )
minute--;
if ( PEG->name.hasprice == 0 )
{
relprice.Pval = PEG->relprices[minute];
if ( relprice.Pval != 0 )
{
if ( price.Pval < PRICE_RESOLUTION_MAXPVAL )
price.Pval = (PRICE_RESOLUTION * price.Pval) / relprice.Pval;
else price.Pval = PRICE_RESOLUTION_ROOT * ((PRICE_RESOLUTION_ROOT * price.Pval) / relprice.Pval);
} else price.Pval = 0;
}
return(price);
}
struct price_resolution peggy_aveprice(struct peggy *PEG,int32_t day,int32_t width)
{
int32_t i,n; struct price_resolution price,aveprice;
aveprice.Pval = 0;
for (i=n=0; i<width; i++,day++)
{
price.Pval = PEG->dayprices[day];
if ( price.Pval != 0 )
aveprice.Pval += price.Pval, n++;
}
if ( n != 0 )
aveprice.Pval /= n;
return(aveprice);
}
int32_t peggy_aprpercs(int64_t dailyrate)
{
int32_t i;
if ( dailyrate == PEGGY_RATE_777 )
return(777);
else if ( dailyrate == -PEGGY_RATE_777 )
return(-777);
for (i=0; i<sizeof(dailyrates)/sizeof(*dailyrates)-1; i++)
if ( dailyrate >= dailyrates[i] && dailyrate < dailyrates[i+1] )
return(i*10);
return(0);
}
char *peggy_aprstr(int64_t dailyrate)
{
static char aprstr[16];
int32_t apr,dir = 1;
if ( dailyrate < 0 )
dir = -1, dailyrate = -dailyrate;
apr = peggy_aprpercs(dailyrate);
sprintf(aprstr,"%c%d.%02d%% APR",dir<0?'-':'+',apr/100,(apr%100));
return(aprstr);
}
int64_t peggy_compound(int32_t dispflag,int64_t satoshis,int64_t dailyrate,int32_t n)
{
int32_t i; int64_t compounded = satoshis;
if ( dailyrate == 0 )
return(satoshis);
if ( dispflag != 0 )
printf("peggy_compound rate.%lld n.%d %.8f %lld %lld -> ",(long long)dailyrate,n,dstr(satoshis),(long long)compounded,(long long)SCALED_PRICE(compounded,dailyrate));
for (i=0; i<n; i++)
{
//tmp = (compounded * (PRICE_RESOLUTION + dailyrate)) / PRICE_RESOLUTION; // WARNING: Do not use floating-point math!
compounded += SCALED_PRICE(compounded,dailyrate);
if ( dispflag != 0 )
printf("%.8f ",dstr(compounded));
}
if ( dispflag != 0 )
printf("%.8f %.8f\n",dstr(compounded),(double)compounded/satoshis);
if ( compounded < 0 )
compounded = 0;
return(compounded);
}
void peggy_updatemargin(struct peggy_margin *funds,int64_t satoshis,uint16_t margin,int64_t amount)
{
if ( margin == 0 )
funds->deposits += satoshis;
else
{
funds->margindeposits += amount;
funds->marginvalue += (amount * margin);
}
}
void peggy_thanks_you(struct peggy_info *PEGS,int64_t tip) { PEGS->bank.crypto777_royalty += tip; }
void peggy_changereserve(struct peggy_info *PEGS,struct peggy *PEG,int32_t dir,struct peggy_entry *entry,int64_t satoshis,uint16_t margin,int64_t marginamount)
{
int64_t *dest; uint64_t replenish = 0,fee = 0;
if ( Debuglevel > 2 )
printf("CHANGE %.8f: %c%s: %lld costbasis %.8f %s fee %.8f royalty %.8f estimated %.8f unlocked %.8f, interestpaid %.8f | units.%lld oppo.%lld\n",dstr(satoshis),entry->polarity<0?'-':'+',PEG->name.name,(long long)entry->denomination,dstr(entry->costbasis),peggy_aprstr(entry->dailyrate),dstr(entry->fee),dstr(entry->royalty),dstr(entry->estimated_interest),dstr(entry->interest_unlocked),dstr(entry->interestpaid),(long long)entry->supply.num,(long long)entry->supply.numoppo);
PEGS->bank.APRfund_reserved += (entry->estimated_interest - entry->interest_unlocked);
if ( PEG->pool.funds.deposits > 0 && PEGS->bank.funds.deposits > 0 )
{
if ( PEGS->bank.APRfund < 0 )
PEGS->bank.APRfund += entry->fee;
else
{
fee = entry->fee / PEGS->feediv, fee *= PEGS->feemult;
PEGS->bank.APRfund += ((entry->fee - fee) - entry->interestpaid);
}
} else replenish = entry->fee;
peggy_thanks_you(PEGS,entry->royalty + fee);
peggy_updatemargin(&PEG->pool.funds,satoshis + replenish,margin,marginamount);
peggy_updatemargin(&PEGS->bank.funds,satoshis + replenish,margin,marginamount);
peggy_updatemargin(&PEGS->basereserves[entry->baseid].funds,dir * satoshis,margin,dir * marginamount);
peggy_updatemargin(&PEGS->basereserves[entry->relid].funds,-dir * satoshis,margin,-dir * marginamount);
//printf("dir.%d polarity.%d baseid.%d relid.%d sats %.8f [%.8f %.8f]\n",dir,entry->polarity,entry->baseid,entry->relid,dstr(satoshis),dstr(PEGS->basereserves[entry->baseid]),dstr(PEGS->basereserves[entry->relid]));
dest = (entry->polarity < 0) ? &PEG->pool.liability.numoppo : &PEG->pool.liability.num;
*dest += (dir * entry->denomination);
if ( Debuglevel > 2 || (rand() % 1000) == 0 )
printf(">>>>>>> %c%s %lld: cost %11.8f %s royalties %.8f APR.(R%.8f B%.8f) satoshis %11.8f %s.(+%lld -%lld) base.(%.8f %.8f) total %s (%.8f %.8f %.8f) (%.8f %.8f %.8f)\n",entry->polarity<0?'-':'+',PEG->name.name,(long long)entry->denomination,dstr(entry->costbasis),peggy_aprstr(entry->dailyrate),dstr(PEGS->bank.crypto777_royalty),dstr(PEGS->bank.APRfund_reserved),dstr(PEGS->bank.APRfund),dstr(satoshis),PEG->name.name,(long long)PEG->pool.liability.num,(long long)PEG->pool.liability.numoppo,dstr(PEGS->basereserves[entry->baseid].funds.deposits),dstr(PEGS->basereserves[entry->relid].funds.deposits),PEG->name.name,dstr(PEG->pool.funds.deposits),dstr(PEG->pool.funds.margindeposits),dstr(PEG->pool.funds.marginvalue),dstr(PEGS->bank.funds.deposits),dstr(PEGS->bank.funds.margindeposits),dstr(PEGS->bank.funds.marginvalue));
}
uint64_t peggy_satoshis(int32_t polarity,int16_t denomination,int64_t price,int64_t oppoprice)
{
uint64_t satoshi;
if ( polarity < 0 )
{
//satoshi = (denomination * price) / PRICE_RESOLUTION;
//satoshi = (satoshi * oppoprice) / PRICE_RESOLUTION;
//return((SATOSHIDEN * satoshi) / PRICE_RESOLUTION);
satoshi = (oppoprice * price) / PRICE_RESOLUTION_ROOT;
satoshi = ((SATOSHIDEN / PRICE_RESOLUTION_ROOT) * (satoshi * denomination)) / PRICE_RESOLUTION;
return(satoshi);
//return(denomination * price * oppoprice * (10 / (PRICE_RESOLUTION * PRICE_RESOLUTION * PRICE_RESOLUTION)));
}
else
{
//satoshi = (denomination * price) / PRICE_RESOLUTION;
//return((SATOSHIDEN * satoshi) / PRICE_RESOLUTION);
return((SATOSHIDEN * denomination * price) / PRICE_RESOLUTION);
}
}
uint64_t peggy_poolmainunits(struct peggy_entry *entry,int32_t dir,int32_t polarity,struct price_resolution price,struct price_resolution oppoprice,struct price_resolution spread,uint64_t poolincr,int16_t denomunits)
{
uint64_t mainunits,satoshis; struct price_resolution fee;
entry->denomination = denomunits;
entry->costbasis = peggy_satoshis(polarity,denomunits,price.Pval,oppoprice.Pval);
if ( (entry->baseid != 0 || entry->relid != 0) && spread.Pval != 0 && (fee.Pval= SCALED_PRICE(price.Pval,spread.Pval)) != 0 )
price.Pval += dir * fee.Pval;
else fee.Pval = 0;
satoshis = peggy_satoshis(polarity,denomunits,price.Pval,oppoprice.Pval);
if ( (satoshis % poolincr) == 0 )
dir = 0;
mainunits = dir + (satoshis / poolincr);
entry->total = poolincr * mainunits;
entry->fee = (entry->total - entry->costbasis);
if ( Debuglevel > 2 )
printf("mainunits %llu: origcost %.8f [%.8f] denomination %d poolincr %.8f price %.6f spread %.6f fee %.8f\n",(long long)mainunits,dstr(entry->costbasis),dstr(entry->fee),denomunits,dstr(poolincr),Pval(&price),Pval(&spread),Pval(&fee));
return(entry->total);
}
int64_t peggy_pairabs(int64_t basebalance,int64_t relbalance)
{
int64_t baserelbalance;
if ( (baserelbalance= basebalance) < 0 )
baserelbalance = -baserelbalance;
if ( relbalance < 0 )
baserelbalance -= relbalance;
else baserelbalance += relbalance;
return(baserelbalance);
}
int64_t peggy_lockrate(struct peggy_entry *entry,struct peggy_info *PEGS,struct peggy *PEG,uint64_t satoshis,uint16_t numdays)
{
int64_t diff,compounded,prod,dailyrate=0,both,needed = 0;
if ( (both= entry->supply.num + entry->supply.numoppo) != 0 )
{
if ( (diff= (entry->supply.numoppo - entry->supply.num)) < 0 )
entry->supplydiff = -diff;
else entry->supplydiff = diff;
prod = (PEG->maxdailyrate * diff);
if ( prod > 0 )
prod *= PEGS->posboost;
dailyrate = (prod / both) + dailyrates[PEGS->interesttenths];
if ( dailyrate > PEGGY_RATE_777 )
dailyrate = PEGGY_RATE_777;
else if ( dailyrate < -PEGGY_RATE_777 )
dailyrate = -PEGGY_RATE_777;
if ( prod < 0 && dailyrate > 0 )
dailyrate /= PEGS->negpenalty;
compounded = peggy_compound(0,satoshis,dailyrate,numdays);
needed = (compounded - satoshis);
needed *= 3, needed /= 2;
if ( Debuglevel > 2 )
printf("2x needed %.8f dailyrate.%lld compounded %lld <- %lld diff.%lld prod.%lld both.%lld -> %lld %s\n",dstr(needed),(long long)dailyrate,(long long)compounded,(long long)satoshis,(long long)diff,(long long)prod,(long long)both,(long long)dailyrate,peggy_aprstr(dailyrate));
if ( (PEGS->bank.APRfund_reserved + needed) > PEGS->bank.APRfund )
{
compounded = peggy_compound(1,satoshis,dailyrate,numdays);
printf("reserved %.8f + needed %.8f) > APRfund %.8f\n",dstr(PEGS->bank.APRfund_reserved),dstr(needed),dstr(PEGS->bank.APRfund));
needed = dailyrate = 0;
} else entry->estimated_interest = needed;
}
entry->dailyrate = dailyrate;
return(dailyrate);
}
int32_t peggy_islegal_amount(struct peggy_entry *entry,struct peggy_info *PEGS,struct peggy *PEG,int32_t dir,int64_t satoshis,int32_t numdays,uint16_t margin,struct price_resolution price)
{
uint64_t newsupply; int64_t baserelbalance,newbaserel;
if ( margin == 0 )
entry->dailyrate = peggy_lockrate(entry,PEGS,PEG,satoshis,numdays);
if ( entry->baseid == entry->relid )
{
printf("illegal baseid.%d relid.%d (%s)\n",PEG->name.baseid,PEG->name.relid,PEG->name.name);
//getchar();
return(1);
}
if ( PEG->name.id == 0 )
{
printf("BTCD is legal\n");
return(1);
}
if ( (newsupply= ((PEG->pool.funds.deposits + PEG->pool.funds.marginvalue) + satoshis)) > PEG->maxsupply )
{
printf("peggy_islegal_amount %.8f %.8f newsupply %.8f > limits %.8f\n",dstr(PEG->pool.funds.deposits),dstr(PEG->pool.funds.marginvalue),dstr(newsupply),dstr(PEG->maxsupply));
return(0);
}
//printf("baseid.%d relid.%d\n",entry->baseid,entry->relid);
baserelbalance = peggy_pairabs(PEGS->basereserves[entry->baseid].funds.deposits,PEGS->basereserves[entry->relid].funds.deposits);
newbaserel = peggy_pairabs(PEGS->basereserves[entry->baseid].funds.deposits + satoshis,PEGS->basereserves[entry->relid].funds.deposits - satoshis);
if ( Debuglevel > 2 )
printf("baseid.%d relid.%d satoshis %.8f baserelbalance %.8f newbaserel %.8f\n",entry->baseid,entry->relid,dstr(satoshis),dstr(baserelbalance),dstr(newbaserel));
if ( newbaserel <= baserelbalance )
return(1);
//printf("entry->supplydiff.%lld diff %.8f vs maxnetbalance %.8f\n",(long long)entry->supplydiff,(double)(price.Pval*entry->supplydiff)/PRICE_RESOLUTION,dstr(PEG->limits.maxnetbalance));
if ( PEG->maxnetbalance != 0 && (price.Pval * entry->supplydiff) / PRICE_RESOLUTION > PEG->maxnetbalance/SATOSHIDEN )
{
printf("entry->supplydiff.%lld diff %.8f vs maxnetbalance %.8f\n",(long long)entry->supplydiff,(double)(price.Pval*entry->supplydiff)/PRICE_RESOLUTION,dstr(PEG->maxnetbalance));
return(0);
}
return(1);
}
static uint64_t peggy_assetbits(char *name) { return((is_decimalstr(name) != 0) ? calc_nxt64bits(name) : stringbits(name)); }
struct peggy *peggy_findpair(struct peggy_info *PEGS,char *name)
{
int32_t i; uint64_t assetbits;
if ( (assetbits= peggy_assetbits(name)) != 0 )
{
for (i=0; i<PEGS->numpegs; i++)
{
if ( PEGS->contracts[i]->name.assetbits == assetbits )
return(PEGS->contracts[i]);
}
}
return(0);
}
struct peggy *peggy_found(struct peggy_entry *entry,struct peggy *PEG,int32_t polarity)
{
int64_t num,numoppo;
memset(entry,0,sizeof(*entry));
num = PEG->pool.liability.num, numoppo = PEG->pool.liability.numoppo;
if ( polarity >= 0 )
entry->polarity = 1, entry->baseid = PEG->name.baseid, entry->relid = PEG->name.relid, entry->supply.num = num, entry->supply.numoppo = numoppo;
else entry->polarity = -1, entry->baseid = PEG->name.relid, entry->relid = PEG->name.baseid, entry->supply.num = numoppo, entry->supply.numoppo = num;
//printf("(%s) -> baseid.%d relid.%d\n",PEG->name.name,entry->baseid,entry->relid);
return(PEG);
}
struct peggy *peggy_find(struct peggy_entry *entry,struct peggy_info *PEGS,char *name,int32_t polarity)
{
struct peggy *PEG;
if ( (PEG= peggy_findpair(PEGS,name)) != 0 )
return(peggy_found(entry,PEG,polarity));
return(0);
}
struct peggy *peggy_findpeg(struct peggy_entry *entry,struct peggy_info *PEGS,int32_t peg)
{
if ( peg >= 0 )
return(peggy_found(entry,PEGS->contracts[peg],peg));
else return(peggy_found(entry,PEGS->contracts[-peg],peg));
}
int32_t peggy_pegstr(char *buf,struct peggy_info *PEGS,char *name)
{
int32_t peg=0; struct peggy *PEG;
buf[0] = 0;
if ( name != 0 )
{
strcpy(buf,name);
if ( (PEG= peggy_findpair(PEGS,name)) != 0 )
return(PEG->name.id);
}
return(peg);
}
int32_t peggy_setname(char *buf,char *name)
{
return(peggy_pegstr(buf,opreturns_context("peggy",0),name));
}
int64_t peggy_calcspread(int64_t spread,int32_t lockdays) { return((lockdays < 30) ? spread : ((30 * spread) / lockdays)); }
uint64_t peggy_setunit(struct peggy_unit *U,struct peggy_entry *entry,struct peggy_info *PEGS,struct peggy_time T,struct peggy *PEG,uint64_t seed,uint64_t nxt64bits,struct peggy_lock *lock)
{
int32_t numdays,polarity; uint64_t poolincr; int16_t denom; struct price_resolution spread;
memset(U,0,sizeof(*U));
U->lock = *lock;
if ( (denom= lock->denom) < 0 )
polarity = -1, denom = -denom;
else polarity = 1;
if ( denom >= PRICE_RESOLUTION_MAXUNITS )
{
printf("denom.%d is too big max %lld\n",denom,(long long)PRICE_RESOLUTION_MAXUNITS);
return(0);
}
if ( U->lock.margin != 0 )
{
if ( U->lock.maxlockdays > PEGGY_MARGINLOCKDAYS )
{
printf("error: maxlockdays cant be more than PEGGY_MARGINLOCKDAYS %d for margin trading\n",PEGGY_MARGINLOCKDAYS);
return(0);
}
else if ( U->lock.margin > PEG->lockparms.margin )
{
printf("error: margin.%d cant be more than %d for margin trading %s\n",U->lock.margin,PEG->lockparms.margin,PEG->name.name);
return(0);
}
else if ( denom < U->lock.margin )
{
printf("error: denomination %lld must be >= margin %dx trading %s\n",(long long)denom,U->lock.margin,PEG->name.name);
return(0);
}
else if ( (denom + entry->supply.num) > entry->supply.numoppo )
{
//printf("error: denom.%d supply %d %d > oppo %d -> balance violation %s\n",denom,entry->supply.num,denom+entry->supply.num,entry->supply.numoppo,PEG->name.name);
return(0);
}
U->lock.redemptiongapdays = PEGGY_MARGINGAPDAYS;
} else U->lock.redemptiongapdays = PEG->lockparms.redemptiongapdays;
if ( U->lock.maxlockdays <= PEG->lockparms.maxlockdays )
U->lock.maxlockdays = PEG->lockparms.maxlockdays;
if ( U->lock.minlockdays >= PEG->lockparms.minlockdays )
U->lock.minlockdays = PEG->lockparms.minlockdays;
if ( U->lock.minlockdays > U->lock.maxlockdays )
U->lock.minlockdays = U->lock.maxlockdays;
if ( U->lock.clonesmear > PEG->lockparms.clonesmear )
U->lock.clonesmear = PEG->lockparms.clonesmear;
if ( U->lock.mixrange > PEG->lockparms.mixrange )
U->lock.mixrange = PEG->lockparms.mixrange;
U->lock.extralockdays = PEGGY_MINEXTRADAYS + (seed % PEG->lockparms.extralockdays);
entry->price = PEG->dayprice;//peggy_nonzprice(PEGS,T,PEG,1,day);
entry->oppoprice = peggy_shortprice(PEG,entry->price);//peggy_nonzprice(PEGS,T,PEG,-1,day);
poolincr = PEG->pool.mainunitsize;
U->timestamp = T.blocktimestamp, U->baseid = entry->baseid, U->relid = entry->relid; //, U->nxt64bits = nxt64bits,
U->lock.peg = PEG->name.id * polarity;
spread.Pval = peggy_calcspread(PEG->spread.Pval,U->lock.minlockdays + U->lock.extralockdays);
peggy_poolmainunits(entry,1,polarity,entry->price,entry->oppoprice,spread,poolincr,denom);
U->costbasis = entry->costbasis;
numdays = (polarity > 0) ? (U->lock.maxlockdays + PEG->lockparms.extralockdays) : (U->lock.minlockdays + PEG->lockparms.extralockdays);
return(entry->total * peggy_islegal_amount(entry,PEGS,PEG,polarity,entry->total,numdays,U->lock.margin,entry->price));
}
int64_t peggy_redeemhash(struct peggy_entry *entry,struct peggy_info *PEGS,struct peggy_time T,struct peggy_unit *U,int32_t lockdays)
{
struct price_resolution price,oppoprice,spread; struct peggy *PEG; int32_t n,delta; uint64_t poolincr,interest,satoshis = 0;
if ( (PEG= peggy_findpeg(entry,PEGS,U->lock.peg)) != 0 )
{
poolincr = PEG->pool.mainunitsize, oppoprice.Pval = spread.Pval = 0;
delta = (T.blocktimestamp - U->timestamp) / PEGGY_DAYTICKS;
lockdays += U->lock.extralockdays;
if ( delta < lockdays )
return(0);
else if ( delta > (lockdays + U->lock.redemptiongapdays) )
return(-1);
T.blocktimestamp = U->timestamp + (lockdays * PEGGY_DAYTICKS);
if ( (n= (int32_t)(U->costbasis / PEG->unitincr)) > 0 )
T.blocktimestamp -= (n * PEGGY_DAYTICKS) / 2;
else n = 1;
price = peggy_aveprice(PEG,(T.blocktimestamp - PEG->genesistime) / PEGGY_DAYTICKS,n);
if ( entry->polarity < 0 )
oppoprice = peggy_shortprice(PEG,price);
spread.Pval = peggy_calcspread(PEG->spread.Pval,lockdays);
satoshis = peggy_poolmainunits(entry,-1,entry->polarity,price,oppoprice,spread,poolincr,U->lock.denom);
entry->costbasis = U->costbasis;
if ( U->estimated_interest != 0 )
{
interest = peggy_compound(0,satoshis,U->dailyrate,lockdays + U->lock.extralockdays) - satoshis;
entry->interestpaid = (interest / poolincr) * poolincr;
if ( entry->interestpaid > U->estimated_interest )
entry->interestpaid = U->estimated_interest;
entry->royalty += (interest - entry->interestpaid);
entry->interest_unlocked = U->estimated_interest;
}
if ( U->lock.margin == 0 )
{
if ( PEG->pool.funds.deposits < satoshis )
satoshis = PEG->pool.funds.deposits;
else if (PEG->pool.funds.deposits >= (satoshis + entry->interestpaid) )
satoshis += entry->interestpaid;
}
else
{
if ( PEG->pool.funds.margindeposits < satoshis )
satoshis = PEG->pool.funds.margindeposits;
}
}
return(satoshis);
}
uint64_t peggy_redeem(struct peggy_info *PEGS,struct peggy_time T,int32_t readonly,char *name,int32_t polarity,uint64_t nxt64bits,bits256 pubkey,uint16_t lockdays,uint8_t chainlen)
{
struct peggy_entry entry; struct peggy_unit *U; struct peggy *PEG; int32_t peg; int64_t satoshis = 0; bits256 lockhash;
if ( (PEG= peggy_findpair(PEGS,name)) != 0 )
{
peg = PEG->name.id * polarity;
lockhash = acct777_lockhash(pubkey,lockdays,chainlen);
if ( (U= peggy_match(PEGS->accts,peg,nxt64bits,lockhash,lockdays)) != 0 )
{
if ( (satoshis= peggy_redeemhash(&entry,PEGS,T,U,lockdays)) < 0 )
{
printf("Autopurge unit.%p\n",U);
satoshis = 0;
}
if ( readonly == 0 )
{
peggy_changereserve(PEGS,PEG,-1,&entry,satoshis,U->lock.margin,U->marginamount);
peggy_delete(PEGS->accts,U,satoshis == 0 ? PEGGY_RSTATUS_AUTOPURGED : PEGGY_RSTATUS_REDEEMED);
}
}
}
return(satoshis);
}
uint64_t peggy_createunit(struct peggy_info *PEGS,struct peggy_time T,struct peggy_unit *readU,uint64_t seed,char *name,uint64_t nxt64bits,bits256 lockhash,struct peggy_lock *lock,uint64_t amount,uint64_t marginamount)
{
struct peggy_entry entry; struct peggy_unit U; int32_t peg,polarity; int16_t denomination; int64_t satoshis = 0; struct peggy *PEG = 0;
denomination = lock->denom;
if ( denomination < 0 )
polarity = -1, denomination = -denomination;
else polarity = 1;
if ( denomination >= PRICE_RESOLUTION_MAXUNITS )
{
printf("denomination.%d is too big max %lld\n",denomination,(long long)PRICE_RESOLUTION_MAXUNITS);
return(0);
}
if ( name == 0 )
{
if ( (peg= lock->peg) == 0 )
PEG = PEGS->contracts[0];
else if ( lock->peg < 0 )
peg = -peg, polarity = -polarity;
if ( peg >= PEGS->numpegs )
{
printf("illegal peg id.%d\n",lock->peg);
return(0);
}
PEG = PEGS->contracts[peg];
PEG = peggy_find(&entry,PEGS,PEG->name.name,polarity);
} else PEG = peggy_find(&entry,PEGS,name,polarity);
if ( PEG != 0 )
{
if ( (satoshis= peggy_setunit(&U,&entry,PEGS,T,PEG,seed,nxt64bits,lock)) != 0 )
{
U.estimated_interest = entry.estimated_interest, U.amount = amount, U.marginamount = marginamount;
if ( readU == 0 )
{
if ( Debuglevel > 2 )
printf("%s.%d amount %.8f satoshis %.8f incr.%.8f price.%.8f\n",PEG->name.name,PEG->name.id,dstr(amount),dstr(satoshis),dstr(PEG->pool.mainunitsize),Pval(&entry.price));
if ( (lock->margin == 0 && amount >= satoshis) || (marginamount * lock->margin) >= satoshis )
{
peggy_changereserve(PEGS,PEG,1,&entry,entry.costbasis,lock->margin,marginamount);
peggy_addunit(PEGS->accts,&U,lockhash);
if ( Debuglevel > 2 )
printf("id.%d needed for interest reserved %.8f BTCD, royalty %.8f | dailyrate %lld %s\n",PEG->name.id,dstr(U.estimated_interest),dstr(amount) - dstr(satoshis),(long long)U.dailyrate,peggy_aprstr(U.dailyrate));
} else printf("%s amount %.8f not enough for unit %.8f or margin.%d %llu %.8f\n",PEG->name.name,dstr(amount),dstr(satoshis),lock->margin,(long long)marginamount,dstr(marginamount * lock->margin)), satoshis = 0;
}
}
if ( readU != 0 ) *readU = U, printf("%c%s cost %.8f %s\n",polarity < 0 ? '-' : '+',PEG->name.name,dstr(satoshis),peggy_aprstr(U.dailyrate));
} else printf("peggy_createunit: cant find.(%s)\n",name);
return(satoshis);
}
uint64_t peggy_gamblers(struct peggy_info *PEGS,struct peggy_time T,struct price_resolution prevprice,struct price_resolution newprice,struct peggy_bet *bets,int32_t numbets)
{
int32_t i,j,match,oppo,numbest = 0; int64_t diff,preddiff,tmp,dist,bestdist;
uint64_t bet,payout,totalbets,losingbets,winningbets,totalpayout,matchbets,oppobets,matchshares;
matchshares = matchbets = oppobets = losingbets = winningbets = bestdist = totalpayout = match = oppo = 0;
if ( bets != 0 )
{
for (totalbets=i=0; i<numbets; i++)
if ( (bet= (bets[i].dirbet + bets[i].distbet)) != 0 )
totalbets += bet;
} else return(0);
if ( (diff= (newprice.Pval - prevprice.Pval)) < 0 )
diff = -1;
else if ( diff > 0 )
diff = 1;
for (j=0; j<numbets; j++)
{
bets[j].minutes = (T.blocktimestamp - bets[j].timestamp) / PEGGY_MINUTE;
bets[j].shares = bets[j].dist = 0;
if ( (bet= bets[j].dirbet) != 0 )
{
if ( (preddiff= (bets[j].prediction.Pval - prevprice.Pval)) < 0 )
preddiff = -1;
else if ( preddiff > 0 )
preddiff = 1;
if ( (tmp= diff*preddiff) > 0 )
match++, matchbets += bet, bets[j].shares = (bet * bets[j].minutes), matchshares += (bet * (1 + bets[j].minutes));
else if ( tmp < 0 )
oppo++, oppobets += bet;
}
if ( (bet= bets[j].distbet) != 0 )
{
if ( (dist= ((int64_t)newprice.Pval - bets[j].prediction.Pval)) < 0 )
dist = -dist;
bets[j].dist = ++dist;
if ( bestdist == 0 || dist < bestdist )
bestdist = dist;
}
bets[j].payout = 0;
}
for (j=0; j<numbets; j++)
{
if ( bets[j].dist == bestdist )
winningbets += bets[j].distbet, numbest++;
else losingbets += bets[j].distbet;
}
winningbets += (winningbets / 100);
for (j=0; j<numbets; j++)
{
if ( bets[j].dist == bestdist && winningbets != 0 )
bets[j].payout = bets[j].distbet + ((bets[j].distbet * losingbets) / winningbets);
}
matchshares += (matchshares / 100);
for (payout=j=0; j<numbets; j++)
{
if ( bets[j].shares != 0 )
bets[j].payout = bets[j].dirbet + ((bets[j].shares * oppobets) / matchshares);
payout += bets[j].payout;
}
PEGS->bank.privatebetfees += (totalbets - payout);
if ( numbets != 0 )
printf("royalty %.8f (%.8f - payout %.8f) numbest.%d winningbets %.8f vs losingbets %.8f | match.%d %.8f, oppo.%d %.8f\n",dstr(totalbets - payout),dstr(totalbets),dstr(payout),numbest,dstr(winningbets),dstr(losingbets),match,dstr(matchbets),oppo,dstr(oppobets));
return(totalpayout);
}
struct price_resolution peggy_newprice(struct peggy_info *PEGS,struct peggy *PEG,struct peggy_time T,uint32_t newpval)
{
uint64_t sum,den,gap; int64_t diff; int32_t i,j,minute,iter; struct price_resolution price,shortprice,newprice;
gap = (PEG->spread.Pval * newpval) / PRICE_RESOLUTION;
if ( (newprice.Pval= newpval) != 0 )
{
for (iter=0; iter<1; iter++)
{
sum = den = 0;
sum = (((uint64_t)peggy_smooth_coeffs[0]) * newprice.Pval);
den = peggy_smooth_coeffs[0];
minute = (T.blocktimestamp - PEG->genesistime) / PEGGY_MINUTE;
//printf("peggy_newprice day.%d: %u sum %lld\n",day,newpval,(long long)sum);
for (i=1; i<PEGGY_NUMCOEFFS; i++)
{
j = (minute - i);
if ( j > 0 )
{
price = peggy_price(PEG,j);
if ( price.Pval != 0 )
{
sum += (((uint64_t)peggy_smooth_coeffs[i]) * price.Pval);
den += peggy_smooth_coeffs[i];
}
//printf("i.%d ind.%d coeff.%llu add.%lld sum %lld den %lld %.6f -> %.7f\n",i,j,(long long)peggy_smooth_coeffs[i],(long long)price.Pval,(long long)sum,(long long)den,Pval(&price),(double)sum/(den));
} else break;
}
price.Pval = newpval;
//printf("sum %lld den %lld %.10f -> %.10f || ",(long long)sum,(long long)den,Pval(&price),(double)sum/(den));
if ( den != 0 )
price.Pval = (sum / den);
else break;
newprice = price;
diff = (newprice.Pval - newpval);
if ( diff < 0 )
diff = -diff;
if ( diff < gap )
break;
newprice.Pval = (newprice.Pval*7 + newpval) / 8;
//printf("%.8f ",Pval(&newprice));
}
peggy_setprice(PEG,newprice,minute);
shortprice = peggy_shortprice(PEG,newprice);
price.Pval = newpval;
if ( Debuglevel > 2 )
fprintf(stderr,"t.%u M%d day.%-4d new %.8f short.%.8f pval.%.8f | first %.10f ",T.blocktimestamp,minute,minute/1440,Pval(&newprice),Pval(&shortprice),Pval(&price),Pval(&PEG->genesisprice));
}
return(newprice);
}
void peggy_margincalls(struct peggy_info *PEGS,struct peggy_time T,struct peggy *PEG,struct price_resolution newprice,struct price_resolution shortprice)
{
int32_t i; struct peggy_unit *U; int64_t satoshis,gain,threshold; struct peggy_entry entry;
for (i=0,U=&PEGS->accts->units[0]; i<PEGS->accts->numunits; i++,U++)
{
if ( U->lock.margin != 0 && PEG != 0 && (PEG->name.id == U->lock.peg || PEG->name.id == -U->lock.peg) )
{
if ( (PEG= peggy_findpeg(&entry,PEGS,U->lock.peg)) != 0 )
{
satoshis = peggy_poolmainunits(&entry,-1,entry.polarity,newprice,shortprice,PEG->spread,PEG->pool.mainunitsize,U->lock.denom);
gain = entry.polarity * (satoshis - U->costbasis), threshold = -U->costbasis/U->lock.margin;
if ( gain < threshold )
{
printf("%s %8.6f %8.6f %2dx gain %11.8f polarity.%-2d (%11.8f - cost %11.8f) -> profit %11.8f margincall %11.8f ",PEG->name.name,Pval(&newprice),Pval(&shortprice),U->lock.margin,dstr(gain),entry.polarity,dstr(satoshis),dstr(U->costbasis),dstr(gain),dstr(threshold));
printf("MARGINCALLED\n");
peggy_changereserve(PEGS,PEG,-1,&entry,satoshis,U->lock.margin,U->marginamount);
peggy_delete(PEGS->accts,U,PEGGY_RSTATUS_MARGINCALL);
}
}
}
}
}
struct price_resolution peggy_priceconsensus(struct peggy_info *PEGS,struct peggy_time T,uint64_t seed,int16_t pricedpeg,struct peggy_vote *votes,uint32_t numvotes,struct peggy_bet *bets,uint32_t numbets)
{
struct peggy_entry entry; struct price_resolution newprice,dayprice,tmp; struct peggy *PEG;
int64_t delta,weight,totalwt = 0; int32_t i,j,n,minute,day,wts[PEGGY_NUMCOEFFS]; uint32_t start,k; uint64_t ind;
newprice.Pval = 0;
if ( (PEG= peggy_find(&entry,PEGS,PEGS->contracts[pricedpeg]->name.name,1)) != 0 )
{
for (i=n=0; i<numvotes; i++)
{
if ( votes[i].pval != 0 )
{
wts[i] = 1;//n + 1;
n++;
totalwt += wts[i];
}
}
if ( n < PEG->pool.quorum || totalwt < PEG->pool.decisionthreshold )
return(PEG->price);
start = (uint32_t)(pricedpeg * seed);
for (k=0; k<numvotes; k++)
{
if ( numvotes == PEGGY_NUMCOEFFS )
ind = Peggy_inds[(k + start) % PEGGY_NUMCOEFFS];
else ind = (start + k);
i = (int32_t)(ind % numvotes);
//fprintf(stderr,"(%d %d) ",i,k);
weight = 0;
if ( votes[i].pval != 0 )
{
for (j=0; j<numvotes; j++)
{
if ( votes[j].pval != 0 )
{
//fprintf(stderr,"%lld ",(long long)(j*incr + seed) % numvotes);
if ( (delta= (votes[i].pval - votes[j].pval)) < 0 )//votes[j].price.Pval)) < 0 )
delta = -delta;
//printf("(%lld %llu %u).%lld ",(long long)delta,(long long)votes[i].pval,votes[j].tolerance,(long long)weight);
if ( delta <= votes[j].tolerance )//votes[j].tolerance.Pval )
{
weight += wts[j];
if ( weight > (totalwt >> 1) ) ////votes[j].weight;
break;
}
}
}
}
//if ( strcmp("SuperNET",PEG->name.name) == 0 )
// fprintf(stderr,"%d.(%.6f %llu) ",i,(double)votes[i].pval/PRICE_RESOLUTION,(long long)weight);
if ( weight > (totalwt >> 1) )
{
if ( Debuglevel > 2 )
fprintf(stderr,"%6d k%-4d %4d-> %.6f wt.%-4lld/%4lld ",votes[i].pval,k,i,Pval(&PEG->price),(long long)weight,(long long)totalwt);
PEG->price = peggy_newprice(PEGS,PEG,T,votes[i].pval);
break;
}
}
if ( 0 && k == numvotes )
fprintf(stderr,"no consensus for %s %6d k%-4d %4d-> %.6f wt.%-4lld/%4lld ",PEG->name.name,votes[i].tolerance,k,i,Pval(&PEG->price),(long long)weight,(long long)totalwt);
if ( (day= (T.blocktimestamp - PEG->genesistime)/PEGGY_DAYTICKS) != PEG->day )
{
peggy_gamblers(PEGS,T,PEG->dayprice,PEG->price,bets,numbets);
dayprice.Pval = 0;
for (minute=PEG->day*1440,i=n=0; i<1440; i++,minute++)
{
tmp = peggy_price(PEG,minute);
if ( tmp.Pval != 0 )
dayprice.Pval += tmp.Pval, n++;
}
if ( n != 0 )
dayprice.Pval /= n;
PEG->dayprice = dayprice;
PEG->day = day;
PEG->dayprices[day] = (uint32_t)dayprice.Pval;
if ( Debuglevel > 2 )
printf(">>>>>>>>>>>> DAY PRICE.%d %s %.8f\n",day,PEG->name.name,Pval(&dayprice));
while ( --day > 0 && PEG->dayprices[day] == 0 )
PEG->dayprices[day] = (uint32_t)dayprice.Pval;
}
peggy_margincalls(PEGS,T,PEG,PEG->price,peggy_shortprice(PEG,PEG->price));
newprice = PEG->price;
} else printf("cant find peg.%d\n",pricedpeg);
return(newprice);
}