Last active
January 28, 2021 16:44
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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?