Skip to content

Instantly share code, notes, and snippets.

@ghost355
Last active January 28, 2021 16:44
Show Gist options
  • Save ghost355/594270c771a10fdc79f3c5458a73c9c8 to your computer and use it in GitHub Desktop.
Save ghost355/594270c771a10fdc79f3c5458a73c9c8 to your computer and use it in GitHub Desktop.
Creating several overlapping pdf with a stock chart for printing and analysis (DRAFT edition)
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
from zigzag import *
STOCK = 'TSLA'
INDEX = '^GSPC' # S&P500
bar_in_page = 250
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
info = yf.Ticker(STOCK).info
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=21,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 Max and Min extremum for price labels
treshold = 0.1 # % of changing price to count as extremum
df['max']=df.high[peak_valley_pivots(np.array(df.high), treshold, -treshold) == 1]
df['min']=df.low[peak_valley_pivots(np.array(df.low), treshold, -treshold) == -1]
# last OHLC data for titles
o = df.open[-1]
h = df.high[-1]
l = df.low[-1]
c = df.close[-1]
v = df.volume[-1]
chg = (df.close[-1]/df.close[-2]-1)*100
# add space to the right side of chart
dfpad = df.tail(round(bar_in_page/50)).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)))
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': 5,
'ytick.labelsize': 5,
'axes.linewidth': 0.8,
'savefig.pad_inches': 0.1,
'savefig.bbox': 'tight',
'grid.alpha': 0.2}
ibd = mpf.make_mpf_style(marketcolors=mc, mavcolors=['green', 'red', 'black', 'blue'], y_on_right=True, rc=base_style)
### !!! temporary replace to chunks loop
df_slice = df[-550:]
# ======== starting ploting ==================================================
# making grid of axis
egrid = (21,29)
fig = mpf.figure(style=ibd, figsize=(11, 8))
ax1 = plt.subplot2grid(egrid,(1,0),colspan=29,rowspan=16)
ax3 = plt.subplot2grid(egrid,(0,0),colspan=29,rowspan=1)
ax2 = plt.subplot2grid(egrid,(17,0),colspan=29,rowspan=4,sharex=ax1)
# remove gaps among axis panels
fig.tight_layout(h_pad= -1.6)
# 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())
# Enable minors ticks visible:
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(which = 'both',labelbottom= False, labeltop=False, labelright = False, bottom=False, top=False, right = False)
base = len(df_slice)
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m'))
ax1.xaxis.set_major_locator(mticker.IndexLocator(base=base/10, offset=0))
ax1.xaxis.set_minor_formatter(mdates.DateFormatter('%d'))
ax1.xaxis.set_minor_locator(mticker.IndexLocator(base=base/50, offset=0))
# making y-axis locator align with prices scale
maxprice = df_slice.high.max()
if maxprice > 100:
locator_size = 100
elif maxprice < 10:
locator_size = 1
else:
locator_size = 10
#ax2.ticklabel_format(axis='y',style='plain')
ax1.yaxis.set_major_locator(mticker.MultipleLocator(locator_size))
ax1.yaxis.set_minor_locator(mticker.MultipleLocator(locator_size/4))
#ax2.yaxis.set_major_formatter(mticker.MultipleLocator(df_slice['volume'].max()/5))
#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)
sp = mpf.make_addplot( df_slice['Close_idx']*1/9+maxprice/1.7, color='black', width=0.9, ax=ax1)
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=0.6, alpha=0.75, 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)
ema21 = mpf.make_addplot(
df_slice['EMA_21'], ax=ax1,color='green', width=0.3, panel=1)
# text adding
kwargs = dict(horizontalalignment='center', color='#000000', fontsize = 5, backgroundcolor = 'white',
bbox=dict(boxstyle='square', fc='white', ec='none', pad=0))
#ax1.text(mdates.date2num(df_slice.index[80]), df_slice.high.iloc[80]+text_pad, "HELLO", verticalalignment='top', **kwargs)
last_coord = -1-(round(bar_in_page/50))
#ax1.text(245, df_slice['rs_line'][last_coord]-5, "RS", color ='blue', verticalalignment='bottom', fontsize=7, alpha = 0.6)
# add price labels above/below extremum bars
price_pad = 15
for i in range(len(df_slice)):
if not(np.isnan(df_slice['max'].iloc[i])):
ax1.text(i+1, df_slice['max'].iloc[i]+price_pad, np.round(df_slice['max'].iloc[i],2), **kwargs, verticalalignment='bottom')
if not(np.isnan(df_slice['min'].iloc[i])):
ax1.text(i+1, df_slice['min'].iloc[i]-price_pad, np.round(df_slice['min'].iloc[i],2), **kwargs, verticalalignment='top')
mpf.plot(df_slice, ax=ax1, volume=ax2,addplot=[sp, rs_line, sma200, sma150, sma50, ema21, vol50], datetime_format="%b'%y",tight_layout=True, xrotation=0,
scale_width_adjustment=dict(volume=0.2),ylim=(lim_bottom*0.1,lim_top*1.1), update_width_config=dict(ohlc_ticksize=0.5))
# 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)
ax3.text(0.01, 0.6, f"{info['shortName']} /{info['symbol']}/ MarketCap: {np.round(info['marketCap']/1000000000,2)}B Shares Outstanding: {np.round(info['sharesOutstanding']/1000000,2)}M", fontsize=8)
ax3.text(0.6, 0.2, f"{info['sector']} - {info['industry']}" , fontsize=8)
ax3.text(0.85, 0.6, f"{df_slice.index[0].strftime('%d %b %Y')} - {df_slice.index[-1].strftime('%d %b %Y')}" , fontsize=8)
ax3.text(0.01, 0.2, f"Last open: {o} high: {h} low: {l} close: {c} %chg: {np.round(chg,2)} Vol: {np.round(v/1000000,2)}M", fontsize=8)
#enable volume log scale
ax2.set_yscale('symlog')
ax2.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, pos: str(np.round(x/1000000,1))+'M'))
ax2.yaxis.set_major_locator(mticker.LogLocator(base=5.0))
# add 100 to bottom of y-axis to put some useful info later
ylimit_ax1 = ax1.get_ylim()
ax1.set_ylim(ymin = ylimit_ax1[0]-100)
fig.savefig('figure.pdf')
@chuckf201
Copy link

chuckf201 commented Jan 23, 2021

Great work - Thank you
I am unable to use Nasdaq symbol '^IXIC' with TQQQ symbol. Nothing shows up on the top info bar.
Am I using the wrong index?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment