Bitcoin technical analysis
import os
import time
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import warnings; warnings.filterwarnings('ignore')
df = pd.read_csv('BTC-USD.csv')
df.head(5)
Date | Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|---|
0 | 2014-09-17 | 465.864014 | 468.174011 | 452.421997 | 457.334015 | 457.334015 | 21056800.0 |
1 | 2014-09-18 | 456.859985 | 456.859985 | 413.104004 | 424.440002 | 424.440002 | 34483200.0 |
2 | 2014-09-19 | 424.102997 | 427.834991 | 384.532013 | 394.795990 | 394.795990 | 37919700.0 |
3 | 2014-09-20 | 394.673004 | 423.295990 | 389.882996 | 408.903992 | 408.903992 | 36863600.0 |
4 | 2014-09-21 | 408.084991 | 412.425995 | 393.181000 | 398.821014 | 398.821014 | 26580100.0 |
df.tail(5)
Date | Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|---|
3175 | 2023-05-28 | 26871.158203 | 28193.449219 | 26802.751953 | 28085.646484 | 28085.646484 | 1.454523e+10 |
3176 | 2023-05-29 | 28075.591797 | 28432.039063 | 27563.876953 | 27745.884766 | 27745.884766 | 1.518131e+10 |
3177 | 2023-05-30 | 27745.123047 | 28044.759766 | 27588.501953 | 27702.349609 | 27702.349609 | 1.325108e+10 |
3178 | 2023-05-31 | NaN | NaN | NaN | NaN | NaN | NaN |
3179 | 2023-06-01 | 27236.576172 | 27326.533203 | 27235.039063 | 27301.021484 | 27301.021484 | 1.596423e+10 |
df.describe()
Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|
count | 3179.000000 | 3179.000000 | 3179.000000 | 3179.000000 | 3179.000000 | 3.179000e+03 |
mean | 13432.217640 | 13763.078443 | 13068.545810 | 13439.410714 | 13439.410714 | 1.659098e+10 |
std | 16028.349128 | 16432.296940 | 15565.365494 | 16025.439960 | 16025.439960 | 1.962819e+10 |
min | 176.897003 | 211.731003 | 171.509995 | 178.102997 | 178.102997 | 5.914570e+06 |
25% | 741.108002 | 750.089508 | 733.108490 | 741.312989 | 741.312989 | 1.219535e+08 |
50% | 7500.700195 | 7680.430176 | 7349.120117 | 7514.470215 | 7514.470215 | 9.744636e+09 |
75% | 19510.696289 | 20034.084961 | 19129.965821 | 19545.489258 | 19545.489258 | 2.776349e+10 |
max | 67549.734375 | 68789.625000 | 66382.062500 | 67566.828125 | 67566.828125 | 3.509679e+11 |
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3180 entries, 0 to 3179
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Date 3180 non-null object
1 Open 3179 non-null float64
2 High 3179 non-null float64
3 Low 3179 non-null float64
4 Close 3179 non-null float64
5 Adj Close 3179 non-null float64
6 Volume 3179 non-null float64
dtypes: float64(6), object(1)
memory usage: 174.0+ KB
# find null values
df.isnull().sum()
Date 0
Open 1
High 1
Low 1
Close 1
Adj Close 1
Volume 1
dtype: int64
#find duplicate values
df.duplicated().sum()
0
df = df.dropna()
name='Bitcoin'
df['Date'] = pd.to_datetime(df['Date'])
df1=df[-300:].reset_index(drop=True)
df2=df
Candlestick
fig = go.Figure(go.Candlestick(x=df1['Date'],open=df1['Open']
,high=df1['High'],low=df1['Low'],close=df1['Close']))
fig.update_layout(title='Candlestick', yaxis_title='USD',width=800,height=500,)
fig.update_yaxes()
Exponential Moving Average
ewma = pd.Series.ewm
df1['rolling_ema_12'] = df1.Close.ewm(min_periods=12, span=12).mean()
df1['rolling_ema_26'] = df1.Close.ewm(min_periods=26, span=26).mean()
fig=make_subplots(specs=[[{"secondary_y":True}]])
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['Close'],mode='lines',name='Close'))
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['rolling_ema_12'], mode='lines', name='EMA_12'))
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['rolling_ema_26'], mode='lines', name='EMA_26'))
fig.update_layout(title='Exponential Moving Average', yaxis_title='USD',width=800,height=500,)
fig.update_yaxes()
fig.show()
MACD
df1['close_12EMA'] = ewma(df1["Close"], span=12).mean()
df1['close_26EMA'] = ewma(df1["Close"], span=26).mean()
df1['MACD'] = df1['close_12EMA'] - df1['close_26EMA']
df1['MACD_Signal'] = ewma(df1["MACD"], span=9).mean()
fig=make_subplots(specs=[[{"secondary_y":True}]])
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['Close'],mode='lines'
,name='Close'),secondary_y=True,)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['MACD'], mode='lines', name='MACD')
,secondary_y=False,)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['MACD_Signal'], mode='lines', name='MACD_Signal')
,secondary_y=False,)
fig.update_layout(autosize=False,width=800,height=400,title_text="MACD")
fig.update_xaxes(title_text="Date")
fig.update_yaxes(title_text="MACD",secondary_y=False)
fig.update_yaxes(title_text="USD",secondary_y=True)
fig.show()
fig=make_subplots(specs=[[{"secondary_y":True}]])
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['Close'],mode='lines'
,name='Close'),secondary_y=True,)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['MACD']-df1['MACD_Signal'], mode='lines',
name='MACD - MACD_Signal'),secondary_y=False,)
fig.update_layout(autosize=False,width=800,height=400,title_text="MACD - MACD_Signal")
fig.update_xaxes(title_text="Date")
fig.update_yaxes(title_text="MACD",secondary_y=False)
fig.update_yaxes(title_text="USD",secondary_y=True)
fig.show()
RSI
def rsiFunc(prices, n=14):
deltas = np.diff(prices)
seed = deltas[:n+1]
up = seed[seed>=0].sum()/n
down = -seed[seed<0].sum()/n
rs = up/down
rsi = np.zeros_like(prices)
rsi[:n] = 100. - 100./(1.+rs)
for i in range(n, len(prices)):
delta = deltas[i-1]
if delta>0:
upval = delta
downval = 0.
else:
upval = 0.
downval = -delta
up = (up*(n-1) + upval)/n
down = (down*(n-1) + downval)/n
rs = up/down
rsi[i] = 100. - 100./(1.+rs)
return rsi
df1['rsi_6'] = rsiFunc(df1['Close'].values, 6)
df1['rsi_14'] = rsiFunc(df1['Close'].values, 14)
df1['rsi_20'] = rsiFunc(df1['Close'].values, 20)
fig=make_subplots(specs=[[{"secondary_y":True}]])
fig.update_layout(title='RSI', yaxis_title='USD',width=800,height=400,)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['Close'],mode='lines'
,name='Close'), secondary_y=True,)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1[f'rsi_14'], mode='lines'
, name=f'rsi_14'),secondary_y=False)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1[f'rsi_20'], mode='lines'
, name=f'rsi_20'),secondary_y=False)
fig.update_xaxes(title_text="Date")
fig.update_yaxes(title_text="RSI",secondary_y=False)
fig.update_yaxes(title_text="USD",secondary_y=True)

Bollinger Band
window = 20
no_of_std = 2
df1[f'MA_{window}MA'] = df1['Close'].rolling(window=window).mean()
df1[f'MA_{window}MA_std'] = df1['Close'].rolling(window=window).std()
df1[f'MA_{window}MA_BB_high'] = df1[f'MA_{window}MA'] + no_of_std * df1[f'MA_{window}MA_std']
df1[f'MA_{window}MA_BB_low'] = df1[f'MA_{window}MA'] - no_of_std * df1[f'MA_{window}MA_std']
fig = go.Figure()
fig.update_layout(title='Bollinger Band', yaxis_title='USD',width=800,height=400,)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['Close'],mode='lines',name='Close'))
fig.add_trace(go.Scatter(x=df1['Date'], y=df1[f'MA_{window}MA'], mode='lines', name=f'BB_mean'))
fig.add_trace(go.Scatter(x=df1['Date'], y=df1[f'MA_{window}MA_BB_high']
, mode='lines', name=f'BB_high'))
fig.add_trace(go.Scatter(x=df1['Date'], y=df1[f'MA_{window}MA_BB_low']
, mode='lines', name=f'BB_low'))
fig.show()

Stochastic Oscillator
df1['Low_min']=df1['Low'].rolling(window=14,center=False).min()
df1['High_max']=df1['High'].rolling(window=14,center=False).max()
df1['STOK'] = ((df1['Close']-df1['Low_min'])/(df1['High_max']-df1['Low_min']))*100
df1['STOD'] = df1['STOK'].rolling(window=3,center=False).mean()
fig=make_subplots(specs=[[{"secondary_y":True}]])
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['Close'],mode='lines'
,name='Close'),secondary_y=False,)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['STOK'],mode='lines',name='STOK')
,secondary_y=True,)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['STOD'],mode='lines',name='STOD')
,secondary_y=True,)
fig.update_layout(title='Stochastic Oscillator', yaxis_title='USD',width=800,height=400,)
fig.show()
Parabolic SAR
def psar(dfw, iaf = 0.02, maxaf = 0.2):
length = len(dfw)
dates = (dfw['Date'])
high = (dfw['High'])
low = (dfw['Low'])
close = (dfw['Close'])
psar = dfw['Close'][0:len(dfw['Close'])]
psarbull = [None] * length
psarbear = [None] * length
bull = True
af = iaf
ep = dfw['Low'][0]
hp = dfw['High'][0]
lp = dfw['Low'][0]
for i in range(2,length):
if bull:
psar[i] = psar[i - 1] + af * (hp - psar[i - 1])
else:
psar[i] = psar[i - 1] + af * (lp - psar[i - 1])
reverse = False
if bull:
if dfw['Low'][i] < psar[i]:
bull = False
reverse = True
psar[i] = hp
lp = dfw['Low'][i]
af = iaf
else:
if dfw['High'][i] > psar[i]:
bull = True
reverse = True
psar[i] = lp
hp = dfw['High'][i]
af = iaf
if not reverse:
if bull:
if dfw['High'][i] > hp:
hp = dfw['High'][i]
af = min(af + iaf, maxaf)
if dfw['Low'][i - 1] < psar[i]:
psar[i] = dfw['Low'][i - 1]
if dfw['Low'][i - 2] < psar[i]:
psar[i] = dfw['Low'][i - 2]
else:
if dfw['Low'][i] < lp:
lp = dfw['Low'][i]
af = min(af + iaf, maxaf)
if dfw['High'][i - 1] > psar[i]:
psar[i] = dfw['High'][i - 1]
if dfw['High'][i - 2] > psar[i]:
psar[i] = dfw['High'][i - 2]
if bull:
psarbull[i] = psar[i]
else:
psarbear[i] = psar[i]
dfw['psar'] = psar
dfw['psarbear'] = psarbear
dfw['psarbull'] = psarbull
psar(df1)
fig=make_subplots(specs=[[{"secondary_y":True}]])
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['psarbear'],mode='lines'
,name='psarbear'),secondary_y=True,)
fig.add_trace(go.Scatter(x=df1['Date'], y=df1['psarbull'],mode='lines'
,name='psarbull'),secondary_y=True,)
fig.update_layout(title='Parabolic SAR', yaxis_title='USD',width=800,height=400,)
fig.show()
Ichimoku Clouds
from datetime import datetime
from datetime import timedelta
df1['Date']=df1['Date'].astype(str)
latest0 = datetime.strptime(df1['Date'].max(),'%Y-%m-%d').date()
dates0=[]
for i in range(1,40):
dates0+=[(latest0+timedelta(i)).strftime('%Y-%m-%d')]
ADD=pd.DataFrame(columns=df1.columns)
ADD['Date']=dates0
df1=pd.concat([df1,ADD],axis=0).reset_index(drop=True)
df1['base9']=(df1['High'].rolling(window=9).max()+df1['High'].rolling(window=9).min())/2
df1['base26']=(df1['High'].rolling(window=26).max()+df1['High'].rolling(window=26).min())/2
df1['base52']=(df1['High'].rolling(window=52).max()+df1['High'].rolling(window=52).min())/2
df1['leading span1']=(df1['base9']/2+df1['base52']/2).shift(26)
df1['leading span2']=df1['base52'].shift(26)
df1['lagging span']=df1['Close'].shift(-26)
fig=make_subplots(specs=[[{"secondary_y":False}]])
fig.add_trace(go.Scatter(x=df1['Date'],y=df1['Close'],name='Close'),secondary_y=False,)
fig.add_trace(go.Scatter(x=df1['Date'],y=df1['leading span1']
,name='leading span1'),secondary_y=False,)
fig.add_trace(go.Scatter(x=df1['Date'],y=df1['leading span2']
,name='leading span2'),secondary_y=False,)
fig.add_trace(go.Scatter(x=df1['Date'],y=df1['lagging span']
,name='lagging span'),secondary_y=False,)
fig.update_layout(autosize=False,width=800,height=400,title_text='Ichimoku Clouds')
fig.update_xaxes(title_text="Date")
fig.update_yaxes(title_text="USD",secondary_y=False)
fig.update_yaxes(title_text="USD",secondary_y=True)
fig.show()
Yearly Growth (original)
df2['Close MA20'] = df2['Close'].rolling(window=20).mean()
df2['Close MA20 shift year']=df2['Close MA20'].shift(253)
df2['Yearly Growth']=(df2['Close MA20']-df2['Close MA20 shift year'])
*100/df2['Close MA20 shift year']
fig=make_subplots(specs=[[{"secondary_y":True}]])
fig.add_trace(go.Scatter(x=df2['Date'][500:], y=df2['Close MA20'][500:],mode='lines',
name='Close MA20'),secondary_y=False,)
fig.add_trace(go.Scatter(x=df2['Date'][500:], y=df2['Yearly Growth'][500:],mode='lines',
name='Yearly Growth'),secondary_y=True,)
fig.update_layout(title='Close MA20 and Yearly Growth',width=800,height=400,)
fig.update_xaxes(title_text="Date")
fig.update_yaxes(title_text="Close MA20 USD",secondary_y=False)
fig.update_yaxes(title_text="Yearly Growth %",secondary_y=True)
fig.show()
The full example is on my Kaggle account. This is the link.
https://www.kaggle.com/code/mixmore/bitcoin-technical-analysis
This is just an explanation of the example on Kaggle.