Al3XS0n

task_3

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