I have trading data for a data and I'm trying to plot the candles along with the pivot point and resistance & support bands on the plot.
The code seems to be working fine, i.e., there are no errors but there is some issue while plotting.
My code:
import pandas as pd
import numpy as np
import math
from mplfinance.original_flavor import candlestick_ohlc
import matplotlib.dates as mpl_dates
import matplotlib.pyplot as plt
df = pd.read_csv('data.csv')
# method 1: fractal candlestick pattern
# determine bullish fractal
def is_support(df, i):
cond1 = df['low'][i] < df['low'][i-1]
cond2 = df['low'][i] < df['low'][i+1]
cond3 = df['low'][i+1] < df['low'][i+2]
cond4 = df['low'][i-1] < df['low'][i-2]
return (cond1 and cond2 and cond3 and cond4)
# determine bearish fractal
def is_resistance(df, i):
cond1 = df['high'][i] > df['high'][i-1]
cond2 = df['high'][i] > df['high'][i+1]
cond3 = df['high'][i+1] > df['high'][i+2]
cond4 = df['high'][i-1] > df['high'][i-2]
return (cond1 and cond2 and cond3 and cond4)
# to make sure the new level area does not exist already
def is_far_from_level(value, levels, df):
ave = np.mean(df['high'] - df['low'])
return np.sum([abs(value-level) < ave for _, level in levels]) == 0
# a list to store resistance and support levels
levels = []
for i in range(2, df.shape[0] - 2):
if is_support(df, i):
low = df['low'][i]
if is_far_from_level(low, levels, df):
levels.append((i, low))
elif is_resistance(df, i):
high = df['high'][i]
if is_far_from_level(high, levels, df):
levels.append((i, high))
# Convert the 'timestamp' column to datetime type
df['timestamp'] = pd.to_datetime(df['timestamp'])
# Convert timestamps to numeric values
df['timestamp'] = mpl_dates.date2num(df['timestamp'])
def plot_all(levels, df):
fig, ax = plt.subplots(figsize=(16, 9))
candlestick_ohlc(ax, df.values, width=0.6, colorup='green', colordown='red', alpha=0.8)
date_format = mpl_dates.DateFormatter('%d %b %Y')
ax.xaxis.set_major_formatter(date_format)
for level in levels:
plt.hlines(level[1], xmin=df['timestamp'][level[0]], xmax=max(df['timestamp']), colors='blue', linestyle='--')
plt.show()
pivots = []
max_list = []
min_list = []
for i in range(5, len(df)-5):
# taking a window of 9 candles
high_range = df['high'][i-5:i+4]
current_max = high_range.max()
# if we find a new maximum value, empty the max_list
if current_max not in max_list:
max_list = []
max_list.append(current_max)
# if the maximum value remains the same after shifting 5 times
if len(max_list)==5 and is_far_from_level(current_max,pivots,df):
pivots.append((high_range.idxmax(), current_max))
low_range = df['low'][i-5:i+5]
current_min = low_range.min()
if current_min not in min_list:
min_list = []
min_list.append(current_min)
if len(min_list)==5 and is_far_from_level(current_min,pivots,df):
pivots.append((low_range.idxmin(), current_min))
plot_all(pivots, df)
The final plot is too compact and nothing can be made out from there.
My aim is to get a graph similar to this and be able to distinguish between different bands and candles.
It can be fine if there are no candles, but a line graph of closing price shall work too. I hope the question is clear.
PS: the data sample i'm using is:
timestamp,open,high,low,close
19/05/23 9:16,344.2,361.7,333.35,347.1
19/05/23 9:17,352.5,362.5,343.7,358.85
19/05/23 9:18,364.6,373.05,358.6,369
19/05/23 9:19,364.3,370.2,350.05,366.3
19/05/23 9:20,357.8,365.25,356.55,357.65
19/05/23 9:21,379.6,379.75,363.5,378.9
19/05/23 9:22,365.9,379.8,357.65,359.45
19/05/23 9:23,349.9,361.5,348.35,357.1
19/05/23 9:24,362.6,367.35,355.4,362.35
19/05/23 9:25,353.6,360.4,345,348.15
19/05/23 9:26,339.2,350.05,338.1,342.6
19/05/23 9:27,346.7,351.25,334.9,344.15
19/05/23 9:28,342.9,346.95,335.55,336.2
19/05/23 9:29,335.8,344.9,332.25,341.7
19/05/23 9:30,342.4,348.75,334.75,343.65
19/05/23 9:31,354.7,357.5,344.15,354.65
19/05/23 9:32,360.7,367.5,354.7,358.6
19/05/23 9:33,345.3,360.2,344.85,351.65
19/05/23 9:34,358,363.95,350.95,356.9
19/05/23 9:35,366.2,367.05,356.5,358.9
19/05/23 9:36,359.4,371.75,359.35,371
19/05/23 9:37,399.5,402.85,364.85,393.75
The initial problem lies with the width
parameter inside candlestick_ohlc
. If we check help(candlestick_ohlc)
, you'll find that it says:
width : float
fraction of a day for the rectangle width
E.g., with width=0.6
, you are setting the width to 0.6
of an entire day, while dealing with minute data.
Suppose we want to set it to 5 seconds: we can pass 1/(24*60*12)
, getting 1 hour (/24
), then 1 minute (/60
), and finally 5 seconds (/12
).
Next, you will also need to adjust the x-axis format. Currently, you have it set to '%d %b %Y'
, but we need minutes as well, and maybe a tick step of 5 minutes. Taken together, we might use something as follows:
def plot_all(levels, df):
fig, ax = plt.subplots(figsize=(16, 9))
# set 5 seconds
candlestick_ohlc(ax, df.values, width=1/(24*60*12), colorup='green', colordown='red', alpha=0.8)
# adjust x axis format
date_format = mpl_dates.DateFormatter('%d %b %Y %H:%M')
# major tick step 5 mins
major_min_loc = mpl_dates.MinuteLocator(byminute=range(0,60,5))
ax.xaxis.set_major_locator(major_min_loc)
ax.xaxis.set_major_formatter(date_format)
# minor step 1 min
minor_min_loc = mpl_dates.MinuteLocator(byminute=range(60))
ax.xaxis.set_minor_locator(minor_min_loc)
for level in levels:
plt.hlines(level[1], xmin=df['timestamp'][level[0]], xmax=max(df['timestamp']), colors='blue', linestyle='--')
plt.xticks(rotation=45)
plt.show()
Result: