Cryptocurrency Analysis with Python — MACD | by Roman Orac | Towards Data Science

I’ve decided to spend the weekend learning about cryptocurrency analysis. I’ve hacked together the code to download daily Bitcoin prices and apply a simple trading strategy to it.
In case you’ve missed my other articles about this topic:
romanorac.medium.com
Here are few links that might interest you:
- Complete your Python analyses 10x faster with Mito [Product]- Free skill tests for Data Scientists & ML Engineers [Test]- All New Self-Driving Car Engineer Nanodegree [Course]
Would you like to read more such articles? If so, you can support me by clicking on any links above. Some of them are affiliate links, but you don’t need to buy anything.
There already exist tools for performing this kind of analysis, eg. tradingview, but doing this analysis in Python enables more in-depth analysis.
I am not a trader and this blog post is not financial advice. This is purely introductory knowledge. The conclusion here can be misleading as we analyze the time period with immense growth.
Python 3
Jupyter Notebook
Pandas Data Analysis Library
Bokeh interactive visualization library
stock Statistics/Indicators Calculation Helper
We download daily Bitcoin data in USD on Bitstamp exchange. Other exchanges are also supported.
from_symbol = 'BTC'to_symbol = 'USD'exchange = 'Bitstamp'datetime_interval = 'day'
The cryptocompare API returns the following columns:
open, the price at which the period opened,
high, the highest price reached during the period,
low, the lowest price reached during the period,
close, the price at which the period closed,
volumefrom, the volume in the base currency that things are traded into,
volumeto, the volume in the currency that is being traded.
We download the data and store it to a file.
import requestsfrom datetime import datetimedef get_filename(from_symbol, to_symbol, exchange, datetime_interval, download_date):    return '%s_%s_%s_%s_%s.csv' % (from_symbol, to_symbol, exchange, datetime_interval, download_date)def download_data(from_symbol, to_symbol, exchange, datetime_interval):    supported_intervals = {'minute', 'hour', 'day'}    assert datetime_interval in supported_intervals,\        'datetime_interval should be one of %s' % supported_intervals    print('Downloading %s trading data for %s %s from %s' %          (datetime_interval, from_symbol, to_symbol, exchange))    base_url = 'https://min-api.cryptocompare.com/data/histo'    url = '%s%s' % (base_url, datetime_interval)    params = {'fsym': from_symbol, 'tsym': to_symbol,              'limit': 2000, 'aggregate': 1,              'e': exchange}    request = requests.get(url, params=params)    data = request.json()    return datadef convert_to_dataframe(data):    df = pd.io.json.json_normalize(data, ['Data'])    df['datetime'] = pd.to_datetime(df.time, unit='s')    df = df[['datetime', 'low', 'high', 'open',             'close', 'volumefrom', 'volumeto']]    return dfdef filter_empty_datapoints(df):    indices = df[df.sum(axis=1) == 0].index    print('Filtering %d empty datapoints' % indices.shape[0])    df = df.drop(indices)    return dfdata = download_data(from_symbol, to_symbol, exchange, datetime_interval)df = convert_to_dataframe(data)df = filter_empty_datapoints(df)current_datetime = datetime.now().date().isoformat()filename = get_filename(from_symbol, to_symbol, exchange, datetime_interval, current_datetime)print('Saving data to %s' % filename)df.to_csv(filename, index=False)
We read the data from a file so we don’t need to download it again.
import pandas as pddef read_dataset(filename):    print('Reading data from %s' % filename)    df = pd.read_csv(filename)    # change type from object to datetime    df.datetime = pd.to_datetime(df.datetime)     df = df.set_index('datetime')     df = df.sort_index() # sort by datetime    print(df.shape)    return dfdf = read_dataset(filename)
A trading strategy is a set of objective rules defining the conditions that must be met for trade entry and exit to occur.
We are going to apply Moving Average Convergence Divergence (MACD) trading strategy, which is a popular indicator used in technical analysis. MACD calculates two moving averages of varying lengths to identify trend direction and duration. Then, it takes the difference in values between those two moving averages (MACD line) and an exponential moving average (signal line) of those moving averages. Tradeview has a great blog post about MACD.
As we can see in the example below:
exit trade (sell) when MACD line crosses below the MACD signal line,
enter the trade (buy) when MACD line crosses above the MACD signal line.
We use stockstats package to calculate MACD.
from stockstats import StockDataFramedf = StockDataFrame.retype(df)df['macd'] = df.get('macd') # calculate MACD
stockstats adds 5 columns to the dataset:
close_12_ema is fast 12 days exponential moving average,
close_26_ema is slow 26 days exponential moving average,
macd is MACD line,
macds is the signal line,
macdh is MACD histogram.
We use bokeh interactive charts to plot the data.
The line graph shows daily closing prices with candlesticks (zoom in). A candlestick displays the high, low, opening and closing prices for a specific period. Tradeview has a great blog post about candlestick graph.
Below the line graph, we plot the MACD strategy with the MACD line (blue), a signal line (orange) and histogram (purple).
from math import pifrom bokeh.plotting import figure, show, output_notebook, output_fileoutput_notebook()datetime_from = '2016-01-01 00:00'datetime_to = '2017-12-10 00:00'def get_candlestick_width(datetime_interval):    if datetime_interval == 'minute':        return 30 * 60 * 1000  # half minute in ms    elif datetime_interval == 'hour':        return 0.5 * 60 * 60 * 1000  # half hour in ms    elif datetime_interval == 'day':        return 12 * 60 * 60 * 1000  # half day in msdf_limit = df[datetime_from: datetime_to].copy()inc = df_limit.close > df_limit.opendec = df_limit.open > df_limit.closetitle = '%s datapoints from %s to %s for %s and %s from %s with MACD strategy' % (    datetime_interval, datetime_from, datetime_to, from_symbol, to_symbol, exchange)p = figure(x_axis_type="datetime",  plot_width=1000, title=title)p.line(df_limit.index, df_limit.close, color='black')# plot macd strategyp.line(df_limit.index, 0, color='black')p.line(df_limit.index, df_limit.macd, color='blue')p.line(df_limit.index, df_limit.macds, color='orange')p.vbar(x=df_limit.index, bottom=[       0 for _ in df_limit.index], top=df_limit.macdh, width=4, color="purple")# plot candlestickscandlestick_width = get_candlestick_width(datetime_interval)p.segment(df_limit.index, df_limit.high,          df_limit.index, df_limit.low, color="black")p.vbar(df_limit.index[inc], candlestick_width, df_limit.open[inc],       df_limit.close[inc], fill_color="#D5E1DD", line_color="black")p.vbar(df_limit.index[dec], candlestick_width, df_limit.open[dec],       df_limit.close[dec], fill_color="#F2583E", line_color="black")output_file("visualizing_trading_strategy.html", title="visualizing trading strategy")show(p)
Follow me on Twitter, where I regularly tweet about Data Science and Machine Learning.