In [ ]:
# Load all analyzers
import graph_analyzer
tickers = ['ANC', 'DGC', 'FTC', 'GLD', 'LKY', 'MNC', 'NMC', 'NVC', 'PPC', 'LTC', 'DRK', 'MEC']
analyzers = map(graph_analyzer.GraphAnalyzer, tickers)

In [92]:
import mining
mining_analyzers = map(mining.MiningAnalyzer, analyzers)

Hypothesis 4: The per-capita value held by an entity decreases over the lifecycle of an altcoin.


In [120]:
# Compute concentration (per capita % of coins) over time for each coin
concentrations = []
for azr in analyzers:
    # Generate powers-of-10 tx_ids, e.g., [10, 100, 1000, ...]
    tx_ids = [10 ** i for i in range(1, int(log(azr.now_tx_id, 10)) + 1)] + [azr.now_tx_id]
    counts = []
    for tx_id in tx_ids:
        # Compute mean balance / total balance to get distribution of existing wealth
        mean_balance = azr.mean_balance_for_cluster(tx_id=tx_id)
        total_balance = azr.balance_for_blockchain(tx_id=tx_id)
        if total_balance == 0:
            counts.append(None)
        else:
            pct = mean_balance / float(total_balance)
            counts.append(pct)

    concentrations.append((tx_ids, counts))

In [143]:
# Plot on log scale
colors = [
    '#e50554',
    '#07ec00',
    '#0266c8',
    '#804a2d',
    '#c5e3bf',
    '#003bba',
    '#f90000',
    '#00ffb8',
    '#ff8737',
    '#a054e1',
    '#8f5a38',
    '#2f1e1e'
]
for i, (x, y) in enumerate(concentrations):
    now_tx_id = analyzers[i].now_tx_id
    xs = [float(n) / now_tx_id for n in x]
    ys = y
    plt.semilogy(xs, ys, label=analyzers[i].ticker.upper(), color=colors[i % len(colors)])

plt.ylabel('% of Wealth Held Per Capita')
plt.xlabel('% of Transactions To-Date')
plt.title('% of Wealth Held Per Capita Over Time')
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)


Out[143]:
<matplotlib.legend.Legend at 0x1d9377a50>

In [78]:
# Let's look at early mining, i.e., the first 1000 blocks
num_miners = []
num_entities = []
first_n = 1000
for (azr, mining_azr) in zip(analyzers, mining_analyzers):
    tx_id = 5000
    miner_pubkeys = mining_azr.miner_pubkeys(tx_id=tx_id)
    while len(miner_pubkeys) < first_n:
        tx_id *= 2
        miner_pubkeys = mining_azr.miner_pubkeys(tx_id=tx_id)
    miner_pubkeys = miner_pubkeys[:first_n]
    
    distinct_miners = set(miner_pubkeys)
    distinct_entities = set(azr.cluster_for_pubkey(pk) for pk in distinct_miners)
    num_miners.append(len(distinct_miners))
    num_entities.append(len(distinct_entities))

In [79]:
xs = [market.rank_for_ticker(azr.ticker) for azr in analyzers]
ys = num_entities
plot_with_best_fit(xs, ys, title='# Distinct Miners in First 1000 Blocks vs. Market Cap', ylabel='# Distinct Miners', xlabel='Market Cap (Ranking)')


Correlation coefficient: -0.221

In [121]:
# Now lets look at the average number of blocks mined per entity
data = []
block_nums = [1, 10, 100, 500, 1000, 5000, 10000, 20000, 50000, 100000]
for (azr, mining_azr) in zip(analyzers, mining_analyzers):
    miner_pubkeys = mining_azr.miner_pubkeys()
    num_entities = []
    keys = set([])
    entities = set([])
    prev_n = 0
    block_nums_for_azr = block_nums + [azr.now_tx_id]
    for n in block_nums_for_azr:
        new_keys = set(miner_pubkeys[prev_n:n]).difference(keys)
        new_entities = set(azr.cluster_for_pubkey(pk) for pk in new_keys)
        keys.update(new_keys)
        distinct_entities.update(new_entities)
        num_entities.append(len(distinct_entities))
        prev_n = n        
    data.append(num_entities)

In [121]:
# This is the average number of blocks mined per miner
for i, series in enumerate(data):
    series_scaled = []
    xs = block_nums
    print xs
    for j, n in enumerate(series[:-1]):
        series_scaled.append(float(xs[j]) / n)
    
    plt.figure(i)
    plt.plot(xs, series_scaled)
    plt.title(analyzers[i].ticker)

Hypothesis 5: Altcoins with larger market cap are less concentrated.


In [39]:
import operator
from math import log
from collections import Counter
from scipy.stats import linregress

import market

In [10]:
def plot_with_best_fit(xs, ys, func=plt.plot, color='r', ylabel=None, xlabel=None, title=None):
    """Plot data with a line of best fit."""
    coeffs = linregress(xs, ys)
    best_fit_xs = [min(xs), max(xs)]
    best_fit_ys = [coeffs[0] * x + coeffs[1] for x in best_fit_xs]
    
    # Add title and labels
    plt.title(title)
    plt.ylabel(ylabel)
    plt.xlabel(xlabel)
    
    # Plot data
    func(xs, ys, color + 'o', best_fit_xs, best_fit_ys, color + '--')
    print 'Correlation coefficient: %.3f' % coeffs[2]

Entity Concentration & Size


In [146]:
# Calculate (# public keys / # clusters), i.e., average keys per entity
xs = [log(market.cap_for_ticker(azr.ticker), 10) for azr in analyzers]
ys = [len(azr.balances) / float(len(azr.clusters)) for azr in analyzers]
plot_with_best_fit(xs, ys, title='# Keys per Entity vs. Market Cap', ylabel='Avg. # Keys', xlabel='Log(Market Cap)')


Correlation coefficient: -0.590

In [248]:
top_n = 10
lengths = [[len(azr.clusters[c]) for c in azr.clusters] for azr in analyzers]
ys = [sum(sorted(z, reverse=True)[:top_n]) / float(len(analyzers[i].balances)) for i, z in enumerate(zs)]

# Calculate (# public keys / # clusters), i.e., average keys per entity
xs = [log(market.cap_for_ticker(azr.ticker), 10) for azr in analyzers]
plot_with_best_fit(xs, ys, title='Keys Covered By Top 10 Clusters vs. Market Cap', ylabel='Pct. of Keys', xlabel='Log(Market Cap)')


Correlation coefficient: -0.541

In [247]:
lengths = [len([cluster for cluster in azr.clusters if len(azr.clusters[cluster]) == 1]) for azr in analyzers]
ys = [float(length) / len(azr.clusters) for (length, azr) in zip(lengths, analyzers)]

# Calculate (# public keys / # clusters), i.e., average keys per entity
xs = [log(market.cap_for_ticker(azr.ticker), 10) for azr in analyzers]
plot_with_best_fit(xs, ys, title='Single-Key Clusters vs. Market Cap', ylabel='Proportion of Single-Key Clusters', xlabel='Log(Market Cap)')


Correlation coefficient: -0.755

Wealth Concentration


In [113]:
# Compute wealth concentration by 10 richest individuals
top_n = 10
pcts = []
for azr in analyzers:
    richest = azr.richest_n_clusters(top_n)
    total_held_by_richest = sum(zip(*richest)[1])
    total_in_blockchain = azr.balance_for_blockchain()
    pct = total_held_by_richest / float(total_in_blockchain)
    pcts.append(pct)

In [86]:
# Plot wealth concentration vs. market cap ranking
xs = [market.rank_for_ticker(azr.ticker) for azr in analyzers]
ys = pcts
plot_with_best_fit(xs, ys, title='Wealth Held by %d Richest Entities vs. Market Cap' % top_n, ylabel='Proportion of Wealth', xlabel='Market Cap (Ranking)')


Correlation coefficient: 0.752

In [116]:
# Plot wealth concentration vs. raw market cap (on log scale)
xs = [log(market.cap_for_ticker(azr.ticker), 10) for azr in analyzers]
ys = pcts
plot_with_best_fit(xs, ys, title='Wealth Held by %d Richest Entities vs. Log(Market Cap)' % top_n, ylabel='Proportion of Wealth', xlabel='Log(Market Cap)')


Correlation coefficient: -0.746

Mining Concentration


In [90]:
# Next, look at % of blocks mined by top n miners
def pct_mined_by_top_n(azr, top_n):
    miner_pubkeys = azr.miner_pubkeys()
    total_blocks_mined = len(miner_pubkeys)
    counter = Counter(miner_pubkeys)
    top_miners = counter.most_common(top_n)
    total_by_top_n = sum([blocks for (_, blocks) in top_miners])
    pct = total_by_top_n / float(total_blocks_mined)
    return pct

In [249]:
# Start by looking at top 10 miners
top_n = 10
pcts_mined = map(lambda azr: pct_mined_by_top_n(azr, top_n), mining_analyzers)

# Plot mining percentages vs. market cap ranking
xs = [log(market.cap_for_ticker(azr.ticker), 10) for azr in analyzers]
ys = pcts_mined
plot_with_best_fit(xs, ys, color='b', title='Blocks Mined by %d Best Miners vs. Log(Market Cap)' % top_n, ylabel='Proportion of Blocks', xlabel='Log(Market Cap)')


Correlation coefficient: -0.844

In [250]:
# Let's do the same thing for the top 1 miner
top_n = 1
pcts_mined = map(lambda azr: pct_mined_by_top_n(azr, top_n), mining_analyzers)

# Plot mining percentages vs. market cap ranking
xs = [log(market.cap_for_ticker(azr.ticker), 10) for azr in analyzers]
ys = pcts_mined
plot_with_best_fit(xs, ys, color='b', title='Blocks Mined by %d Best Miners vs. Market Cap' % top_n, ylabel='Proportion of Blocks', xlabel='Market Cap (Ranking)')


Correlation coefficient: -0.184

In [103]:
def richest_n_pubkeys(azr, n):
    balances = {
        pubkey: azr.balances[pubkey][-1][1] for pubkey in azr.balances
    }
    richest_n = sorted(balances.items(), key=operator.itemgetter(1), reverse=True)[:n]
    return richest_n

In [105]:
# Compute wealth concentration by 10 richest individuals
top_n = 10
pcts = []
for azr in analyzers:
    richest = richest_n_pubkeys(azr, top_n)
    total_held_by_richest = sum(zip(*richest)[1])
    total_in_blockchain = azr.balance_for_blockchain()
    pct = total_held_by_richest / float(total_in_blockchain)
    pcts.append(pct)

In [112]:
# Plot wealth concentration vs. market cap ranking
xs = [log(market.cap_for_ticker(azr.ticker), 10) for azr in analyzers]
ys = pcts
plot_with_best_fit(xs, ys, title='Wealth Held by %d Richest Addresses vs. Log(Market Cap)' % top_n, ylabel='Proportion of Wealth', xlabel='Log(Market Cap)')


Correlation coefficient: -0.539

In [ ]: