Algorithmically assisted Fundamental Analysis/Investing

In this notebook we explore several algorithmically assisted approaches towards fundamental analysis and investing.
"Algorithmic assistance" here does not impact our investment strategy as such. The strategy is already defined, however, we will write and use plain as well as machine learning
algorithms that will hopefully help us execute our strategy in a more straightforward and informed manner.

General Strategy

Our goal here is finding market inefficiencies in pricing.
In other words, the alpha is gained by finding potentially undervalued (or overvalued) coins, assigning a correct price and making a buy or short decision based on better information.
We will focus on undervalued assets first (as shorting is difficult in current crypto environment). Since $$Cm = Q * P \text{, where Q is a constant}$$ we can assume perfect correlation between $P$ and $Cm$ as well high dependence of the rate of change of $P$ on the supply to current market cap value. To maximize our strategy it then needs to be restated as trying to find undervalued coins with the highest potential rate of change or, more precisely, trying to find those coins with low market cap and limited supply.

Extensions

Load notebook specific extensions here

In [74]:
%load_ext autoreload
%autoreload 2

Imports

This space is reserved for all imports

In [65]:
import numpy as np
import pandas as pd
from cryptocompy import price as cprice
from coinmarketcap import Market
from ccap import Ccap
import utils
import imp
import seaborn as sns

from IPython.display import Image

from jupyterthemes import jtplot

from ipywidgets import IntProgress
from ipywidgets import Box
from IPython.display import display

import matplotlib.pyplot as plt

from selenium import webdriver

import time

imp.reload(utils)
pd.options.display.float_format = '{:,.6f}'.format

jtplot.style()

Market Data

We can start by grabbing market data from a coin directory such as coinmarketcap.
This data usually includes our indicators of interest (market cap, price and supply) as well as other, potentially interesting, datapoints.

In [18]:
cmc = Market()
markets = utils.markets_to_df(cmc.ticker()).dropna()
print("Got %d markets with complete information in total." % len(markets))
Got 862 markets with complete information in total.

Here's a quick overview of our markets

In [19]:
markets.head()
Out[19]:
24h_volume_usd available_supply cached id last_updated market_cap_usd name percent_change_1h percent_change_24h percent_change_7d price_btc price_usd rank symbol total_supply
0 4,567,910,000.000000 16,660,412.000000 False bitcoin 1509654851 118,682,277,527.000000 Bitcoin 2.140000 7.900000 20.270000 1.000000 7,123.610000 1 BTC 16,660,412.000000
1 897,218,000.000000 95,482,864.000000 False ethereum 1509654850 27,701,392,957.000000 Ethereum 0.630000 -2.800000 -2.330000 0.041014 290.119000 2 ETH 95,482,864.000000
2 1,639,260,000.000000 16,753,550.000000 False bitcoin-cash 1509654863 9,751,923,138.000000 Bitcoin Cash 2.130000 15.870000 71.860000 0.082289 582.081000 3 BCH 16,753,550.000000
3 165,735,000.000000 38,531,538,922.000000 False ripple 1509654841 7,529,717,742.000000 Ripple -0.740000 -0.440000 -4.070000 0.000028 0.195417 4 XRP 99,993,667,738.000000
4 197,295,000.000000 53,650,857.000000 False litecoin 1509654841 2,914,523,811.000000 Litecoin -0.120000 -0.640000 -2.780000 0.007680 54.323900 5 LTC 53,650,857.000000

And a quick summary of the data

In [20]:
markets.describe()
Out[20]:
24h_volume_usd available_supply market_cap_usd percent_change_1h percent_change_24h percent_change_7d price_btc price_usd rank total_supply
count 862.000000 862.000000 862.000000 862.000000 862.000000 862.000000 862.000000 862.000000 862.000000 862.000000
mean 10,431,926.968393 6,146,695,566.916473 224,109,809.868910 1.908782 0.674988 -1.151810 0.030890 218.558791 446.733179 26,572,094,279.996521
std 168,884,788.236085 50,845,717,277.933968 4,172,583,575.444901 9.018918 27.134689 44.286781 0.748131 5,292.049459 268.105160 437,824,317,148.045898
min 0.287881 1.000000 4.000000 -39.860000 -81.740000 -98.960000 0.000000 0.000000 1.000000 1.000000
25% 326.177250 5,360,269.250000 134,592.250000 0.232500 -8.747500 -19.697500 0.000001 0.005945 217.250000 8,783,570.750000
50% 5,718.130000 22,838,861.500000 1,231,063.000000 2.070000 -1.425000 -4.725000 0.000007 0.046710 434.500000 38,623,725.000000
75% 150,634.750000 113,651,411.000000 10,051,856.250000 2.210000 7.300000 10.130000 0.000054 0.380812 669.000000 214,717,130.250000
max 4,567,910,000.000000 932,202,073,611.000000 118,682,277,527.000000 115.070000 242.410000 628.750000 21.850000 154,558.000000 956.000000 10,000,000,000,000.000000

Implementing our first strategy

Lets remember that we are interested in undervalued assets with the highest potential rate of change. Meaning those assets most likely to 2x-3x+ in value.
To do this we need to define our search criteria, mainly max market cap, min market cap, max supply and max price (which is actually optional).
The parameters as well as the filter are defined below.

Note This step can be optimized via machine learning as there must exist a set of parameters leading to the highest returns.

In [21]:
denom =  1000000            # Unit denomination so we don't have to type this out eveyrtime
market_cap_min = 1  * denom # 1m marketcap min
market_cap_max = 45 * denom # 45m marketcap max
supply_max  = 200 * denom   # Total supply of no more than 900m
price_max = 0.20            # Current max price in USD
markets = utils.filter_by_market_cap(markets, market_cap_min, market_cap_max)
markets = utils.filter_by_total_supply(markets, supply_max)
markets = utils.filter_by_price(markets, price_max)
print("Got %d valid markets." % len(markets))
print("Sorting by current market cap.")
sorted_markets = markets.sort_values('market_cap_usd')
sorted_markets
Got 89 valid markets.
Sorting by current market cap.
Out[21]:
24h_volume_usd available_supply cached id last_updated market_cap_usd name percent_change_1h percent_change_24h percent_change_7d price_btc price_usd rank symbol total_supply
427 1,398.690000 33,356,077.000000 False renos 1509654857 1,138,533.000000 Renos 4.800000 -1.550000 -9.350000 0.000005 0.034133 428 RNS 33,356,077.000000
425 2,697.580000 126,000,000.000000 False fastcoin 1509654841 1,167,405.000000 Fastcoin -0.400000 -2.940000 9.010000 0.000001 0.009265 426 FST 126,000,000.000000
420 113,718.000000 22,489,025.000000 False elixir 1509654866 1,274,975.000000 Elixir -4.890000 -4.070000 -21.170000 0.000008 0.056693 421 ELIX 22,489,025.000000
415 47,593.900000 75,614,500.000000 False bitzeny 1509654848 1,303,481.000000 Bitzeny -4.360000 -0.680000 244.930000 0.000002 0.017239 416 ZNY 75,614,500.000000
414 3,955.950000 8,035,193.000000 False woodcoin 1509654846 1,307,270.000000 Woodcoin 2.200000 15.210000 19.710000 0.000023 0.162693 415 LOG 8,035,193.000000
413 4,423.680000 76,403,300.000000 False trezarcoin 1509654867 1,313,587.000000 TrezarCoin -0.080000 11.310000 46.730000 0.000002 0.017193 414 TZC 88,403,300.000000
634 106.361000 10,000,000.000000 False inpay 1509654858 1,322,050.000000 InPay 2.110000 7.630000 1.510000 0.000019 0.132205 635 INPAY 10,000,000.000000
412 191,791.000000 12,000,000.000000 False eboostcoin 1509654860 1,363,224.000000 eBoost -0.660000 -12.440000 -17.980000 0.000016 0.113602 413 EBST 99,990,001.000000
410 11,397.700000 46,500,000.000000 False oceanlab 1509654866 1,392,405.000000 Oceanlab 6.170000 4.360000 -15.700000 0.000004 0.029944 411 OCL 50,000,000.000000
408 4,435.030000 13,600,388.000000 False darcrus 1509654857 1,408,320.000000 Darcrus 2.300000 2.750000 -12.120000 0.000015 0.103550 409 DAR 13,600,388.000000
403 501.821000 12,052,943.000000 False adshares 1509654864 1,480,885.000000 AdShares 2.230000 4.640000 12.310000 0.000017 0.122865 404 ADST 12,052,943.000000
402 1,722.100000 8,660,756.000000 False real-estate-tokens 1509654866 1,485,987.000000 REX 0.910000 20.110000 10.400000 0.000024 0.171577 403 REX 24,015,497.000000
401 373,412.000000 12,529,747.000000 False trackr 1509654866 1,559,327.000000 CryptoInsight -11.740000 10.700000 20.930000 0.000018 0.124450 402 TKR 12,529,747.000000
399 17,380.400000 19,889,830.000000 False huntercoin 1509654845 1,653,135.000000 HunterCoin 2.550000 -7.710000 10.100000 0.000012 0.083115 400 HUC 19,889,830.000000
397 70,653.800000 24,530,355.000000 False arcticcoin 1509654860 1,667,387.000000 ArcticCoin 3.260000 -5.200000 55.000000 0.000010 0.067972 398 ARC 24,530,355.000000
395 13,996.900000 18,155,889.000000 False chips 1509654866 1,672,123.000000 CHIPS 2.110000 -22.290000 0.890000 0.000013 0.092098 396 CHIPS 18,155,889.000000
392 44,996.800000 77,231,176.000000 False cannabiscoin 1509654858 1,769,227.000000 CannabisCoin 0.270000 -6.790000 -8.660000 0.000003 0.022908 393 CANN 91,859,176.000000
391 455,676.000000 10,815,966.000000 False vtorrent 1509654848 1,785,694.000000 vTorrent -0.980000 1.640000 -7.920000 0.000023 0.165098 392 VTR 10,815,966.000000
390 30,782.000000 16,180,000.000000 False nautiluscoin 1509654844 1,794,589.000000 NautilusCoin 1.250000 6.750000 0.770000 0.000016 0.110914 391 NAUT 16,180,000.000000
386 81,156.900000 45,079,785.000000 False startcoin 1509654845 1,830,217.000000 Startcoin 2.350000 0.220000 -7.460000 0.000006 0.040600 387 START 70,782,220.000000
382 27,461.100000 27,432,814.000000 False onix 1509654861 1,893,844.000000 Onix 2.000000 1.760000 -9.830000 0.000010 0.069036 383 ONX 103,181,030.000000
381 36,537.300000 15,517,121.000000 False e-gulden 1509654845 1,937,794.000000 e-Gulden 2.710000 8.470000 6.300000 0.000018 0.124881 382 EFL 20,556,461.000000
380 1,820.070000 50,000,000.000000 False embers 1509654863 2,013,590.000000 Embers 1.330000 -16.590000 1.840000 0.000006 0.040272 381 MBRS 50,000,000.000000
377 32,028.400000 14,819,400.000000 False zoin 1509654854 2,067,677.000000 Zoin -0.970000 -1.680000 30.390000 0.000020 0.139525 378 ZOI 14,819,400.000000
375 99,169.900000 111,085,648.000000 False dopecoin 1509654842 2,097,941.000000 DopeCoin -0.610000 -10.620000 -16.030000 0.000003 0.018886 376 DOPE 111,085,648.000000
631 358.882000 20,999,088.000000 False ixcoin 1509654841 2,103,290.000000 Ixcoin 2.000000 18.040000 39.020000 0.000014 0.100161 632 IXC 20,999,088.000000
630 79.051100 23,472,224.000000 False adelphoi 1509654860 2,200,321.000000 Adelphoi 2.070000 -3.200000 12.530000 0.000013 0.093741 631 ADL 100,000,000.000000
373 142,190.000000 46,508,192.000000 False indorse-token 1509654867 2,214,143.000000 Indorse Token 1.090000 -2.250000 -10.470000 0.000007 0.047608 374 IND 170,622,047.000000
371 122,370.000000 38,578,125.000000 False riecoin 1509654842 2,224,017.000000 Riecoin -1.740000 -11.210000 38.600000 0.000008 0.057650 372 RIC 38,578,125.000000
370 14,330.200000 15,912,401.000000 False chaincoin 1509654850 2,235,342.000000 ChainCoin 6.830000 2.360000 -1.060000 0.000020 0.140478 371 CHC 15,912,401.000000
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
278 74,980.800000 50,000,000.000000 False project-decorum 1509654856 5,482,050.000000 Project Decorum 2.760000 -10.530000 -18.750000 0.000016 0.109641 279 PDC 50,000,000.000000
272 41,990.100000 113,761,209.000000 False dao-casino 1509654862 5,712,849.000000 DAO.Casino -0.110000 -10.750000 -35.350000 0.000007 0.050218 273 BET 167,270,821.000000
269 324,373.000000 83,000,000.000000 False coss 1509654866 5,773,621.000000 COSS 6.560000 -16.590000 -28.450000 0.000010 0.069562 270 COSS 200,000,000.000000
267 72,497.900000 40,599,508.000000 False goldcoin 1509654841 5,888,512.000000 GoldCoin 3.630000 -4.640000 9.550000 0.000021 0.145039 268 GLD 40,599,508.000000
266 45,146.900000 45,166,200.000000 False blockmason 1509654868 5,890,124.000000 BlockMason Credit Protocol 1.130000 -2.160000 0.640000 0.000018 0.130410 267 BCPT 116,158,667.000000
261 14,793.300000 32,601,362.000000 False tao 1509654855 6,341,747.000000 Tao 2.090000 -7.310000 67.860000 0.000028 0.194524 262 XTO 32,601,362.000000
260 1,688,550.000000 44,161,200.000000 False bitcny 1509654847 6,414,061.000000 bitCNY -3.410000 -0.930000 -3.300000 0.000021 0.145242 261 BITCNY 44,161,200.000000
255 142,556.000000 46,016,625.000000 False incent 1509654854 6,880,406.000000 Incent -2.220000 -2.290000 6.540000 0.000021 0.149520 256 INCNT 46,016,625.000000
254 53,170.100000 35,778,844.000000 False ixledger 1509654864 6,911,864.000000 iXledger -0.330000 3.690000 -21.820000 0.000027 0.193183 255 IXT 65,778,844.000000
253 54,661.700000 191,381,257.000000 False investfeed 1509654864 6,965,129.000000 InvestFeed -4.780000 -4.910000 -23.910000 0.000005 0.036394 254 IFT 191,381,257.000000
245 334,291.000000 40,000,000.000000 False blocktix 1509654864 7,481,200.000000 Blocktix 5.550000 -12.120000 -62.250000 0.000026 0.187030 246 TIX 62,500,000.000000
228 3,988.870000 51,200,000.000000 False primas 1509654865 9,226,906.000000 Primas 0.910000 -4.410000 -15.270000 0.000025 0.180213 229 PST 100,000,000.000000
224 59,798.700000 183,000,000.000000 False wagerr 1509654862 9,426,348.000000 Wagerr -0.400000 5.260000 2.910000 0.000007 0.051510 225 WGR 198,360,471.000000
212 102,638.000000 72,809,323.000000 False centurion 1509654856 10,472,092.000000 Centurion 3.780000 20.910000 31.830000 0.000020 0.143829 213 CNT 77,809,323.000000
207 435,141.000000 75,000,000.000000 False guppy 1509654858 10,745,775.000000 Matchpool 2.340000 -0.660000 1.480000 0.000020 0.143277 208 GUP 98,855,150.000000
203 799,646.000000 70,000,000.000000 False patientory 1509654860 10,987,900.000000 Patientory -3.410000 -5.900000 -15.360000 0.000022 0.156970 204 PTOY 100,002,000.000000
201 14,416.800000 85,978,873.000000 False agrello-delta 1509654866 11,267,617.000000 Agrello 4.550000 -16.250000 -22.740000 0.000019 0.131051 202 DLT 130,271,020.000000
200 241,002.000000 139,533,727.000000 False florincoin 1509654842 11,294,362.000000 FlorinCoin 2.180000 -5.830000 -17.320000 0.000011 0.080944 201 FLO 139,533,727.000000
186 150,406.000000 127,506,002.000000 False monetaryunit 1509654850 12,728,006.000000 MonetaryUnit 4.070000 -6.500000 -11.650000 0.000014 0.099823 187 MUE 127,506,002.000000
184 396,203.000000 66,521,586.000000 False nexium 1509654855 12,836,205.000000 Nexium -5.330000 2.570000 -19.790000 0.000027 0.192963 185 NXC 66,521,586.000000
182 150,711.000000 140,000,000.000000 False mothership 1509654864 13,045,914.000000 Mothership -7.000000 -24.630000 -17.620000 0.000013 0.093185 183 MSP 200,000,000.000000
180 30,182.200000 70,000,000.000000 False bitdice 1509654866 13,235,320.000000 BitDice 2.080000 -0.190000 -15.890000 0.000027 0.189076 181 CSNO 100,000,000.000000
177 56,281.300000 121,353,087.000000 False energycoin 1509654843 13,629,287.000000 Energycoin -2.510000 -1.630000 -10.350000 0.000016 0.112311 178 ENRG 121,353,087.000000
171 39,618.100000 82,500,000.000000 False paypie 1509654867 14,469,510.000000 PayPie 2.540000 -3.090000 -13.820000 0.000025 0.175388 172 PPP 165,000,000.000000
166 324,755.000000 162,859,340.000000 False humaniq 1509654859 15,073,365.000000 Humaniq 3.500000 4.340000 33.070000 0.000013 0.092554 167 HMQ 184,191,340.000000
163 209,886.000000 124,518,891.000000 False golos 1509654856 15,279,589.000000 Golos -2.840000 8.470000 21.270000 0.000017 0.122709 164 GOLOS 124,518,891.000000
160 75,383.500000 85,000,000.000000 False rialto 1509654863 15,941,920.000000 Rialto -2.370000 -0.060000 -4.470000 0.000027 0.187552 161 XRL 100,000,000.000000
151 1,058,070.000000 120,000,000.000000 False viberate 1509654867 18,448,080.000000 Viberate 0.460000 -4.190000 -1.380000 0.000022 0.153734 152 VIB 200,000,000.000000
147 2,734,310.000000 180,434,360.000000 False feathercoin 1509654841 19,106,916.000000 Feathercoin 0.500000 -16.860000 120.400000 0.000015 0.105894 148 FTC 180,434,360.000000
122 153,628.000000 127,108,905.000000 False xaurum 1509654847 24,584,515.000000 Xaurum 3.200000 0.730000 0.050000 0.000027 0.193413 123 XAUR 127,108,905.000000

89 rows × 15 columns

Filtering out noise - Approach I

Above we got 89 potential coins that fit our previously defined criteria.
Now applying the 80/20 rule, most of these coins will be useless and we need to get to the 20% which aren't.
We need to define an additional filter that will hopefully help us in reducing the noise before we can begin actually analyzing these assets.

Here we grab OHLC data for each of our coins and only look at coins that showed a positive price trend over the past 7 days.

In [22]:
symbols = sorted_markets['symbol'].values # our coins
ohlc = {}
l = len(symbols)
prog = IntProgress(min=0, max=l, value=10, description="Grabbing data: ")
display(prog)
for i, symbol in enumerate(symbols):
    d = pd.DataFrame(cprice.get_historical_data(symbol, 'BTC', 'day', limit=7))
    ohlc[symbol] = d
    prog.value += 1

Defining and running our price filter.

In [23]:
outliers = []

for symbol in ohlc:
    df = ohlc[symbol]
    if 'close' in df:
        f = df['close'].head(1).values[0]
        l = df['close'].tail(1).values[0]
        if l > f:
            outliers.append(symbol)

print("Found outliers: ", outliers)
Found outliers:  ['ZNY', 'TZC', 'REX', 'ARC', 'ZOI', 'RIC', 'XTO', 'CNT', 'HMQ', 'FTC']

Lets graph these coins.

In [24]:
for symbol in outliers:
    plt.plot(ohlc[symbol].close, label=symbol)
plt.legend(shadow=False, fancybox=True)
plt.show()

Light Analysis

We have managed to reduce our original set of 89 coins to 10 potentially undervalued coins.
Here we do some light analysis of those coins. Most of these steps are performed manually right now with only minor algorithmic assistance.
In the future we could develop a whole toolset to help us automate the whole process!

Some utility functions as well as the actual analysis step follow below.

In [75]:
# Utility functions
ccap = Ccap()
br.quit()
br = webdriver.PhantomJS()
br.set_window_size(1024,768)

def get_coin_stats(market):
    """
    Extracts supply, marketcap and price from 
    coin market data.
    """
    supply = int(market['available_supply'])
    mcap = market['market_cap_usd'].values[0]
    p = market['price_usd'].values[0]
    return supply, mcap, p

def get_and_prettify_stats(market):
    """
    Calls 'get_coin_stats' and pretty prints results.
    """
    s, m, p = get_coin_stats(market)
    print("Supply: {}, Marketcap: ${:,.2f}, Price: ${:,.2f}".format(s, m, p))

def capture_site(site, name):
    """
    Take a screencapture of any given site.
    """
    br.get(site)
    time.sleep(3)
    br.save_screenshot('/tmp/{}.png'.format(name))

def create_coin_summary(coin):
    """
    Calls 'get_and_prettify_stats' as well as
    grabs some additional information about the coin (like its official website).
    """
    market = sorted_markets[sorted_markets["symbol"] == coin]
    name = market.name.values[0]
    print("Investigating coin: {}".format(name))
    get_and_prettify_stats(market)
    print("Getting coin's website ")
    website, exchanges = ccap.info(name)
    print("Coin available at: {} and trading at {} exchanges.".format(website, len(exchanges)))
    return name, website, exchanges

def coin_summary_with_screenshot(coin):
    """
    Performs all above steps at once.
    """
    name, website, exchanges = create_coin_summary(coin)
    print("Taking screenshot")
    capture_site(website, name)
    display(Image("/tmp/{}.png".format(name), width=120, height=120))

XTO - Tao

In [72]:
coin_summary_with_screenshot('XTO')
Investigating coin: Tao
Supply: 32601362, Marketcap: $6,341,747.00, Price: $0.19
Getting coin's website 
Coin available at: http://tao.network/ and trading at 2 exchanges.
Taking screenshot