1
$\begingroup$

I want to place a Call Marker on a plot. Call should be "buy" whenever the smaller moving average (21) crosses over longer moving average (34) AND the Call should be "sell" whenever smaller moving average crosses under longer moving average.

I have a column average price. I have calculated the rolling mean for 21 and 34 days using the rolling() function and plotted the line plot of all three columns: average price, sma_21 and sma_34 using matplotlib. I want to place a marker on the plot.

If the smaller moving average (21) crosses over longer moving average (34) I need to place this maker "^" AND if smaller moving average crosses under longer moving average I need to place this maker "v".

averageprice = [
    2352.6, 2410.26, 2443.31, 2525.78, 2506.58, 2530.69, 2530.49, 2545.01,
    2605.4, 2593, 2577.65, 2554.74, 2549.69, 2552.85, 2568.84, 2577.2,
    2693.18, 2624.95, 2543.44, 2513.28, 2487.48, 2464.89, 2469.41, 2427.94,
    2402.96, 2430.5, 2427.14, 2412.24, 2403.02, 2388.78, 2357.33, 2345.89,
    2342.52, 2361.01, 2368.46, 2366.9, 2354.42, 2348.75, 2343.49, 2426.54,
    2478.13, 2453.34, 2449.5, 2396.18, 2402.63
]
avg_p = df['Average Price'] 
sma21 = avg_p.rolling(window = 21).mean() 
sma34 = avg_p.rolling(window = 34).mean()
avg_p.plot() 
x = df.index 
f = sma21 
g = sma34 
plt.plot(x, f) 
plt.plot(x, g) 
idx = np.argwhere(np.diff(np.sign(f - g))).flatten() 
plt.plot(x[idx], f[idx], '^') 
plt.show() 

I used this code to get the intersection point and place the marker

idx = np.argwhere(np.diff(np.sign(f - g))).flatten()

and got a plot like this:

enter image description here

I need to get markers that look something like this:

enter image description here

$\endgroup$
1
  • $\begingroup$ It would help if you can share the code that created that plot. What format is your data in? $\endgroup$
    – n1k31t4
    Jul 7 '19 at 11:50
1
$\begingroup$

I was able to produce the following with a marker for each point, based on some criteria:

markers based on sine being higher than cosine

I made a dummy Pandas Dataframe, using the sine and cosine to get lines that cross each other nicely; also making the index itself a column (with reset_index) just to make plotting easier later on.

df = pd.DataFrame.from_dict(dict(sin=np.sin(np.linspace(0, 2*np.pi, 50)),
                             cos=np.cos(np.linspace(0, 2*np.pi, 50))))
df.index.name = "index"
df.reset_index(inplace=True)
df.head(5)

  index sin        cos
0   0   0.000000    1.000000
1   1   0.063424    0.997987
2   2   0.126592    0.991955
3   3   0.189251    0.981929
4   4   0.251148    0.967949

I make a new column which contains the criteria you want, so if one line is above or below another line:

df["criteria"] = df.sin > df.cos    # where sin values are higher than cos values

This will allow us to select which points to plot with which style (e.g. which marker).

The final plot itself is made up of two parts (note that we re-use the same ax object, so all plots appear on the same graph)

  1. The underlying data itself can be added to create the simple lines (these are your moving average columns).

    ax = df.sin.plot(c="gray", figsize=(10, 6))    # plots the sine curve in gray
    df.cos.plot(c="cyan", ax=ax)                   # re-uses the ax object - important!
    
  2. The markers, based on our criteria column. We filter the points that satisfy our criteria and plot them with a certain marker, then filter the points that do not have the criteria (using the negation selector: ~). Whereas we used the basic line plot above, we now need to use the scatter kind of plot:

    df[df.criteria].plot(kind="scatter", x="index", y="sin",
                         c="green", marker="^", ax=ax, label="higher")
    
    df[~df.criteria].plot(kind="scatter", x="index", y="sin",
                         c="red", marker="v", ax=ax, label="lower");
    

    Note that we continue to use the same ax object.

We can also add the legend, which uses either the Pandas column names by default or the label that was passed in each plot method:

plt.legend()="lower");    # adds legend for all plots

You can see in the matplotlib documentation that there is a marker argument, but it seems you cannot simply specify a column of a Pandas dataframe to give the marker for each individual point.

$\endgroup$
3
  • $\begingroup$ If you only want markers in certain position (e.g. where the lines cross instead of at all points), you wil only need to change the criteria column in my example. $\endgroup$
    – n1k31t4
    Jul 7 '19 at 12:57
  • $\begingroup$ how to change the criteria so that i only get the marker on certain point $\endgroup$ Jul 7 '19 at 13:01
  • $\begingroup$ If you do df["markers"] = df.criteria.astype(int).diff(), you will have a new column called markers with three values: 0 where there should be no marker, 1 where you want an upwards/green marker, and -1 where you want a downwards/green marker. $\endgroup$
    – n1k31t4
    Jul 7 '19 at 13:06
1
$\begingroup$

just find the intersections of those two data and impose the conditions greater than and less than and find their indexes and plot it.

ndf = pd.read_csv('Nifty50.csv')
ndf.Date = pd.to_datetime(ndf['Date'])

fig, ax = plt.subplots(figsize=(15, 5))
ndf['roll21'] = ndf['Close'].rolling(21).mean()
ndf['roll34'] = ndf['Close'].rolling(34).mean()
ndf.dropna()

def whenCrosses(values):
    l=[]
    were = values[0]
    flag = True
    for i, ele in enumerate(values):
        if were==ele:
            l.append(0)
        else:
            l.append(1)
            were = ele
    return l

ndf['buy'] = ndf['roll34']<ndf['roll21']
ndf['sell'] = ndf['roll34']>ndf['roll21']

ndf['buy_change'] = np.array(whenCrosses(ndf.buy.values.reshape(1, len(ndf.buy)).flatten()))
ndf['sell_change'] = np.array(whenCrosses(ndf.sell.values.reshape(1, len(ndf.sell)).flatten()))

ndf['buy'] = ndf['buy_change'].where(ndf['buy']==True)
ndf['buy'] = ndf['roll21'].where(ndf['buy']==1)

ndf['sell'] = ndf['sell_change'].where(ndf['sell']==True)
ndf['sell'] = ndf['roll21'].where(ndf['sell']==1)

ax.plot(ndf.Date, ndf.Close, 'r')
ax.plot(ndf.Date, ndf.roll34, 'b', label='34_SMA')
ax.plot(ndf.Date, ndf.roll21, 'g', label='21_SMA')
ax.plot(ndf.Date, ndf.buy, "g^")
ax.plot(ndf.Date, ndf.sell, "kv")

ax.set_xlabel('Date')
plt.legend(loc=2)
plt.tight_layout()
plt.xticks(plt.xticks()[0], df.index.date, rotation=45)
plt.show()

and you should get- enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.