Skip to content

Instantly share code, notes, and snippets.

@ghost355
Last active January 28, 2021 16:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ghost355/83cfc0791ae3f4b41611b6d5c98e1f39 to your computer and use it in GitHub Desktop.
Save ghost355/83cfc0791ae3f4b41611b6d5c98e1f39 to your computer and use it in GitHub Desktop.
full history stock chart like IBD in multipage pdf
import json
from tqdm import tqdm
from bs4 import BeautifulSoup
import requests
import pandas_ta as ta
import yfinance as yf
import pandas as pd
import numpy as np
import mplfinance as mpf
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.dates as mdates
from matplotlib.backends.backend_pdf import PdfPages
# !!! you need set stock symbol, if you want set other index
# how many bars you want on each page, and how many bars overlaping you want
STOCK = 'TSLA'
INDEX = '^GSPC' # S&P500
bar_in_page = 220
shift = 35
# get stock data
def get_ohlc (stock, period = 'max', interval = '1d'):
df_ohlc = yf.Ticker(stock.upper()).history(actions = False, period = period, interval = interval)
return df_ohlc
df = get_ohlc(STOCK) # main DataFrame for join everything
df_idx = get_ohlc(INDEX)
df=df.join(df_idx['Close'], rsuffix='_idx')
# add TA columns
df.ta.sma(length=50,append=True)
df.ta.sma(length=200,append=True)
df.ta.sma(length=150,append=True)
df.ta.ema(length=20,append=True)
df['VOL_50'] = df.ta.sma(close=df['volume'], length=50)
# add RS-line column
stock_close = df['close']
ind_close = df['Close_idx']
df['rs_line'] = stock_close/ind_close*100 * stock_close.shift(60)/(stock_close/ind_close *100).shift(60)*0.68 # 0.68 for placing like in IBD chart
# add space to the right side of chart
dfpad = df.tail(2).copy()
dfpad.loc[:,:] = float('nan')
newdf = df.append(dfpad)
df = newdf
# todo - get financial info like EPS and Sales
def get_macro_info(stock="AAPL", period='q'):
pass
# make chunks for export in multiple pages pdf, I guess there is the way to do the same better
iter_list = list(range(len(df)))
#bar_in_page = 220
#shift = 35
chunks = [iter_list[i:i + bar_in_page] for i in range(0, len(iter_list), bar_in_page-shift)]
pages = [(x[0],x[-1]+1) for x in chunks] # pages[n][0] - slice start, pages[n][1] - slice end
# styling chart
mc = mpf.make_marketcolors(up='#2A3FE5', down='#DB39AD', inherit=True)
base_style = { 'axes.titlesize': 5,
'axes.labelsize': 5,
'lines.linewidth': 3,
'lines.markersize': 4,
'ytick.left': False,
'ytick.right': True,
'ytick.labelleft': False,
'ytick.labelright': True,
'xtick.labelsize': 6,
'ytick.labelsize': 6,
'axes.linewidth': 0.8,
'grid.alpha': 0.2}
ibd = mpf.make_mpf_style(marketcolors=mc, mavcolors=['green', 'red', 'black', 'blue'], y_on_right=True, rc=base_style)
def get_pdf(pages):
for (start,end) in pages:
### !!! temporary replace to chunks loop
df_slice = df[start:end]
print(start,end)
# ======== starting plot =======
# making grid of axis
egrid = (21,29)
fig = mpf.figure(style=ibd, figsize=(11, 8))
ax1 = plt.subplot2grid(egrid,(2,0),colspan=29,rowspan=15)
ax3 = plt.subplot2grid(egrid,(0,0),colspan=29,rowspan=2,sharex=ax1)
ax2 = plt.subplot2grid(egrid,(17,0),colspan=29,rowspan=4,sharex=ax1)
# Set locator intervals
lim_bottom =min(df_slice['rs_line'].min(), df_slice['SMA_200'].min())
lim_top = max(df_slice['rs_line'].max(), df_slice['SMA_200'].max(),df_slice['close'].max())
# Включаем видимость делений:
ax1.minorticks_on()
ax1.grid(which='major',color='k')
ax1.grid(which='minor',color='gray')
ax2.grid(which='major',color='gray')
ax2.tick_params(axis='x', which='major', pad = 8)
ax1.tick_params(axis='x', which='both',labelbottom= False, labeltop=False )
ax3.tick_params(axis='x', which='both',labelbottom= False, labeltop=False, bottom=False, top=True, direction='in')
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m'))
ax1.xaxis.set_major_locator(mticker.IndexLocator(base=30, offset=0))
ax1.xaxis.set_minor_formatter(mdates.DateFormatter('%d'))
ax1.xaxis.set_minor_locator(mticker.IndexLocator(base=5, offset=0))
maxprice = df_slice.high.max()
if maxprice > 100:
locator_size = 100
elif maxprice < 10:
locator_size = 1
else:
locator_size = 10
ax1.yaxis.set_major_locator(mticker.MultipleLocator(locator_size))
ax1.yaxis.set_minor_locator(mticker.MultipleLocator(locator_size/4))
ax2.yaxis.set_major_locator(mticker.MultipleLocator(df_slice['volume'].max()/5))
# additional lines
sp500 = mpf.make_addplot( df_slice['Close_idx'], color='black', width=0.9, panel=0, title=f'{STOCK.upper()} {df_slice.index[0]} - {df_slice.index[-1]}', ax=ax3)
vol50 = mpf.make_addplot(
df_slice['VOL_50'], panel=2, color='red', width=0.6, ax=ax2)
rs_line = mpf.make_addplot(
df_slice['rs_line'], ax=ax1, color='blue', width=1.2, alpha=0.35, panel=1)
sma200 = mpf.make_addplot(
df_slice['SMA_200'], ax=ax1,color='black', width=0.8, panel=1)
sma50 = mpf.make_addplot(
df_slice['SMA_50'], ax=ax1,color='red', width=0.6, panel=1)
sma150 = mpf.make_addplot(
df_slice['SMA_150'], ax=ax1,color='gray', width=0.2, alpha=0.8, panel=1)
ema20 = mpf.make_addplot(
df_slice['EMA_20'], ax=ax1,color='green', width=0.3, panel=1)
mpf.plot(df_slice, ax=ax1, volume=ax2,addplot=[sp500, rs_line, sma200, sma150, sma50, ema20, vol50], datetime_format="%b'20",tight_layout=True, xrotation=0,
scale_width_adjustment=dict(volume=0.2),ylim=(lim_bottom*0.1,lim_top*1.1))
# scale_width_adjustment=dict(volume=0.2), addplot=[sp500, rs_line, sma200, sma150, sma50, ema20, vol50],
# datetime_format="%b'20",tight_layout=True, xrotation=0, ylim=(lim_bottom*0.1,lim_top*1.1),update_width_config=dict(ohlc_linewidth=0.9,ohlc_ticksize=0.4)
# remove gaps among axis panels
plt.tight_layout(h_pad= -0.8)
fig.savefig(f'figure{end}.pdf')
get_pdf(pages)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment