# 4.3 Hàm SeasonalityPrice

{% hint style="info" %}
Hàm này dùng để vẽ tương quan thay đổi giá của 1 hay nhiều mã cổ phiếu trong một khoảng thời gian
{% endhint %}

```python
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()
```

<figure><img src="/files/8vpI6SolqkPb2c7E1cnD" alt=""><figcaption><p>Bảng thay đổi giá theo tháng của VCB</p></figcaption></figure>

<figure><img src="/files/Rqyr09jvH1FnYRyIIRN1" alt=""><figcaption><p>Sharpe Ratio theo tháng </p></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.fiinquant.vn/ham-va-cong-thuc/4.-thong-ke-thi-truong/4.3-ham-seasonalityprice.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
