Skip to content

Instantly share code, notes, and snippets.

@ghost355
Last active January 30, 2023 22:44
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ghost355/6e169c87a5bbcbf5aaec754ffb19ded7 to your computer and use it in GitHub Desktop.
Save ghost355/6e169c87a5bbcbf5aaec754ffb19ded7 to your computer and use it in GitHub Desktop.
draw charts in pdf IBD style
# usage: make_charts(one_ticker as stirng or list of string, number_days(bars)for_showing)
from tqdm import tqdm
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 *
import time
import datetime as dt
def make_charts(stock_list,days = 260,INTERVAL='1d'):
INDEX = '^GSPC' # SP500
df_ohlc_index = yf.Ticker(INDEX).history(actions = False, period = 'max', interval = INTERVAL, rounding=True )
if type(stock_list) == type('string'):
stock_list = [stock_list]
with PdfPages(f'charts{dt.date.today()}.pdf') as pdf:
for STOCK in tqdm(stock_list):
try:
info = yf.Ticker(STOCK).info
df = yf.Ticker(STOCK).history(actions = False, period = 'max', interval = INTERVAL, rounding=True )
df=df.join(df_ohlc_index['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.05 # % 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(days/50)).copy()
dfpad.loc[:,:] = float('nan')
newdf = df.append(dfpad)
df = newdf
# styling chart
mc = mpf.make_marketcolors(up='#2A3FE5', down='#DB39AD', inherit=True,vcdopcod=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[-days:]
# ======== starting ploting ==================================================
# making grid of axis
egrid = (21,29)
fig = mpf.figure(style=ibd, figsize=(11, 8))
ax1 = plt.subplot2grid(egrid,(1,0),colspan=25,rowspan=16)
ax3 = plt.subplot2grid(egrid,(0,0),colspan=25,rowspan=1)
ax2 = plt.subplot2grid(egrid,(17,0),colspan=25,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['high'].max())
# Enable minors ticks visible:
ax1.minorticks_on()
ax2.minorticks_on()
ax1.grid(which='major',color='k')
ax1.grid(which='minor',color='gray')
ax2.grid(which='major',color='gray')
ax2.grid(which='minor',axis='x', color='gray', alpha=0.5)
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))
factor = (max(df_slice.high) / max(df_slice['Close_idx'])) * 1/2.5
shift = max(df_slice.high)/1.5
sp = mpf.make_addplot( (df_slice['Close_idx'] * factor + shift ) , color='black', width=0.9, ax=ax1, alpha = 0.6)
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 = 4, backgroundcolor = 'white',
bbox=dict(boxstyle='square', fc='white', ec='none', pad=0))
# add price labels above/below extremum bars
price_pad = (max(df_slice.high)-min(df_slice.high))*0.01
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, 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))
try:
high_p = round((c/info['fiftyTwoWeekHigh']-1)*100)
low_p = round((c/info['fiftyTwoWeekLow']-1)*100)
ax3.text(0.01, 0.6, f"{info['shortName'].title()} / {info['symbol']} ({info['sector']} - {info['industry']}) ", fontsize=8)
except:
ax3.text(0.01, 0.6, f" No data ", fontsize=8)
try:
ax3.text(0.65, 0.2, f"MarketCap: {np.round(info['marketCap']/1000000000,2)}B Shares Outstanding: {np.round(info['sharesOutstanding']/1000000,2)}M" , fontsize=8)
except:
ax3.text(0.65, 0.2, f" No data ", fontsize=8)
try:
ax3.text(0.65, 0.6, f"52w High: {high_p}% ({info['fiftyTwoWeekHigh']}) 52w Low: {low_p}% ({info['fiftyTwoWeekLow']})" , fontsize=8)
except:
ax3.text(0.8, 0.6, f" No data ", fontsize=8)
try:
ax3.text(0.01, 0.2, f"Last Open: {o} High: {h} Low: {l} Close: {c} %Chg: {np.round(chg,2)} Volume: {np.round(v/1000000,2)}M {info['quoteType']}", fontsize=8)
except:
ax3.text(0.01, 0.2, f" No data ", 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]-ylimit_ax1[1]*0.1)
#plt.show(fig)
pdf.savefig()
plt.close(fig)
except Exception as exception:
print('Problem with : ', STOCK)
print(exception)
plt.close(fig)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment