Advertisement
Al3XS0n

task_5_v2

May 10th, 2024
660
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.46 KB | None | 0 0
  1. import pandas as pd
  2. import numpy as np
  3. import cvxpy as cvx
  4.  
  5.  
  6. RISK_FREE_RATE = 0.035 # безрисковая процентная ставка
  7. RISK_FREE_RATE_MONTH = 0.035 / 12. # безрисковая процентная ставка(в месяц)
  8.  
  9. def filter_days(df: pd.DataFrame, timegate: str = '2023-01-01') -> tuple[pd.DataFrame, pd.DataFrame]:
  10.     """ Удаляем выходные дни
  11.  
  12.    :param df: Датафрейм со столбцом Date
  13.    
  14.    :returns: Отфильтрованный DataFrame
  15.    """
  16.     mask = np.is_busday(df['Date'].to_numpy().tolist(), weekmask='1111100')
  17.     df = df[mask]
  18.     return df[df['Date'] < timegate], df[df['Date'] >= timegate]
  19.  
  20.  
  21. def prepare_data(df: pd.DataFrame) -> np.ndarray:
  22.     """ Получаем все необходимые данные из DataFram'а
  23.  
  24.    :param df: DataFrame c данными
  25.    """
  26.     returns = []
  27.  
  28.     cur_month = ''
  29.     begin_pr = -1
  30.     prev = None
  31.     # YYYY-MM-DD
  32.     # 0123456789
  33.     for row in df.iterrows():
  34.         data = row[1]
  35.         if data['Date'][5:7] != cur_month or cur_month == '':
  36.             if cur_month != '':
  37.                 returns.append((prev['Close'] - begin_pr) / begin_pr)
  38.             cur_month = data['Date'][5:7]
  39.             begin_pr = data['Close']
  40.         prev = data
  41.  
  42.     returns.append((df.iloc[-1]['Close'] - begin_pr) / begin_pr)
  43.  
  44.     return np.array(returns)
  45.  
  46.  
  47. def sample_var(data: np.array, percentile: float) -> np.float64:
  48.     """ Получение VaR по выборке по формуле Var(X) = -X_(percentile * n).
  49.    Где n длинна выборки
  50.  
  51.    :param data: выборка
  52.    :param percentile: персентиль VaR'а
  53.  
  54.    :returns: VaR соответсвтующего персентиля
  55.    """
  56.     return -np.sort(data)[int(percentile * len(data))]
  57.  
  58.  
  59. def gaussian_var(data: np.array, percentile: float) -> np.float64:
  60.     """ Получение VaR в предположении гаусовости.
  61.    Формула VaR = -(a + s * q). Где
  62.    - a - оцененное среднее
  63.    - s - оцененная дисперсия
  64.    - q - минус квантиль нужного персентиля Гаусовского распределения
  65.  
  66.    :param data: выборка
  67.    :param percentile: персентиль VaR'а
  68.  
  69.    :returns: VaR соответсвтующего персентиля
  70.    """
  71.  
  72.     mean = np.mean(data)
  73.     std_dev = np.std(data)
  74.     return -mean + 1.65 * std_dev  # 1.65 - квантиль нормального распределения для 5%
  75.  
  76. def rf_weight(weights: np.ndarray) -> float:
  77.     return 1 - np.sum(weights)
  78.  
  79. def calc_portfolio(weights: np.ndarray, expectaion: float) -> float:
  80.     return weights.T @ expectaion + rf_weight(weights) * RISK_FREE_RATE_MONTH
  81.  
  82. def backtest(var: float, test_sample: np.ndarray, step: int=5) -> None:
  83.     data = prepare_data(test_sample, step)
  84.     exception_count = np.sum(data < -var)
  85.     exception_freq = exception_count / len(data)
  86.     print(f"Количество исключений {exception_count} из {len(data)}")
  87.     print(f"Исключения в процентах {exception_freq * 100}%")
  88.  
  89.  
  90.  
  91. print(f"{'Читаем данные и объединяем':-^40}")
  92. df_1 = pd.read_csv('MSFT.csv')
  93. df_1['Close_MSFT'] = df_1.Close
  94. df_2 = pd.read_csv('TSLA.csv')
  95. df_2['Close_TSLA'] = df_2.Close
  96. df = df_1.merge(df_2, on='Date', how='inner')[['Date', 'Close_MSFT', 'Close_TSLA']]
  97. print(df_1.head(5))
  98. print(df_2.head(5))
  99. print(f"{'Итоговый':-^40}")
  100. print(df.head(5))
  101. print(f"{'Done':-^40}")
  102.  
  103. # Удаляем выходные дни
  104. print(f"{'Удаляем выходные дни':-^40}")
  105. train_df, test_df = filter_days(df)
  106. print(train_df.head(5))
  107. print(test_df.head(5))
  108. print(f"{'Done':-^40}")
  109.  
  110.  
  111. # Подготавливаем данные
  112. print(f"{'Считаем месячные доходности акций':-^40}")
  113. tmp1, tmp2 = train_df[['Date', 'Close_MSFT']], train_df[['Date', 'Close_TSLA']]
  114.  
  115. tmp1['Close'] = tmp1['Close_MSFT']
  116. tmp1 = tmp1[['Date', 'Close']]
  117.  
  118. tmp2['Close'] = tmp2['Close_TSLA']
  119. tmp2 = tmp2[['Date', 'Close']]
  120.  
  121. data_1 = prepare_data(tmp1)
  122. data_2 = prepare_data(tmp2)
  123.  
  124. print(data_1[:5])
  125. print(data_2[:5])
  126. print(f"{'Done':-^40}")
  127.  
  128.  
  129. # Келли
  130. print(f"{'Келли':-^40}")
  131. data = np.array([data_1, data_2])
  132.  
  133. expected_returns = data.mean(axis=1)
  134. covariance_returns = np.cov(data)
  135. print(f"Среднее : {expected_returns}")
  136. print(f"Матрица ковариаций : {covariance_returns}")
  137.  
  138. optimal_weights_anal = np.linalg.inv(covariance_returns) @ (expected_returns - RISK_FREE_RATE_MONTH)
  139.  
  140. print(f"Оптимальные веса(Аналитическое решение): {optimal_weights_anal}")
  141.  
  142. optimal_weights_conv = cvx.Variable(2)
  143. cvx.Problem(cvx.Maximize(
  144. RISK_FREE_RATE_MONTH + optimal_weights_conv.T @ (expected_returns - RISK_FREE_RATE_MONTH)
  145. - (1 / 2) * (optimal_weights_conv.T @ covariance_returns @ optimal_weights_conv)
  146. )).solve()
  147.  
  148. print(f"Оптимальные веса(Выпуклая оптимизация): {optimal_weights_conv.value}")
  149. print(f"{'Done':-^40}")
  150.  
  151. print(f"{'Результат':-^40}")
  152. print(f"Норма доходности: { str(calc_portfolio(optimal_weights_anal, expected_returns) * 100)}%")
  153. print(f"Веса вкладов")
  154. print(f"\tMSFT: {optimal_weights_anal[0]}")
  155. print(f"\tTSLA: {optimal_weights_anal[1]}")
  156. print(f"Безрисковый: {rf_weight(optimal_weights_anal)}")
  157. print(f"{'Done':-^40}")
  158.  
  159.  
  160. # БЭКТЕСТ
  161.  
  162. print(f"{'Бэктест':=^50}")
  163.  
  164. print(f"{'Считаем месячные доходности акций':-^40}")
  165. tmp1, tmp2 = test_df[['Date', 'Close_MSFT']], test_df[['Date', 'Close_TSLA']]
  166.  
  167. tmp1['Close'] = tmp1['Close_MSFT']
  168. tmp1 = tmp1[['Date', 'Close']]
  169.  
  170. tmp2['Close'] = tmp2['Close_TSLA']
  171. tmp2 = tmp2[['Date', 'Close']]
  172.  
  173. data_1 = prepare_data(tmp1)
  174. data_2 = prepare_data(tmp2)
  175.  
  176. print(data_1[:5])
  177. print(data_2[:5])
  178. print(f"{'Done':-^40}")
  179.  
  180. test_data = np.array([data_1, data_2])
  181.  
  182. test_return_ratio = calc_portfolio(optimal_weights_anal, test_data.mean(axis=1))
  183. print(f"Среднемесячная ожидаемая норма дохожности равна : {test_return_ratio * 100}%")
  184.  
  185.  
  186. std = optimal_weights_anal.T @ np.cov(test_data) @ optimal_weights_anal
  187. sharp_ratio = (test_return_ratio -  RISK_FREE_RATE_MONTH) / np.sqrt(std)
  188. print(f"Sharp ratio : {sharp_ratio}")
  189.  
  190. print(f"{'Done':=^40}")
  191.  
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement