pythonpandasmatplotlibtime-seriesmplfinance

Plotting issue with resistance, support bands and pivot points on trading data


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. enter image description here

My aim is to get a graph similar to this and be able to distinguish between different bands and candles. enter image description here

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

Solution

  • 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:

    candlestick