罪滅ぼし
Bitcoin Bubble IndexがツイッターのAPIの変更のせいで使えなくなってるにも関わらずそれに気づかず延々とこのインジケーターを推してしまっていたので大反省してちゃんと使えるインジケーターに自分で修正しました。
元データ
Chao Maが作ったインジケータでGitHubにて公開されています。今回これを参考にコードを準備しました。
バブル・インデックスのコード
データを取得する部分はオリジナルコードとGoogleTrendsを利用しました。
import requests
from bs4 import BeautifulSoup
url_price = 'https://bitinfocharts.com/comparison/bitcoin-price.html'
url_sentaddr = 'https://bitinfocharts.com/comparison/activeaddresses-btc.html'
url_transaction = 'https://bitinfocharts.com/comparison/bitcoin-transactions.html'
url_difficulty = 'https://bitinfocharts.com/comparison/bitcoin-difficulty.html'
def download(url, var):
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
scripts = soup.find_all("script")
for script in scripts:
if 'd = new Dygraph(document.getElementById("container")' in str(script.string):
StrList = str(script.string)
StrList = '[[' + StrList.split('[[')[-1]
StrList = StrList.split(']]')[0] + ']]'
with open(var + '.txt', 'w') as f:
f.write(StrList)
download(url_price, 'price')
download(url_sentaddr, 'sentaddr')
download(url_transaction, 'transaction')
download(url_difficulty, 'difficulty')
インジケータデータ生成を生成するコードです。
import collections
import datetime
import json
import math
import pandas as pd
import re
def parse_strlist(sl):
clean = re.sub(r"[\[\],\s]", "", sl)
splitted = re.split(r"[\'\"]", clean)
values_only = [s for s in splitted if s != '']
return values_only
def read_datafile(filename):
result = []
file = open(filename, "r")
txt_data = file.read()
txt_data = txt_data[1:][:-1]
key = ''
value = ''
data = txt_data.split(',')
for i in range(len(data)):
if i % 2 == 0:
key = data[i].replace('[new Date("', '').replace('")', '')
date = datetime.datetime.strptime(key, '%Y/%m/%d')
if date < datetime.datetime(2010, 7, 17):
continue
else:
if date < datetime.datetime(2010, 7, 17):
continue
value = data[i].replace(']', '')
result.append(key + ":" + value)
return result
def add_missing_data(start_date, end_date, init_value, scale_factor):
result = []
index = 0
while True:
date = get_day(start_date, index)
if date == end_date:
break
init_value += init_value * scale_factor
result.append(date + ':' + str(init_value))
index += 1
return result
def get_day(start_date, gap_day):
tmp = datetime.datetime.strptime(start_date, '%Y/%m/%d')
target_date = (tmp + datetime.timedelta(days=gap_day)).strftime('%Y/%m/%d')
return target_date
def get_bubble_index(price, growth_60_day, hot_keywords, difficulty_value, sentaddr_value, transaction_value):
bubble_index_0 = 5000 * price / (sentaddr_value + difficulty_value / 10000000 + transaction_value)
bubble_index_1 = growth_60_day * math.pi + hot_keywords / math.pi
return bubble_index_0 + bubble_index_1 / 10.0 - 30.0
def process_data_with_google_trends():
google_trends_data = pd.read_csv('mutiltimeline.csv', skiprows=1)
google_trends_data['Month'] = pd.to_datetime(google_trends_data['Month'])
google_trends_data.set_index('Month', inplace=True)
price = read_datafile('price.txt')
difficulty = read_datafile('difficulty.txt')
sentaddr = read_datafile('sentaddr.txt')
transaction = read_datafile('transaction.txt')
json_data = {}
json_data['date'] = []
json_data['price'] = []
json_data['growth_60_day'] = []
json_data['hot'] = []
json_data['bubble'] = []
last_price = 0.0
accumulate_60 = 0.0
my_q = collections.deque()
for data in price:
key, value = data.split(':')
json_data['date'].append(key)
json_data['price'].append('%0.2f' % float(value))
if last_price == 0.0:
last_price = float(value)
if len(my_q) >= 60:
accumulate_60 -= my_q.popleft()
day_growth = (float(value) - last_price) / last_price
accumulate_60 += day_growth
my_q.append(day_growth)
last_price = float(value)
json_data['growth_60_day'].append(int(accumulate_60 * 100))
for date in json_data['date']:
try:
trend_value = google_trends_data.loc[pd.to_datetime(date), 'bitcoin: (Worldwide)']
trend_value = 0 if trend_value == '<1' else int(trend_value)
except KeyError:
trend_value = 0
json_data['hot'].append(trend_value)
while len(json_data['hot']) < len(json_data['price']):
json_data['hot'].append(json_data['hot'][-1])
assert len(json_data['hot']) == len(json_data['price'])
def extend_data(data, json_data, data_name):
while len(data) < len(json_data['price']):
data_value = data[-1].split(':')[1]
data.append(json_data['date'][len(data)] + ':' + data_value)
return data
difficulty = extend_data(difficulty, json_data, 'difficulty')
sentaddr = extend_data(sentaddr, json_data, 'sentaddr')
transaction = extend_data(transaction, json_data, 'transaction')
for i in range(len(price)):
difficulty_key, difficulty_value = difficulty[i].split(':')
assert difficulty_key == json_data['date'][i]
sentaddr_key, sentaddr_value = sentaddr[i].split(':')
assert sentaddr_key == json_data['date'][i]
transaction_key, transaction_value = transaction[i].split(':')
assert transaction_key == json_data['date'][i]
difficulty_value = 0.0 if difficulty_value == 'null' else float(difficulty_value)
sentaddr_value = 0.0 if sentaddr_value == 'null' else float(sentaddr_value)
transaction_value = 0.0 if transaction_value == 'null' else float(transaction_value)
bubble_index = get_bubble_index(
float(json_data['price'][i]),
float(json_data['growth_60_day'][i]),
float(json_data['hot'][i]),
float(difficulty_value),
float(sentaddr_value),
float(transaction_value))
json_data['bubble'].append(int(bubble_index))
assert len(json_data['bubble']) == len(json_data['price'])
with open('../data.json', "w") as file:
file.write("data = '")
file.write(json.dumps(json_data))
file.write("'")
if __name__ == '__main__':
process_data_with_google_trends()
データを元にチャート生成するコードです。
import json
import matplotlib.pyplot as plt
with open('../data.json', 'r') as file:
data = file.read()
json_data = json.loads(data.lstrip("data = '").rstrip("'"))
dates = json_data['date']
bubble_indices = json_data['bubble']
plt.plot(dates, bubble_indices)
plt.xlabel('Date')
plt.ylabel('Bubble Index')
plt.title('Bitcoin Bubble Index Over Time')
plt.xticks(dates[::len(dates)//10], rotation=45)
plt.tight_layout()
plt.show()
実行結果
実行結果を見る限り、まだまだバブル水準とはいえませんね。やったね。
改善版
ビットコイン価格と同時表記の方がいいと思って改良しました。
import json
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.ticker import MaxNLocator
from datetime import datetime
with open('../data.json', 'r') as file:
data = file.read()
json_data = json.loads(data.lstrip("data = '").rstrip("'"))
dates = json_data['date']
bubble_indices = json_data['bubble']
prices = json_data['price']
dates = [datetime.strptime(date, "%Y/%m/%d") for date in dates]
prices = [float(price) for price in prices]
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True)
ax1.set_ylabel('Bitcoin Price (USD)', color='tab:blue')
ax1.plot(dates, prices, color='tab:blue', label='Bitcoin Price', linewidth=1.5)
ax1.tick_params(axis='y', labelcolor='tab:blue')
ax1.set_title('Bitcoin Price')
ax2.set_xlabel('Date')
ax2.set_ylabel('Bubble Index', color='tab:red')
ax2.plot(dates, bubble_indices, color='tab:red', label='Bubble Index', linewidth=1.5, linestyle='--')
ax2.tick_params(axis='y', labelcolor='tab:red')
ax2.set_title('Bubble Index')
ax2.xaxis.set_major_locator(MaxNLocator(nbins=10))
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
fig.autofmt_xdate(rotation=45)
ax1.grid(True)
ax2.grid(True)
ax1.legend(loc='upper left')
ax2.legend(loc='upper left')
fig.tight_layout()
plt.show()