#! /usr/bin/gawk -f # Last edited on 2016-10-31 11:23:20 by stolfilocal # Reads a daily bitcoin price series. # Outputs a file with upper and lower estimates of the "Bitcoin National Debt" # accumulated up to each day. # # The user must define (with {export}) the environment variable {TZ="UTC"}. # # The user must load (with "-f") the packages {useful_functions.gawk} # and {price_series-functions.gawk}. # # The user must define (with "-v") the internal variables # # {priceFile} the input file with daily price series (see {price_series_read_file} in {price_series_functions.gawk). # {minedFile} the input file with the daily BTC mined. # # Writes to standard output a single file, with one line number {id} per day in the format # # "{DATE[id]} | {PLO[id]} | {PHI[id]} | {PAV[id]} | {VBM[id]} | {DLO[id]} | {DHI[id]}" where # # {DATE[id],PLO[id],PHI[id]} are as in the input file, # {VBM[id]} is the amount of BTC mined on that day, {DLO[id],DHI[id]} are the # low and high estimates for the Bitcin National Debt at the end of dat {DATE[id]}. BEGIN \ { abort = -1; if (priceFile == "") { arg_error("must define {priceFile}"); } if (minedFile == "") { arg_error("must define {minedFile}"); } # R = 1.10; # Expected payoff factor per year. R = 1.00; # Expected payoff factor per year. # Precision (unit-in-last-place) of input and output values: ulp_vbt = 0.0001; # Unit in the last place of input {vbt} ulp_vcr = 0.0001; # Unit in the last place of input {vcr} ulp_pav = 0.00001; # Unit in the last place of input average price {pav} ulp_phl = 0.00001; # Unit in the last place of {pop,phi,plo,pcl}. # Read the price series (indexed with {id} in {0..nd_price-1}) split("", date_id); # Date of start of interval. split("", time_id); # Time of start of interval. split("", pop_id); # Opening price. split("", phi_id); # High price. split("", plo_id); # Low price split("", pcl_id); # Closing price. split("", vbt_id); # BTC volume. split("", vcr_id); # Currency volume. split("", pav_id); # Average price. nd_price = price_series_read_file( \ priceFile,24*60*60, \ date_id,time_id,pop_id,phi_id,plo_id,pcl_id,vbt_id,vcr_id,pav_id \ ); # Read the miner output per day, indexed by {jd} in {0..nd_mined}: split("", date_jd); # Date from BTC mined file. split("", vbm_jd); # Amount of BTC mined on that day. nd_mined = read_btc_mined_per_day(minedFile, date_jd,vbm_jd); # Output data indexed by {kd} in {0..nd_debit-1}: split("", date_kd); # Date for output file. split("", plo_kd); # Low price in day. split("", phi_kd); # High price in day. split("", pav_kd); # Average price in day. split("", vbm_kd); # Mined bitcoin in day. split("", dhi_kd); # High estimate of debt for coins mined after {date_kd[kd]}. split("", dlo_kd); # Low estimate of debt for coins mined after {date_kd[kd]}. # Debit bounds: slo = 0; shi = 0; # Assumes that the mined data is more complete nd_debit = nd_mined; id = nd_price-1; jd = nd_mined-1; kd = nd_debit-1; while (jd >= 0) { # Copy data from mined BTC file: date_kd[kd] = date_jd[jd]; vbm_kd[kd] = vbm_jd[jd]; jd--; if (id < 0) { # Early dates, price was zero: plo_kd[kd] = 0; phi_kd[kd] = 0; pav_kd[kd] = 0; } else { if ((date_id[id] < date_kd[kd]) || (phi_id[id] == 0)) { # No price data, replicate the next defined price: printf "missing price data for %s\n", date_kd[kd] > "/dev/stderr"; plo_kd[kd] = plo_kd[kd+1]; phi_kd[kd] = phi_kd[kd+1]; pav_kd[kd] = pav_kd[kd+1]; } else { # Get price data from price file: if (date_id[id] != date_kd[kd]) { price_error(("date mismatch \"" date_id[id] "\" \"" date_kd[kd] "\"")); } plo_kd[kd] = plo_id[id]; phi_kd[kd] = phi_id[id]; pav_kd[kd] = pav_id[id]; } if (date_id[id] == date_kd[kd]) { id--; } } # Update the debit bounds for coins mined on {date_kd[kd]}: if (kd == nd_debit-1) { plo_after = plo_kd[kd]; phi_after = phi_kd[kd]; } else { Rpow = exp((nd_debit-1-kd)*log(R)/365.25); # Interest factor. plo_adj = plo_kd[kd]*Rpow; # Low price on {date_kd[kd]} plus interest to final date. if (plo_adj < plo_after) { plo_after = plo_adj; } phi_adj = phi_kd[kd]*Rpow; # High price on {date_kd[kd]} plus interest to final date. if (phi_adj > phi_after) { phi_after = phi_adj; } } # Add to the debit bounds: dlo_kd[kd] = slo; dhi_kd[kd] = shi; slo += vbm_kd[kd]*plo_after; shi += vbm_kd[kd]*phi_after; kd--; } # Write out: printf "current debit in [ %20.5f _ %20.5f ] million USD\n", slo/1000000, shi/1000000 > "/dev/stderr"; for (kd = 0; kd < nd_debit; kd++) { printf "%s", date_kd[kd]; printf " | %12.5f | %12.5f | %12.5f", plo_kd[kd], phi_kd[kd], pav_kd[kd]; printf " | %9.2f", vbm_kd[kd]; printf " | %20.2f | %20.2f", slo-dlo_kd[kd], shi-dhi_kd[kd]; printf "\n"; if ((kd % 100) == 0) { printf "%s [ %20.2f _ %20.5f ] million USD \n", \ date_kd[kd], (slo-dlo_kd[kd])/1000000, (shi-dhi_kd[kd])/1000000 > "/dev/stderr"; } } nd_debit = kd; fflush("/dev/stdout"); printf "%6d debit lines written (%s -- %s)\n", \ nd_debit, date_kd[0], date_kd[nd_debit-1] > "/dev/stderr"; exit(0); } END \ { } function read_btc_mined_per_day \ ( fname, date_ix,vbm_ix, \ \ nlin,lin,ndata,fld,nfld,dy,tm,dt, \ vbm,j,ody \ ) { # Reads a file "{fname}" with amounts of BTC mined in each day. # Stores the data in {date_ix[ix]},vbm_ix[ix]} # where {ix} is the index of the data line. # Returns the number of data lines read. printf "reading file %s ...\n", fname > "/dev/stderr"; ERRNO = ""; # Read the file: nlin = 0; # Number of lines read. ndata = 0; # Number of non-blank, non-header, non-comment lines. ody = ""; # Date on previous data line. while((getline lin < fname) > 0) { nlin++; # Remove tabs, inline comments, spurious blanks gsub(/[\011]/, " ", lin); gsub(/[\#].*$/, "", lin); gsub(/^[ ]+/, "", lin); gsub(/[ ]+$/, "", lin); gsub(/[ ][ ]+/, " ", lin); if ((lin != "") && (! match(lin, /[!]/))) { /* Data line: */ nfld = split(lin, fld, " "); if (nfld != 3) { file_error(fname, nlin, ("wrong field count = \"" lin "\"")); } # Get the input fields: dy = usf_check_date(fname,nlin,fld[1]); tm = usf_check_time(fname,nlin,fld[2]); vbm = usf_check_num(fname,nlin,fld[3]); # Consistency checks: if ((tm != "18:15:05") && (tm != "00:00:00")) { file_error(fname,nlin, ("unexpected time \"" tm "\"")); } if ((ody != "") && (! usf_dates_are_consecutive(ody,dy))) { file_error(fname,nlin, ("non-consecutive dates \"" ody "\" \"" dy "\"")); } ody = dy; # Save in arrays: date_ix[ndata] = dy; vbm_ix[ndata] = vbm; ndata++; } } if ((ERRNO != "0") && (ERRNO != "")) { file_error(fname, nlin, ERRNO); } close (fname); if (nlin == 0) { arg_error(("file \"" fname "\" empty or missing")); } printf "%6d lines read\n", nlin > "/dev/stderr" printf "%6d data lines found (%s -- %s)\n", \ ndata, date_ix[0], date_ix[ndata-1] > "/dev/stderr"; return ndata; }