比特幣市場到底是如何運作的?數字加密貨幣(cryptocurrency)跌宕起伏的原因是什麼?不同的山寨幣(altcoins)市場之間是緊密聯絡還是各自為營?我們該如何預測接下來將發生什麼?
關於數字加密貨幣(如:比特幣和以太坊)的文章鋪天蓋地,數百個自詡專家的作者各自發表著他們對比特幣未來的猜想。而用來支援他們觀點的這些分析中強有力的資料和統計學基礎卻乏善可陳。
這篇文章的目的是簡單介紹“如何用Python來分析數字加密貨幣”。我們將用簡單的Python程式碼來檢索、分析和視覺化不同的數字貨幣資料。在這個過程中,我們將揭示一個有趣的趨勢:這些不穩定的市場是如何運作的,它們又是如何發展的。
這不是一篇解釋數字加密貨幣是什麼的科普貼(如果你需要,我推薦這篇很棒的概述),這也不是一篇講哪個貨幣會升值、哪個會貶值的觀點性文章。相反,在這篇教程中,我們所關心的只是獲取原始資料,並揭示隱藏在數字中的故事。
步驟1 - 配置你的資料實驗室
這篇教材適合在不同技能水平上的愛好者、工程師和資料科學家們。要求的技能只是對Python有基礎的瞭解,以及知道如何用命令建立一個專案。
包含執行結果的notebook完整版本可以在這裡下載。
步驟1.1 - 安裝 Anaconda
安裝這個專案所需的所有相關環境,最簡單的辦法就是用Anaconda。它是一個打包的Python資料科學生態系統和依賴管理器。
推薦使用下面的官方安裝指南來安裝Anaconda https://www.continuum.io/downloads
如果你是一個高階使用者,而你不需要使用Anaconda,那也完全沒有問題。我會假設你在安裝必須的依賴環境時不需要幫助,你可以直接跳到第二部分。
步驟1.2 - 建立一個Anaconda專案環境
當Anaconda安裝完成後,我們需要建立一個新的環境來管理我們的依賴包。執行 conda create --name cryptocurrency-analysis python=3 來為我們的專案建立一個新的Anaconda環境。接下來,執行source activate cryptocurrency-analysis ( Linux/macOS 系統) 或者 activate cryptocurrency-analysis (windows 系統) 來啟用這個環境。
最後,執行 conda install numpy pandas nb_conda jupyter plotly quandl 來為這個環境安裝所需的依賴包。完成這些需要幾分鐘的時間。
為什麼要用環境?如果你打算在你的電腦上執行很多Python專案,那麼分開不同專案的依賴包(軟體庫和包)來避免衝突是很有幫助的。
Ananconda會為每一個專案的依賴包建立一個特殊的環境目錄,使得所有包都能妥善地被管理和區分。
步驟1.3 - 啟動一個互動的Jupyter Notebook
當環境和依賴包都安裝好之後,執行 jupyter notebook 來啟動 iPython 核心,然後用你的瀏覽器訪問http://localhost:8888/ 。建立一個新的Python notebook,確保它使用的核心是Python [conda env:cryptocurrency-analysis]。
步驟1.4 – 匯入依賴包
當你開啟了一個空的Jupyter notebook,我們要做的第一件事就是匯入所需的依賴包。
import os import numpy as np import pandas as pd import pickle import quandl from datetime import datetime
我們還要匯入Plotly來啟用離線模式。
import plotly.offline as py import plotly.graph_objs as go import plotly.figure_factory as ff py.init_notebook_mode(connected=True)
步驟2 - 獲得比特幣的價格資料
一切就緒,我們可以開始獲取要分析的資料了。首先,我們要用Quandl的免費比特幣介面來獲得比特幣的價格資料。
步驟2.1 - 編寫Quandl幫助函式
為了方便資料獲取,我們要編寫一個函式來下載和同步來自Quandl(https://www.quandl.com/ 號稱金融資料界的維基百科)的資料。
def get_quandl_data(quandl_id): '''Download and cache Quandl dataseries''' cache_path = '{}.pkl'.format(quandl_id).replace('/','-') try: f = open(cache_path, 'rb') df = pickle.load(f) print('Loaded {} from cache'.format(quandl_id)) except (OSError, IOError) as e: print('Downloading {} from Quandl'.format(quandl_id)) df = quandl.get(quandl_id, returns="pandas") df.to_pickle(cache_path) print('Cached {} at {}'.format(quandl_id, cache_path)) return df
我們用pickle來序列化,把下載的資料存成檔案,這樣程式碼就不會在每次執行的時候重新下載同樣的資料。這個函式將返回Pandas資料框(Dataframe)格式的資料。如果你對資料框不太熟悉,你可以把它想成是強大的電子表格。
步驟2.2 – 抓取Kraken交易所定價資料
我們首先來獲取Kraken比特幣交易所的歷史比特幣匯率。
# Pull Kraken BTC price exchange data btc_usd_price_kraken = get_quandl_data('BCHARTS/KRAKENUSD')
我們可以用head()方法來檢視資料框的前五行。
btc_usd_price_kraken.head()
接下來,我們要做一個簡單的圖表,以此來快速地通過視覺化的方法驗證資料基本正確。
# Chart the BTC pricing data btc_trace = go.Scatter(x=btc_usd_price_kraken.index, y=btc_usd_price_kraken['Weighted Price']) py.iplot([btc_trace])
這裡,我們用Plotly 來完成視覺化部分。相對於使用一些更成熟的Python資料視覺化庫,例如Matplotlib ,用Plotly是一個不那麼傳統的選擇,但我認為Plotly是一個不錯的選擇,因為它可以呼叫D3.js的充分互動式圖表。這些圖表有非常漂亮的預設設定,易於探索,而且非常方便嵌入到網頁中。
我們可以將生成的圖表與公開可用的比特幣價格圖表(如Coinbase上的圖表)進行比較,作為一個快速的完整性檢查,驗證下載的資料是否合理。
步驟2.3 從更多的比特幣交易所抓取價格資料
你可能已經注意到,上面的資料集中存在資料缺失現象--特別是在2014年末和2016年初。在Kraken交易所的資料集中,這種資料缺失情況尤為明顯。我們當然不希望這些資料會影響到我們對價格的全面分析。
比特幣交易所的特點是,供需關係決定比特幣的價格。因而,沒有哪個交易的價格所能夠成為市場的“主流價格”。為了解決這個問題,以及剛剛提到的資料缺失問題(可能是由於技術性斷電和資料的差錯),我們將從三家主要的比特幣交易所抓取資料,進而計算出平均的比特幣價格指數。
首先,我們把各個交易所的資料下載到到由字典型別的資料框中。
# Pull pricing data for 3 more BTC exchanges exchanges=['COINBASE','BITSTAMP','ITBIT']exchange_data={}exchange_data['KRAKEN']=btc_usd_price_kraken for exchange in exchanges:exchange_code='BCHARTS/{}USD'.format(exchange)btc_exchange_df=get_quandl_data(exchange_code)exchange_data[exchange]=btc_exchange_df
步驟2.4 將所有價格資料整合到單一資料框之中
接下來,我們將要定義一個簡單的函式,把各個資料框中共有的列合併為一個新的組合資料框。
def merge_dfs_on_column(dataframes, labels, col):'''Merge a single column of each dataframe into a new combined dataframe'''series_dict={} for index in range(len(dataframes)):series_dict[labels[index]]=dataframes[index][col]return pd.DataFrame(series_dict)
現在,基於各個資料集的“加權價格”列,把所有的資料框整合到一起。
# Merge the BTC price dataseries' into a single dataframebtc_usd_datasets=merge_dfs_on_column(list(exchange_data.values()), list(exchange_data.keys()),'Weighted Price')
最後,可以使用“tail()”方法,檢視合併後資料的最後五行,以確保資料整合成功。
btc_usd_datasets.tail()
看起來,這些資料符合我們的預期:資料範圍大致相同,只是基於各個交易所的供求關係而略有不同。
步驟2.5 價格資料的視覺化
從邏輯上來說,下一步就是通過視覺化,將這些資料進行比對。為此,我們需要先定義一個輔助函式,通過提供單行命令使用資料製作圖表。
def df_scatter(df, title,seperate_y_axis=False,y_axis_label='', scale='linear',initial_hide=False):'''Generate a scatter plot of the entire dataframe'''label_arr= list(df)series_arr= list(map(lambda col:df[col],label_arr)) layout =go.Layout(title=title, legend=dict(orientation="h"), xaxis=dict(type='date'), yaxis=dict(title=y_axis_label, showticklabels=notseperate_y_axis, type=scale))y_axis_config=dict(overlaying='y', showticklabels=False, type=scale ) visibility ='visible'ifinitial_hide: visibility ='legendonly'# Form Trace For Each Seriestrace_arr=[]for index, series in enumerate(series_arr): trace =go.Scatter(x=series.index, y=series, name=label_arr[index], visible=visibility)# Add seperate axis for the seriesif seperate_y_axis: trace['yaxis']='y{}'.format(index +1) layout['yaxis{}'.format(index +1)]=y_axis_configtrace_arr.append(trace) fig =go.Figure(data=trace_arr, layout=layout)py.iplot(fig)
為簡便起見,我不會過多探討這個輔助函式的工作原理。如果想了解更多,請檢視Pandas 和 Plotly的說明檔案。
現在,我們就可以輕鬆製作比特幣價格資料的圖形了!
# Plot all of the BTC exchange pricesdf_scatter(btc_usd_datasets,'Bitcoin Price (USD) By Exchange')
步驟2.6 清理並加總價格資料
從以上圖形中可以看到,儘管這四個系列資料遵循大致相同的路徑,但其中還是有一些不規則的變化,我們將設法清除這些異常變化。
在2012-2017年的時間段中,我們知道比特幣的價格從來沒有等於零的時候,所以我們先去除資料框中所有的零值。
# Remove "0" valuesbtc_usd_datasets.replace(0,np.nan,inplace=True)
在重新構建資料框之後,我們可以看到更加清晰的圖形,沒有缺失資料的情況了。
# Plot the revised dataframedf_scatter(btc_usd_datasets,'Bitcoin Price (USD) By Exchange')
我們現在可以計算一個新的列:所有交易所的比特幣日平均價格。
# Calculate the average BTC price as a new columnbtc_usd_datasets['avg_btc_price_usd']=btc_usd_datasets.mean(axis=1)
新的一列就是比特幣的價格指數!我們再把它畫出來,以核對該資料看起來是否有問題。
# Plot the average BTC pricebtc_trace=go.Scatter(x=btc_usd_datasets.index, y=btc_usd_datasets['avg_btc_price_usd'])py.iplot([btc_trace])
太好了,看起來確實沒有問題。稍後,我們將繼續使用這個加總的價格序列資料,以便能夠確定其他數字貨幣與美元之間的兌換匯率。
步驟3 獲取山寨幣(Altcoins)價格
現在,我們已經有了比特幣價格的時間序列資料了。接下來我們來看看非比特幣的數字加密貨幣的一些資料,一般是指山寨幣(Altcoins)。
步驟3.1 定義Poloniex交易所的API輔助函式
我們將使用Poloniex API來獲取數字加密貨幣交易的資料資訊。我們定義了兩個輔助函式來獲取山寨幣的相關資料,這兩個函式主要是通過這個API下載和快取JSON資料。
首先,我們定義函式get_json_data,它將從給定的URL中下載和快取JSON資料。
defget_json_data(json_url,cache_path):'''Download and cache JSON data, return as a dataframe.'''try: f = open(cache_path,'rb')df=pickle.load(f)print('Loaded {} from cache'.format(json_url))except(OSError,IOError) as e:print('Downloading {}'.format(json_url))df=pd.read_json(json_url)df.to_pickle(cache_path)print('Cached {} at {}'.format(json_url,cache_path))return df
然後,我們定義一個新的函式,該函式將產生Poloniex API的HTTP請求,並呼叫剛剛定義的get_json_data函式,以儲存呼叫的資料結果。
base_polo_url='https://poloniex.com/public?command=returnChartData¤cyPair={}&start={}&end={}&period={}'start_date=datetime.strptime('2015-01-01','%Y-%m-%d')# get data from the start of 2015end_date=datetime.now()# up until todaypediod=86400# pull daily data (86,400 seconds per day)def get_crypto_data(poloniex_pair):'''Retrieve cryptocurrency data from poloniex'''json_url=base_polo_url.format(poloniex_pair,start_date.timestamp(),end_date.timestamp(),pediod)data_df=get_json_data(json_url,poloniex_pair)data_df=data_df.set_index('date')return data_df
上述函式將抽取加密貨幣配對字元程式碼(比如“BTC_ETH”),並返回包含兩種貨幣歷史兌換匯率的資料框。
步驟3.2 從Poloniex下載交易資料
絕大多數山寨幣都無法使用美元直接購買,個人要想獲取這些電子貨幣,通常都得先買比特幣,再根據加密貨幣兌換匯率,兌換成山寨幣。因而,我們就得下載每一種加密貨幣兌換比特幣的兌換匯率,然後再使用現有比特幣價格資料轉換成美元。
我們會下載9種排名靠前的加密貨幣交易資料:Ethereum,Litecoin,Ripple,Ethereum Classic,Stellar,Dash,Siacoin,Monero,和NEM。
altcoins=['ETH','LTC','XRP','ETC','STR','DASH','SC','XMR','XEM']altcoin_data={} for altcoin in altcoins:coinpair='BTC_{}'.format(altcoin)crypto_price_df=get_crypto_data(coinpair)altcoin_data[altcoin]=crypto_price_df
現在,我們有了包含9個資料框的字典,每種都包含山寨幣與比特幣之間的歷史日平均價格資料。
我們可以通過Ethereum價格表格的最後幾行,來判定資料是否可用。
altcoin_data['ETH'].tail()
步驟3.3 – 統一貨幣單位:美元
現在,我們可以將BTC-山寨幣匯率資料與我們的比特幣價格指數結合,來直接計算每一個山寨幣的歷史價格(單位:美元)。
# Calculate USD Price as a new column in each altcoin dataframe for altcoin in altcoin_data.keys(): altcoin_data[altcoin]['price_usd'] = altcoin_data[altcoin]['weightedAverage'] * btc_usd_datasets['avg_btc_price_usd']
此處,我們為每一個山寨幣的資料框新增一列儲存其相應的美元價格。
接著,我們可以重新使用之前定義的函式merge_dfs_on_column,來建立一個合併的資料框,整合每種電子貨幣的美元價格。
# Merge USD price of each altcoin into single dataframe combined_df = merge_dfs_on_column(list(altcoin_data.values()), list(altcoin_data.keys()), 'price_usd')
就是如此簡單!
現在讓我們同時把比特幣價格作為最後一欄新增到合併後的資料框中。
# Add BTC price to the dataframe combined_df['BTC'] = btc_usd_datasets['avg_btc_price_usd']
現在我們有一個唯一的資料框,它包含了我們正在驗證的十種電子貨幣的每日美元價格。
我們重新呼叫之前的函式df_scatter,以圖表形式展現全部山寨幣的相應價格。
# Chart all of the altocoin prices df_scatter(combined_df, 'Cryptocurrency Prices (USD)', seperate_y_axis=False, y_axis_label='Coin Value (USD)', scale='log')
看起來不錯!這張圖為我們展現了每一種貨幣兌換匯率在過去幾年的變化情況的一個全貌。
在這裡我們使用了對數規格的y軸,在同一繪圖上比較所有貨幣。你也可以嘗試其他不同的引數值(例如scale='linear'),可以從不同視角理解資料。
步驟3.4- 執行相關性分析
你可能注意到電子貨幣的匯率看上去似乎是相關的,儘管他們的貨幣價值相差很大,而且波動性很高。尤其是從2017年4月的迅猛上漲開始,甚至很多的小波動似乎都與整個市場的波動同步出現。
當然,有資料支撐的結論比依據影像而產生的直覺更有說服力。
我們可以利用Pandas corr()函式來驗證上述的相關性假設。該檢驗手段為資料框的每一欄計算了其對應另一欄的皮爾森相關係數。
8/22/2017修訂說明-這部分的修改是為了在計算相關係數時使用每日回報率而不是價格的絕對值。
基於一個非穩態時間序列(例如原始的價格資料)直接計算可能會導致相關性係數的偏差。針對此問題,我們的解決方案是使用pct_change()方法,將資料框中的每一個的價格絕對值轉化為相應的日回報率。
首先,我們來計算2016年的相關係數。
# Calculate the pearson correlation coefficients for cryptocurrencies in 2016 combined_df_2016 = combined_df[combined_df.index.year == 2016] combined_df_2016.pct_change().corr(method='pearson')
上面這張圖顯示的都是相關係數。係數接近1或-1,分別意味著這一序列是強正相關的,或逆相關的,相關係數趨近於0則說明相應物件並不相關,他們的波動是相互獨立的。
為了更視覺化的展示結果,我們建立了一個新的視覺化的幫助函式。
def correlation_heatmap(df, title, absolute_bounds=True): '''Plot a correlation heatmap for the entire dataframe''' heatmap = go.Heatmap( z=df.corr(method='pearson').as_matrix(), x=df.columns, y=df.columns, colorbar=dict(title='Pearson Coefficient'), ) layout = go.Layout(title=title) if absolute_bounds: heatmap['zmax'] = 1.0 heatmap['zmin'] = -1.0 fig = go.Figure(data=[heatmap], layout=layout) py.iplot(fig) correlation_heatmap(combined_df_2016.pct_change(), "Cryptocurrency Correlations in 2016")
圖示為2016年的電子貨幣相關係數
此處,深紅色的數值代表強相關性(每一種貨幣顯然是與其自身高度相關的),深藍色的數值表示強逆相關性。所有介於中間的顏色-淺藍/橙/灰/茶色-其數值代表不同程度的弱相關或不相關。
這張圖表說明了什麼呢?關鍵在於,它說明了不同的數字加密貨幣價格在2016年間的波動情況,幾乎沒有統計上的顯著相關性。
現在,為了驗證我們的假設-電子貨幣在近幾個月的相關性增強,接下來,我們將使用從2017年開始的資料來重複同樣的測試。
combined_df_2017 = combined_df[combined_df.index.year == 2017] combined_df_2017.pct_change().corr(method='pearson')
上圖似乎顯示了更強的相關性。但這些資料可以單獨作為投資決策的依據嗎?答案是否定的。
然而值得注意得是,在這張圖上幾乎所有的電子貨幣相互之間都變得相關性更強了。
correlation_heatmap(combined_df_2017.pct_change(), "Cryptocurrency Correlations in 2017")
圖:2017年的電子貨幣相關性
這是不是相當有趣呢!?
為什麼?
好問題!其實,我也不是很確定。。。
我的第一反應是,對衝基金最近開始公開在電子貨幣市場交易[1][2]。這些基金持有遠超於普通交易人的大量資本,當一支基金如果在多種加密貨幣間對衝自己的投入資本,然後根據獨立變數(比如說,股票市場)對每一種貨幣使用相似的交易策略。如果從這個角度來看,出現這種越來越強的相關性趨勢是合理的。
更深度的理解XRP和STR
例如,從上圖中可以很明顯看出XRP(Ripple的代幣)是與其他電子貨幣相關性最低的。但這裡有一個值得注意的例外是STR(Stellar的代幣,官方名字是"Lumens"),與XRP有強相關關係(相關係數:0.62)。
有趣的是,Stellar和Ripple是非常相似的金融科技平臺,他們都旨在減少銀行間跨國轉賬時的繁瑣步驟。
可想而知,考慮到區塊鏈服務使用代幣的相似性,一些大玩家以及對衝基金可能會對他們在Stellar和Ripple上的投資使用相似的交易戰略。這可能就是為什麼XRP相比其他電子貨幣,與STR有更強相關性的原因。
快訊-我是Chipper的合作人之一。Chipper是一家使用Stella的非常早期的初創企業,旨在顛覆非洲的小額匯款行業。
現在,到你了!
然而,這一解釋在很大程度上是推測性的,可能你會做的更好。基於我們已經奠定的基礎,你有成百上千條不同的方法可以繼續探索資料中蘊藏的故事。
可以考慮從以下思路入手:
為整個分析新增更多加密貨幣的資料
調整相關性分析的時間範圍和顆粒度,以得到優化的或粗粒度的趨勢檢視。
從交易量或區塊鏈資料探勘集中尋找趨勢。相較於原始的價格資料,如果你想預測未來價格波動,你可以更需要買/賣量的比率資料。
在股票、商品、法定貨幣上加入價格資料來決定他們當中哪一項與電子貨幣具有相關性(但是,別忘了那句老話“相關不蘊含因果”)
使用Event Registry, GDELT,以及Google Trends來量化圍繞著特定電子貨幣的“熱詞”數量。
利用資料訓練一個預測性機器學習模型,來預測明日價格。如果你有更大的雄心壯志,你甚至可以考慮嘗試用迴圈神經網路(RNN)進行上述訓練。
利用你的分析來建立一個自動化的交易機器人,通過對應的應用程式設計介面(API),應用在例如“Poloniex”或“Coinbase”的交易網站上。請小心:一個效能欠佳的機器人可以輕易地讓你的資產瞬間灰飛煙滅。