#Configure the environment
import numpy as np
import pandas as pd
import requests
import logging
%matplotlib notebook
import matplotlib
import matplotlib.pyplot as pyplot
import bitcoin
log = logging.getLogger(bitcoin.__name__)
log.setLevel(logging.ERROR)
print 'Pandas version: %s' % (pd.__version__,)
print 'Numpy version: %s' % (np.__version__,)
print 'Using MatplotLib backend: %s' % (matplotlib.get_backend())
btc = bitcoin.BTCMarket()
Miners can choose to stockpile, or sell BTC into global circulation. However, it's assumed that a miner must sell enough BTC to cover his costs.
For a given miner, assuming he sells all his BTC, we have
\begin{equation} \pi_i = \int_{t1}^{t2} \frac{m_i \cdot b(t) \cdot p(t)}{H(t)} dt \end{equation}where
Since the functions aren't truly continuous, the integral becomes a summation, and a daily income can be derived. Furthermore, the stock of BTC a miner holds is a simple differential equation; the rate of production minus the selling rate. $$ \frac{dS_i}{dt} = \beta_i - s_i $$
Rearranging and assuming that the smallest time period of interest is a day, then the discretised functions look like
\begin{align} \beta_{ij} &= \frac{m_i b_j}{\bar{H}_j} \cdot 3600 \cdot 24 \\ \Delta S_{ij} &= \beta_{ij} - s_{ij} \\ I_{ij} &= s_{ij} \bar{p}_j \end{align}where
Let's assume the following:
phc = btc.priceHistoryComplete
ph = btc.getPriceHistory()
# Plot the BTC price in USD over its full history with a 95% confidence interal
ci = ph.Stdev*1.96
hi = ph.price + ci
lo = ph.price - ci
ax = ph.price.plot(figsize=(12,7))
pyplot.fill_between(ci.index, hi, lo, color='b', alpha=0.2)
ax.set_ylabel('BTC Price (USD)')
ax.set_title('Bitcoin price history')
Let's have a look at the pace of BTC mining capacity addition.
In theory the time per block is 10 minutes. In times of rapid capacity addition, this number will be below ten minutes all the time, with the increase in difficulty lagging somewhat to try and compensate.
A bootstrap plot (below) shows that the mean time per block is generally 9 minutes, indicating that capacity has generally been ahead of the difficulty level.
I've also plotted the monthly mean time (in minutes) for mining a BTC block based on the global net mining rate and the difficulty below that. Also in the plot is the average BTC price for the month as an indication of what's driving demand for increasing capacity.
The two periods of greatest price increase, between 2010-06 and 2011-6 and in 2013 we see the average block mining time at values well below 10 minutes consistently.
dhexp = btc.difficulty_history.drop('blockNumber',1).resample('1d',fill_method='ffill')
#timeForBlock = 1e-9 * 4295032833 * dhexp.difficulty / btc.hashrate_history / 60.0
timeForBlock = 1e-9 * 4295032833 / 60 * dhexp.difficulty.div(btc.hashrate_history.Value)
timeForBlock = timeForBlock.dropna()
blocktime = timeForBlock.resample('M')
aveMonthlyPrice = ph.price.resample('M')
fig, ax1 = pyplot.subplots()
ax2 = ax1.twinx()
ax2.set_yscale('log')
ax1.plot(blocktime.index, blocktime, 'r')
ax2.plot(aveMonthlyPrice.index, aveMonthlyPrice, 'b')
ax1.set_xlabel('')
ax1.set_ylabel("time per block (min)", color='r')
ax2.set_ylabel("ave BTC price (\$)", color='b')
ax1.axvspan('2010-07-01','2011-07-01', facecolor='g', alpha=0.5)
ax1.axvspan('2013-02-01','2013-12-31', facecolor='g', alpha=0.5)
# Plot the net hash rate in GH/s over history, on a log scale
aveNetHash = btc.hashrate_history
aveNetHash.sort_index(inplace=True)
aveNetHash.plot(logy=True)
# Example simple global mining pool calculation with manual capacity addition
reload(bitcoin)
start_date = '2013-03-01'
end_date = '2015-10-01'
supply = bitcoin.MiningSupplyGenerator(start_date, end_date, None, overheads=0.15)
supply.generateSupplyFromHistory()
The folowing plot displays the hardware distribution over time as the hashing difficulty improves, and the hardware (cost and energy efficiency) improves.
The y-axis tracks total hash rate on a log scale, and the x-axis indicates time. The left-hand plot shows total global mining capacity, while the right-hand plot shows only hardware that should be operating, i.e. where cash cost < income from mining. The dots are the actual network hash rate as reported by Quandl's network index.
# How to mix pyplot commands with Pandas plotting features
#hwdata = hwdist.xs('totalHashRate', level='category')
drange = supply.date_range
hrData = supply.data['hashRateDistribution'].loc[drange]
mask = supply.data['canOperate'].loc[drange]
hrDataAll = hrData.sum(axis=1, level='Product')
hrDataRunning = hrData.mul(mask).sum(axis=1, level='Product')
# Plot configuation
netHash = btc.hashrate_history.loc[drange,:]
fig, axes = pyplot.subplots(nrows=1, ncols=2, figsize=(16, 8))
#axes[0].plot(hrDataAll.index, hrDataAll)
axes[0].set_yscale('log')
axes[0].set_ylabel('Hash rate capacity(GH/s)')
axes[0].plot(netHash.index, netHash, 'k:', label='Actual historical')
hrDataAll.plot(stacked=True, kind='area', ax=axes[0])
axes[1].set_yscale('log')
axes[1].set_ylabel('Hash rate capacity(GH/s)')
axes[1].plot(netHash.index, netHash, 'k:', label='Actual historical')
hrDataRunning.plot(stacked=True, kind='area', ax=axes[1])
The cash cost curves on the bottom row of the following figures are quite interesting. The red lines show the cash cost curve (assuming a 15% overheads rate -- which is almost certainly on the low side) for the given dates. The entire global BTC mining capacity is graphed, but the y-axis range is capped, clipping most expensive hardware (FGPAs and early ASICs) out of the picture. The fully absorbed cash cost (FACC), which includes depreciation (assuming a 2 year lifetime on hardware) and a desired return on capital (assumed as 15%) is also shown in blue.
Hardware is sorted by cash cost and the lowest threshold of each type of hardware is annoted on the plot for illustration purposes.
The 2015 curve indicates that most operaters are running at an absolute loss at present; a fact that has been going on since the beginning of 2015, as evidenced in the 'rational predicted' and 'actual' hash rates of the right-hand plot above.
As the 2015 cash cost curve below indicates, only the most efficient miners are recovering the capital costs of their equipment, but it also shows that even with a BTC price under $300, there are rigs available that make sense for investment. One therefore expects the hashing difficulty to remain roughly flat until all the unprofitable operators are forced out -- though they seem to be displaying some resiliance; perhaps by selling off BTC accumulated pre-2014 -- and then marginal capacity increasing at the rate factories can produce these efficient rigs.
dates = ['2013-10-01', '2013-11-30','2015-01-01','2015-10-01']
titles = ['before bubble', 'peak of bubble', 'after crash', 'today']
topY = [20, 100, 400, 500]
n = len(dates)
fig, axes = pyplot.subplots(nrows=2, ncols=n, figsize=(n*8,15))
for col, date in enumerate(dates):
hw = supply.getHardwareDistribution(date)
cc = supply.getCostCurve(date)
price = ph.price[date]
labels = cc.index.drop_duplicates()
axPie = axes[0, col]
axCC = axes[1, col]
axPie.set_title('Hardware distribution %s\n %s - $%3.0f/BTC' % (titles[col], str(date), price))
hw.plot(kind='pie', ax=axPie)
axCC.set_title('Cash cost curve %s' % (titles[col],))
axCC.set_ylim([0,topY[col]])
cc.plot(x='cumHash', y=['cashCost', 'facc'], ax=axCC, legend=False)
axCC.set_xlabel('Cumulative hash (GH/s)')
axCC.axhline(price, color='b')
#Annotate curve with hardware
maxH = cc['cumHash'].max()
for label in labels:
row = cc.loc[label].iloc[0]
lx = row.cumHash + 0.1*maxH if row.cumHash < 0.8*maxH else row.cumHash - 0.1*maxH
axCC.annotate(label, xy=(row.cumHash, row.cashCost),
xytext=(lx, row.cashCost - 0.1*topY[col]),
arrowprops={'facecolor': 'k',
'shrink': 0.05,
'width': 1})
The following chart presents a minimum capital investment into bitcoin mining hardware.
It is a minimum because
Notice the remarkable addition of hardware over Q1 2014, where at least $1.3billion was added to bitcoin mining operations in 3 months. Clearly, ASIC manufacturers could never have supplied this demand, so it really begs the question as to where this incredible amount of capacity came form.
numMachines = supply.data['numMachines']
hw = btc.hardware[['Product','Price']]
df = pd.merge(numMachines, hw, on='Product')
cols = numMachines.columns
cols = cols.drop('Product')
df = df[cols].mul(df.Price, axis=0).sum(axis=1).mul(1e-6)
capex = df.cumsum()
capex.index = numMachines.index
#Plot
fid, ax = pyplot.subplots()
ax.plot(capex.index, capex)
ax.set_ylabel('Capital investment ($mil)')
ax.set_xlabel('Date')
pyplot.close('all')
#supply.save()