I've set up a suggestion that should take care of your concerns. I'm using a built-in dataset and some random choices for duplicated dates. If you would like me to work on a sample of your actual dataset, please include that using the approach described here.
First suggestion:
1. Main trace is added to figure with fig.add_traces(go.Scatter)
2. Dates with quakes are arranged in two different datasets; one showing dates with single events, and one with duplicate dates.
3. Duplicate dates are organized in multiple = quakes[quakes.date.duplicated()]
for which each and every record are assigned to a single trace. This will let you set up different symbols and hoverdata as you wish.
4. Values that belong to duplicated dates are offset on the y-axis compared to each other to ensure that the corresponding annotations are not overlapping or overwritten.
If this comes close to your desired result, we can talk details when you find the time.
Plot:

Code 1
# imports
import pandas as pd
import plotly.express as px
import random
import numpy as np
import plotly.graph_objects as go
from plotly.validators.scatter.marker import SymbolValidator
from itertools import cycle
np.random.seed(123)
# data
df = px.data.stocks()
df = df.drop(['GOOG', 'AMZN', 'NFLX', 'FB'], axis = 1).tail(150)
# simule
quakes =pd.DataFrame()
dates = [random.choice(df.date.values) for obs in range(0, 6)]
dates.extend([df.date.iloc[2], df.date.iloc[2], df.date.iloc[6], df.date.iloc[6], df.date.iloc[6]])
# synthetic data for earthquakes
quakes['date'] = dates
quakes['magnitude'] = [np.random.uniform(5,7) for obs in quakes.date]
quakes = pd.concat([quakes, quakes.groupby('date').cumcount().to_frame('dupes')], axis = 1)
# find dates with multiple quakes
multiple = quakes[quakes.date.duplicated()].sort_values('date').reset_index()#.sorted()
# find dates where only one quake occurs (to keep number of traces at a minimum)
single = quakes[~quakes.date.duplicated()].sort_values('date').reset_index()
single = pd.merge(df, single, on = 'date', how = 'right')
fig = go.Figure(go.Scatter(x = df['date'], y = df['AAPL'], name = 'Apple'))
fig.add_traces(go.Scatter(x=single['date'], y =single['AAPL'],
mode = 'markers',
name = 'days with quakes',
showlegend = True,
marker = dict(symbol = 'square', size = single['magnitude']**2)))
symbols = cycle(['circle', 'hexagon', 'diamond', 'star'])
annotations = []
for i, r in multiple.iterrows():
fig.add_traces(go.Scatter(x=[r['date']], y = df[df['date']==r['date']]['AAPL']*(1 + r['dupes']/10),
mode = 'markers',
name = r['date'],
marker = dict(symbol = next(symbols), size = r['magnitude']**2)))
annotations.append([r['date'], df[df['date']==r['date']]['AAPL']*(1 + r['dupes']/10), r['magnitude']])
# annotate single events
for i, q in enumerate(fig.data[1].x):
fig.add_annotation(x=q, y = fig.data[1].y[i],
text = str(fig.data[1].y[i])[:3], showarrow = False,
font = dict(size = 10),
yref = 'y',
ay=0)
# annotate duplicates
for a in annotations:
fig.add_annotation(x=a[0], y = a[1].item(),
text = str(a[2])[:4], showarrow = False,
font = dict(size = 10),
yref = 'y',
ay=0)
fig.show()