3

Below is the plot I generated using axes.text option,

ax[0].text(row.TIMESTAMP, row.HIGH+(0.1*width),row['candlestick_pattern'], fontsize=5, rotation='vertical') enter image description here

I'm trying to achieve the same output using TextPath and PathPatch, in order to increase/decrease the font size when I zoom in/out of the plot, and below is the code I have (taken from here and here )

textPath = TextPath((data_coord[1], -data_coord[0]), row['candlestick_pattern'], size=2)
pathPatch = PathPatch(textPath, color="black")
transform = mpl.transforms.Affine2D().rotate_deg(90) + ax[0].transData
pathPatch.set_transform(transform)
ax[0].add_patch(pathPatch)

Output with this is enter image description here

You could see that the text is cramped into a very small region and its not what I want. I would want to set the font size to a smaller value and increase the width (in vertical mode - height) of the TextPath. Is that possible?

Below is the complete code with which we can reproduce the problem for the dataset here

import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.textpath import TextPath
from matplotlib.patches import PathPatch
from mplfinance.original_flavor import candlestick_ohlc
from matplotlib import transforms as tf

import pandas as pd

plotDf = pd.read_csv("data.csv")
plotDf.reset_index(inplace=True)
del plotDf['TIMESTAMP']
del plotDf['TOTTRDQTY']

fig, ax = plt.subplots(1)

candlestick_ohlc(ax,plotDf.values,width=0.6, \
                 colorup='green', colordown='red', alpha=0.8)

maxHigh = plotDf['HIGH'].max()
minLow = plotDf['LOW'].min()
width = maxHigh - minLow
threshold = (width)*0.6

for idx, row in plotDf.iterrows():
    if (row['candlestick_pattern'] != 'NO_PATTERN'):
        if (row.HIGH < (threshold+minLow)):
            data_coord = (idx, row.HIGH+(0.1*width))
            #ax.text(idx, row.HIGH+(0.1*width), row['candlestick_pattern'], fontsize=5, rotation='vertical')
        else:
            data_coord = (idx, row.LOW-(0.4*width))
            #ax.text(idx, row.LOW-(0.4*width), row['candlestick_pattern'], fontsize=5, rotation='vertical')

        textPath = TextPath((data_coord[1], -data_coord[0]), row['candlestick_pattern'], size=2)
        pathPatch = PathPatch(textPath, color="black")
        transform = mpl.transforms.Affine2D().rotate_deg(90) + ax.transData
        pathPatch.set_transform(transform)
        ax.add_patch(pathPatch)

fig.autofmt_xdate()
fig.tight_layout()
fig.suptitle("test", fontsize=16)
fig.set_size_inches(10.5, 10.5)

plt.subplots_adjust(top=0.95) 
plt.show()
Raj Kumar
  • 143
  • 2
  • 11
  • Could you add the data you're using for that plot? Just to make things easier – mgmussi Apr 06 '22 at 00:43
  • Hello, I have shared the data file in [here](https://wetransfer.com/downloads/7e5a49c20e3b2cdd3862efbdc55510f020220406044139/c6a42b).. It has the data for OHLC charts and the strings are from "candlestick_pattern" column. Thanks – Raj Kumar Apr 06 '22 at 04:43

1 Answers1

0

Apparently, your problem is a scaling problem. Messing around with .scale(x,y), ax.set_xlim and ax.set_ylim might allow you to "unsqueeze" the text. You can also try to set an anchor for your plot like done here:

ts = ax.transData
coords = ts.transform([0,0]) #<-anchor
tr = mpl.transforms.Affine2D().rotate_deg_around(coords[0],coords[1],90).scale(1,3) #<- scale
t = ts + tr
#<extra code>
pathPatch = PathPatch(textPath, color="black", transform = t)

EDIT

I tried many things, but I couldn't find a good way of doing it. I'll leave below what I tried and some resources that might help.

The way to properly use .rotate_deg_around would be like such:

ts = ax.transData
# ts = fig.dpi_scale_trans #this guy uses the fig scale, if you're interested
coords = ts.transform([data_coord[0],data_coord[1]])
converter = (coords[0]/data_coord[0], coords[1]/data_coord[1])
#plot the anchor points for visualization:
plt.plot(coords[0]/converter[0], coords[1]/converter[1],'xk') 
tr = mpl.transforms.Affine2D().rotate_deg_around(coords[0]/converter[0],coords[1]/converter[1],90).scale(converter[0],converter[1])

pathPatch = PathPatch(textPath, color="black", transform = tr)
ax.add_patch(pathPatch)

Nonetheless, the results are still similar to what you had at the beginning: enter image description here

It appears that TextPath does not behave like it should when using transform. Here .get_offset_transform is used, and it apparently fixes this sort of issue, but I was unable to use it since the plt has a Line type.

Also, you will see that if you increase the y axis in .scale, you can start to see the text, but it spreads the coordinates as well. One idea you can try is setting a good readable y scale (use ax.set_ylim to see your text) and then use that value as a divisor when setting the coordinates for your plot.

There are also some ideas here that might serve you.

mgmussi
  • 520
  • 1
  • 4
  • 19
  • thanks for your suggestion, mgmussi. I will try this out and update you. In the meantime, I have updated my question with the complete code to plot. Thanks again – Raj Kumar Apr 11 '22 at 16:20
  • @RajKumar see my updates (sorry I couldn't help too much) – mgmussi Apr 11 '22 at 19:32
  • thanks a lot for your efforts and suggestions, mgmussi. I havent got time to work on top of your suggestions further. Will update here once I progress further on this. – Raj Kumar Apr 15 '22 at 08:47
  • 1
    After checking the matplotlib library code, I found a hack to achieve what I want. While generating the textPath, they get the vertices to place each characters based on the font property and size. I have added couple of new variables "xscale, yscale" using which I modify the vertices. I pass this while creating the TextPath and it is used in TextPath.__init__()->text_to_path.get_text_path() -> get_glyphs_with_font to manipulate the output of font.get_path() – Raj Kumar Apr 15 '22 at 11:47