8.1. Hàm SeasonalityPrice
import pandas as pd
import numpy as np
from plotly import graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
from FiinQuantX import FiinSession
username = 'REPLACE_WITH_YOUR_USER_NAME'
password = 'REPLACE_WITH_YOUR_PASS_WORD'
client = FiinSession(
username=username,
password=password
).login()
class SeasonalityPrice:
def __init__(self, data, tickers):
self.data = data
self.data['timestamp'] = pd.to_datetime(self.data['timestamp'])
self.tickers = []
if isinstance(tickers, str):
self.tickers = [tickers]
elif isinstance(tickers, list):
self.tickers = tickers
else:
self.tickers = [tickers]
def monthly_seasonality(self):
if len(self.tickers) > 1:
raise ValueError("Only one ticker is supported for monthly seasonality")
filtered_data = self.data.copy()
filtered_data.set_index('timestamp', inplace=True)
monthly_returns = filtered_data['close'].resample('M').last().pct_change() * 100
monthly_seasonality = pd.DataFrame()
monthly_seasonality['Month'] = monthly_returns.index.month
monthly_seasonality['Year'] = monthly_returns.index.year
monthly_seasonality['Returns'] = monthly_returns.values
return monthly_seasonality
def plot_seasonality(self):
if len(self.tickers) > 1:
print("Tickers: ", self.tickers)
raise ValueError("Only one ticker is supported for monthly seasonality")
monthly_seasonality = self.monthly_seasonality()
pivot_table = monthly_seasonality.pivot(index='Year', columns='Month', values='Returns').sort_index(ascending=True)
monthly_averages = pivot_table.mean(axis=0).values.reshape(1, -1)
monthly_stdev = pivot_table.std(axis=0).values.reshape(1, -1)
monthly_avg_plus_stdev = monthly_averages + monthly_stdev
monthly_avg_minus_stdev = monthly_averages - monthly_stdev
monthly_Sharpe_ratio = monthly_averages / monthly_stdev
annotations = []
for i, row in enumerate(pivot_table.values):
for j, value in enumerate(row):
annotations.append(
dict(
x=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][j],
y=pivot_table.index[i],
text=f'{value:.2f}' if not np.isnan(value) else '',
showarrow=False,
font=dict(color='black' if abs(value) < 10 else 'white')
)
)
fig = go.Figure()
fig.add_trace(go.Heatmap(
z=pivot_table.values,
x=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
y=pivot_table.index,
colorscale=[
[0, 'darkred'],
[0.49, '#ff6666'],
[0.5, 'white'],
[0.51, '#99ff99'],
[1, 'darkgreen']
],
colorbar_title="Returns (%)",
showscale=False,
zmin=-20,
zmax=20,
))
# Add the averages heatmap
fig.add_trace(go.Heatmap(
z=monthly_averages,
x= ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
y=['Avgs: '],
colorscale=[
[0, 'darkred'],
[0.49, '#ff6666'],
[0.5, 'white'],
[0.51, '#99ff99'],
[1, 'darkgreen']
],
showscale=False,
showlegend=False,
text=monthly_averages,
texttemplate='%{z:.2f}%',
yaxis='y2',
xaxis='x2',
))
fig.add_trace(go.Heatmap(
z=monthly_stdev,
x= ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
y=['Stdev: '],
colorscale=[
[0, 'darkred'],
[0.49, '#ff6666'],
[0.5, 'white'],
[0.51, '#99ff99'],
[1, 'darkgreen']
],
text=monthly_stdev,
texttemplate='%{z:.2f}%',
showscale=False,
showlegend=False,
yaxis= 'y3',
xaxis= 'x3',
))
fig.add_trace(go.Heatmap(
z=monthly_avg_plus_stdev,
x=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
y=['+ 1 stdev: '],
colorscale=[
[0, 'darkred'],
[0.49, '#ff6666'],
[0.5, 'white'],
[0.51, '#99ff99'],
[1, 'darkgreen']
],
showscale=False,
showlegend=False,
text=monthly_avg_plus_stdev,
texttemplate='%{z:.2f}%',
yaxis='y4',
xaxis='x4',
))
# Add the avg - 1stdev heatmap
fig.add_trace(go.Heatmap(
z=monthly_avg_minus_stdev,
x=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
y=[' -1 stdev: '],
colorscale=[
[0, 'darkred'],
[0.49, '#ff6666'],
[0.5, 'white'],
[0.51, '#99ff99'],
[1, 'darkgreen']
],
showscale=False,
showlegend=False,
text=monthly_avg_minus_stdev,
texttemplate='%{z:.2f}%',
yaxis='y5',
xaxis='x5',
))
fig.update_layout(
title=f'Monthly Percentage Price Change for {self.tickers} (2018-2024)',
xaxis=dict(domain=[0, 1], showticklabels=False),
yaxis=dict(domain=[0.5, 1.0], title='Year', autorange='reversed'),
xaxis2=dict(domain=[0, 1], anchor='y2', matches='x', showticklabels=False),
yaxis2=dict(domain=[0.35,0.45], autorange='reversed'),
xaxis3=dict(domain=[0, 1], anchor='y3', matches='x',showticklabels=False),
yaxis3=dict(domain=[0.24,0.34], autorange='reversed'),
yaxis4=dict(domain=[0.13,0.23], autorange='reversed'),
xaxis4=dict(domain=[0, 1], anchor='y4', matches='x', showticklabels=False),
yaxis5=dict(domain=[0.02,0.12], autorange='reversed'),
xaxis5=dict(domain=[0, 1], anchor='y5', matches='x', title='Month'),
annotations=annotations,
)
fig.show()
fig_sharpe = go.Figure()
sharpe_values = monthly_Sharpe_ratio.flatten()
colors = ['#ff9999' if x < 0 else '#66b3ff' if x < 1 else '#99ff99' for x in sharpe_values]
fig_sharpe.add_trace(go.Bar(
x=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
y=sharpe_values,
marker_color=colors,
name='Sharpe Ratio'
))
fig_sharpe.update_layout(
title=f'Monthly Sharpe Ratio for {self.tickers} (2018-2024)',
xaxis=dict(title='Month'),
yaxis=dict(title='Sharpe Ratio'),
plot_bgcolor='white',
paper_bgcolor='white',
showlegend=False,
bargap=0.2
)
fig_sharpe.show()
def plot_average_sharpe(self):
monthly_sharpe_ratios = {}
average_sharpe_ratios = {}
for ticker in self.tickers:
# Filter data for the current ticker
ticker_data = self.data[self.data['ticker'] == ticker]
ticker_data['timestamp'] = pd.to_datetime(ticker_data['timestamp'])
mask = (ticker_data['timestamp'] >= '2018-01-01') & (ticker_data['timestamp'] <= '2024-11-30')
filtered_data = ticker_data[mask].copy()
filtered_data.set_index('timestamp', inplace=True)
monthly_returns = filtered_data['close'].resample('ME').last().pct_change() * 100
# Calculate monthly averages and standard deviations
monthly_averages = monthly_returns.groupby(monthly_returns.index.month).mean()
monthly_stdev = monthly_returns.groupby(monthly_returns.index.month).std()
# Calculate Sharpe ratio
sharpe_ratios = (monthly_averages / monthly_stdev).values
monthly_sharpe_ratios[ticker] = sharpe_ratios
average_sharpe_ratios[ticker] = np.nanmean(sharpe_ratios)
# Find the ticker with the highest average Sharpe ratio
highest_avg_sharpe_ticker = max(average_sharpe_ratios, key=average_sharpe_ratios.get)
highest_avg_sharpe_value = average_sharpe_ratios[highest_avg_sharpe_ticker]
# Create subplots
fig = make_subplots(rows=1, cols=2, shared_yaxes=False, horizontal_spacing=0.1,
subplot_titles=("Monthly Sharpe Ratios", "Average Sharpe Ratios"))
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
# Plot monthly Sharpe ratios
for ticker, sharpe_values in monthly_sharpe_ratios.items():
colors = ['#00FF00' if ticker == highest_avg_sharpe_ticker else '#ff9999' if value < 0 else '#66b3ff' if value < 1 else '#99ff99' for value in sharpe_values]
fig.add_trace(go.Bar(
x=months,
y=sharpe_values,
marker_color=colors,
name=f'Sharpe Ratio for {ticker}',
width=0.15
), row=1, col=1)
# Plot average Sharpe ratios
avg_sharpe_values = list(average_sharpe_ratios.values())
avg_colors = ['#00FF00' if ticker == highest_avg_sharpe_ticker else '#66b3ff' for ticker in tickers]
fig.add_trace(go.Bar(
x=tickers,
y=avg_sharpe_values,
marker_color=avg_colors,
name='Average Sharpe Ratio',
width=0.4
), row=1, col=2)
# Add annotation for the highest average Sharpe ratio
fig.add_annotation(
x=highest_avg_sharpe_ticker, y=highest_avg_sharpe_value,
text=f"Highest Avg Sharpe: {highest_avg_sharpe_ticker} ({highest_avg_sharpe_value:.2f})",
showarrow=True,
arrowhead=1,
yshift=10,
font=dict(size=12, color="black"),
xref="x2", yref="y2"
)
fig.update_layout(
title='Sharpe Ratios for Multiple Tickers (2018-2024)',
xaxis=dict(title='Month'),
yaxis=dict(title='Sharpe Ratio'),
xaxis2=dict(title='Ticker'),
yaxis2=dict(title='Average Sharpe Ratio', range=[0, max(avg_sharpe_values) * 1.2]), # Scale y-axis
plot_bgcolor='white',
paper_bgcolor='white',
showlegend=False,
bargap=0.2
)
fig.show()
tickers = ['VCB', 'TPB', 'MBB','VIB','TCB', 'VPB', 'ACB', 'BID','CTG','EIB']
data = client.Fetch_Trading_Data(
tickers=tickers,
fields=['close'],
adjusted=True,
realtime=False,
by='1d',
from_date='2018-01-01').get_data()
new_SeasonalityPrice = SeasonalityPrice(data, 'VCB')
new_SeasonalityPrice.plot_seasonality()
#Uncomment this to plot average sharpe ratio for multiple tickers
tickers = ['VCB', 'TPB', 'MBB','VIB','TCB', 'VPB', 'ACB', 'BID','CTG','EIB']
# new_SeasonalityPrice = SeasonalityPrice(data, tickers)
# new_SeasonalityPrice.plot_average_sharpe()


Last updated