Adding vertical lines using matplotlib

52 views Asked by At

My aim is to first create two plots (which I correctly created), each of them representing the CDF of the number of Puts and Calls still open in the market. For each plot, I also wanted to draw the vertical line representing the 40% and 80% percentile.

However, the final aim is to merge the two plot and also add the respective vertical lines for both sides (here is the problem). This is what I did:

Creation of the two single plots (FIRST CALL):

plt.figure(figsize=(14, 6))  # Adjusting figure size
    plt.bar(CALL.index, CALL['CDF'], width=0.5, color='lightgreen')  # Using DataFrame index as x-axis ticks
    plt.xlabel('STRIKE')
    plt.ylabel('CDF')
    plt.title('Cumulative Distribution Function (CDF) for CALL')
    plt.xticks(CALL.index, CALL['STRIKE'], rotation=90)  # Setting x-axis ticks to the Strike values with rotation
    plt.grid(True)

    # Find the nearest value to 40% in the 'Percentile' column
    nearest_40_percentile_idx = (CALL['Percentile'] - 0.4).abs().idxmin()
    nearest_40_percentile_strike = CALL.loc[nearest_40_percentile_idx, 'STRIKE']

    # Add a vertical line at the corresponding 'Strike' value for 40th percentile
    plt.axvline(x=nearest_40_percentile_idx, color='red', linestyle='--', label=f'Nearest to 40% ({nearest_40_percentile_strike})')

    # Find the nearest value to 80% in the 'Percentile' column
    nearest_80_percentile_idx = (CALL['Percentile'] - 0.8).abs().idxmin()
    nearest_80_percentile_strike = CALL.loc[nearest_80_percentile_idx, 'STRIKE']

    # Add a vertical line at the corresponding 'Strike' value for 80th percentile
    plt.axvline(x=nearest_80_percentile_idx, color='blue', linestyle='--', label=f'Nearest to 80% ({nearest_80_percentile_strike})')

    # Show legend
    plt.legend()

    plt.show()

The one of the PUT is basically the same, just with the df's name changed.

Now, I tried to merge them and plot the vertical lines in this way, but the graph is a mess:

 res = pd.concat([CALL, PUT])

    # Set different colors for CALL and PUT bars
    colors = {1: 'lightgreen', 2: 'orange'}

    # Plot the bar chart
    plt.figure(figsize=(16, 6))
    sns.barplot(x='Strike', y='CDF', data=res, hue='hue', palette=colors)

    # Calculate the nearest strike values to the 40th and 80th percentiles for both CALL and PUT
    nearest_40_percentile_idx_call = (CALL['Percentile'] - 0.4).abs().idxmin()
    nearest_40_percentile_strike_call = CALL.loc[nearest_40_percentile_idx_call, 'Strike']

    nearest_40_percentile_idx_put = (PUT['Percentile'] - 0.4).abs().idxmin()
    nearest_40_percentile_strike_put = PUT.loc[nearest_40_percentile_idx_put, 'Strike']

    # Add vertical lines at the corresponding strike values for the 40th and 80th percentiles
    plt.axvline(x=nearest_40_percentile_idx_call, color='blue', linestyle='--', label=f'40% for CALL ({nearest_40_percentile_strike_call})')
    plt.axvline(x=nearest_40_percentile_idx_put, color='green', linestyle='--', label=f'40% for PUT ({nearest_40_percentile_strike_put})')

    # Set labels and title
    plt.xlabel('Strike')
    plt.ylabel('Cumulative Distribution Function (CDF)')
    plt.title('CDF for CALL and PUT')

    # Rotate x-axis labels for better readability
    plt.xticks(rotation=60)

    # Show legend
    plt.legend()

    # Show the plot
    plt.show()

Any suggestion?

1

There are 1 answers

0
Ricter On

SOLUTION

res = pd.concat([CALL, PUT])
    unique_strikes = sorted(res['STRIKE'].unique())
    ind40call = 0
    ind80call = 0
    ind40put = 0
    ind80put = 0
    for i, v in enumerate(unique_strikes):
        if v == nearest_80_percentile_call:
            ind80call = i
        if v == nearest_80_percentile_put:
            ind80put = i
        if v == nearest_40_percentile_call:
            ind40call = i
        if v == nearest_40_percentile_put:
            ind40put = i

    colors = {1: 'lightgreen', 2: 'orange'}
    plt.figure(figsize=(16, 6))
    sns.barplot(x='STRIKE', y='CDF', data=res, hue='hue', palette=colors)

    plt.xlabel('Strike')
    plt.ylabel('Open Interest')
    plt.title('Open Interest for CALL and PUT')
    plt.xticks(rotation=60)

    plt.axvline(x=ind40put, color='red', linestyle='--')
    plt.axvline(x=ind80call, color='red', linestyle='--')
    plt.axvline(x=ind80put, color='red', linestyle='--')
    plt.axvline(x=ind40call, color='red', linestyle='--')

    plt.text(ind40put, res['CDF'].max(), f'40% - {nearest_40_percentile_put}', ha='left', va='top', color='blue')
    plt.text(ind80call, res['CDF'].max(), f'80% - {nearest_80_percentile_call}', ha='left', va='top', color='blue')
    plt.text(ind80put, res['CDF'].max(), f'80% - {nearest_80_percentile_put}', ha='left', va='top', color='blue')
    plt.text(ind40call, res['CDF'].max(), f'40% - {nearest_40_percentile_call}', ha='left', va='top', color='blue')

    # Create a custom legend
    handles = [plt.Rectangle((0,0),1,1, color='lightgreen', ec="k"), plt.Rectangle((0,0),1,1, color='orange', ec="k")]
    labels = ['CALL', 'PUT']
    plt.legend(handles, labels)

    plt.show()

The idea is to sorte all the strikes, which represent the x-axis and then, by defining the corresponding value of the strike in which I'd like to plot the vertical line, to find the corresponding index and then plot the line in that index.