0

When you load up the code, it's supposed to plot the Candlestick chart and the 4 EMA lines over each other, but they are separated. I'm currently testing the code on Tesla stocks for the example I am showing here.

enter image description here

Code:

import yfinance as yf
import mplfinance as mpf
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import pandas as pd

# Dates to get stock data
start_date = "2010-07-01"
end_date = "2023-06-17"

# Fetch Tesla stock data
tesla_data = yf.download("TSLA", start=start_date, end=end_date)
tesla_weekly_data = tesla_data.resample("W").agg(
    {"Open": "first", "High": "max", "Low": "min", "Close": "last", "Volume": "sum"}
).dropna()

# Get the latest closing price
latest_price = tesla_weekly_data['Close'][-1]

# Create additional plot
close_price = tesla_weekly_data['Close']
apd = mpf.make_addplot(close_price, color='cyan', width=2)

# Calculate the EMA with different lengths
ema_lengths = [8, 13, 21, 55]
ema_colors = ['blue', 'green', 'yellow', 'red']
ema_lines = []
for length, color in zip(ema_lengths, ema_colors):
    ema_line = tesla_weekly_data['Close'].ewm(span=length, adjust=False).mean()
    ema_lines.append(ema_line)

# Plot the candlestick chart with EMA lines
fig, axes = mpf.plot(tesla_weekly_data,
                     type='candle',
                     addplot=apd,
                     style='yahoo',
                     title='Tesla Stock Prices',
                     ylabel='Price',
                     xlabel='Date',
                     volume=True,
                     ylabel_lower='Volume',
                     volume_panel=1,
                     figsize=(16, 8),
                     returnfig=True)

# Move the y-axis labels to the left side
axes[0].yaxis.tick_left()
axes[1].yaxis.tick_left()

# Adjust the position of the y-axis label for price
axes[0].yaxis.set_label_coords(-0.08, 0.5)

# Adjust the position of the y-axis label for volume
axes[1].yaxis.set_label_coords(-0.08, 0.5)

# Set y-axis label for price and volume
axes[0].set_ylabel('Price', rotation=0, labelpad=20)
axes[1].set_ylabel('Volume', rotation=0, labelpad=20)

# Make the legend box
handles = axes[0].get_legend_handles_labels()[0]
red_patch = mpatches.Patch(color='red')
green_patch = mpatches.Patch(color='green')
cyan_patch = mpatches.Patch(color='cyan')
handles = handles[:2] + [red_patch, green_patch, cyan_patch]
labels = ["Price Up", "Price Down", "Closing Price"]
axes[0].legend(handles=handles, labels=labels)

# Add a box to display the current price
latest_price_text = f"Current Price: ${latest_price:.2f}"
box_props = dict(boxstyle='round', facecolor='white', edgecolor='black', alpha=0.8)
axes[0].text(0.02, 0.95, latest_price_text, transform=axes[0].transAxes,
             fontsize=12, verticalalignment='top', bbox=box_props)

# Add EMA lines
for ema_line, color in zip(ema_lines, ema_colors):
    axes[0].plot(ema_line, color=color)

# Display the chart
plt.show()

Why are the EMA lines separated from the rest of the plot when they should be aligned?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Damian
  • 29
  • 5
  • 1
    I would guess that `ema_line` is not indexed by `datetime` values. The output of `Dataframe.ewb` seems to lose the times from the original data. Can you check that `ema_lines[0].index` is a `DatetimeIndex`? – Emilio Silva Jun 17 '23 at 22:45
  • 1
    Why don't you just use the `ema=` kwarg of mplfinance: `mpf.plot(df,... ema=[8, 13, 21, 55])` ?? – Daniel Goldfarb Jun 18 '23 at 03:25
  • @DanielGoldfarb Is there an existing SO question demonstrating the use of `ema=`? If so, link it to me in a comment please. I did not find one. – Trenton McKinney Jun 18 '23 at 20:22
  • The primary issue with the existing implementation is `axes[0].get_xticks()` are just a range index `array([-100., 0., 100., 200., 300., 400., 500., 600., 700., 800.])`, whereas, each index of the dataframes in `ema_lines` are datetimes, so the x-axis values do not align. The [comment](https://stackoverflow.com/questions/76498274/ema-lines-not-with-the-rest-of-the-graph#comment134883175_76498274) from @DanielGoldfarb (the package author), is the correct solution. – Trenton McKinney Jun 18 '23 at 20:35
  • 1
    Use of the `ema=` kwarg is identical to use of the `mav=` kwarg described in the [basic usage](https://github.com/matplotlib/mplfinance#usage) (and as noted [in the PR](https://github.com/matplotlib/mplfinance/pull/563#issuecomment-1286328844)). Sorry there is not an explicit example. – Daniel Goldfarb Jun 19 '23 at 11:12
  • 1
    Regarding the x-axis issue mentioned above by @TrentonMcKinney ... there is [**documentation here**](https://github.com/matplotlib/mplfinance/wiki/Mplfinance-Time-Axis-Concerns,-and-Internals-of-Displaying-or-Not-Displaying-Non-Trading-periods). That said, as Trnton mentioned, the best solution (for this and in general) is to do as much as possible using mplfinance kwargs and features, and do not directly access the matplotlib Axes objects *except when absolutely necessary*. – Daniel Goldfarb Jun 19 '23 at 11:14
  • @DanielGoldfarb I think you should convert your comments into an actual answer, since there isn't an existing SO question discussing this aspect of your excellent python package. – Trenton McKinney Jun 19 '23 at 17:46
  • If @r-beginner's answer works, you should accept their answer. – jared Jun 20 '23 at 16:30

1 Answers1

0

I don't know much about it, but it seems to me that each time line is treated differently based on the events that are taking place. By combining the additional graphs into the mpl side of the process, it can be displayed correctly. I don't know if the warning is displayed in your environment, but I have added the settings from the error message, as it was displayed when the number of data was large.

import yfinance as yf
import mplfinance as mpf
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import pandas as pd

# Dates to get stock data
start_date = "2010-07-01"
end_date = "2023-06-17"

# Fetch Tesla stock data
tesla_data = yf.download("TSLA", start=start_date, end=end_date)
tesla_weekly_data = tesla_data.resample("W").agg(
    {"Open": "first", "High": "max", "Low": "min", "Close": "last", "Volume": "sum"}
).dropna()

# Get the latest closing price
latest_price = tesla_weekly_data['Close'][-1]

# Calculate the EMA with different lengths
ema_lengths = [8, 13, 21, 55]
ema_colors = ['blue', 'green', 'yellow', 'red']
ema_lines = []
for length, color in zip(ema_lengths, ema_colors):
    ema_line = tesla_weekly_data['Close'].ewm(span=length, adjust=False).mean()
    ema_lines.append(ema_line)

# Create additional plot
apds = []
close_price = tesla_weekly_data['Close']
apds.append(mpf.make_addplot(close_price, color='cyan', width=2))

# Add EMA lines
for ema_line, color in zip(ema_lines, ema_colors):
    apds.append(mpf.make_addplot(ema_line, color=color))

# Plot the candlestick chart with EMA lines
fig, axes = mpf.plot(tesla_weekly_data,
                     type='candle',
                     addplot=apds, # update
                     style='yahoo',
                     title='Tesla Stock Prices',
                     ylabel='Price',
                     #xlabel='Date',
                     volume=True,
                     ylabel_lower='Volume',
                     volume_panel=1,
                     figsize=(16, 8),
                     returnfig=True,
                     warn_too_much_data=2800, # update: 677 records x 4 lines
                    )

# Move the y-axis labels to the left side
axes[0].yaxis.tick_left()
axes[1].yaxis.tick_left()

# Adjust the position of the y-axis label for price
axes[0].yaxis.set_label_coords(-0.08, 0.5)

# Adjust the position of the y-axis label for volume
axes[1].yaxis.set_label_coords(-0.08, 0.5)

# Set y-axis label for price and volume
axes[0].set_ylabel('Price', rotation=0, labelpad=20)
axes[1].set_ylabel('Volume', rotation=0, labelpad=20)

# Make the legend box
handles = axes[0].get_legend_handles_labels()[0]
red_patch = mpatches.Patch(color='red')
green_patch = mpatches.Patch(color='green')
cyan_patch = mpatches.Patch(color='cyan')
handles = handles[:2] + [red_patch, green_patch, cyan_patch]
labels = ["Price Up", "Price Down", "Closing Price"]
axes[0].legend(handles=handles, labels=labels)

# Add a box to display the current price
latest_price_text = f"Current Price: ${latest_price:.2f}"
box_props = dict(boxstyle='round', facecolor='white', edgecolor='black', alpha=0.8)
axes[0].text(0.02, 0.95, latest_price_text, transform=axes[0].transAxes,
             fontsize=12, verticalalignment='top', bbox=box_props)

# Display the chart
plt.show()

enter image description here

r-beginners
  • 31,170
  • 3
  • 14
  • 32