{
"cells": [
{
"cell_type": "code",
"execution_count": 142,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import math\n",
"from datetime import datetime\n",
"import hvplot.pandas\n",
"import math\n",
"# load data\n",
"profile_df = pd.read_pickle('../data/portfolio_portfile.pkl')\n",
"benchmark_df = pd.read_pickle('../data/benchmark_portfolio.pkl')\n",
"portfolio_df = pd.read_pickle('../data/portfolio_data.pkl')"
]
},
{
"cell_type": "code",
"execution_count": 143,
"metadata": {},
"outputs": [],
"source": [
"# to acoomodate the current pipe line\n",
"min_dates = benchmark_df.groupby('ticker')['date'].min()\n",
"\n",
"for ticker, min_date in min_dates.items():\n",
" benchmark_df.loc[(benchmark_df['ticker'] == ticker) & (benchmark_df['date'] != min_date), 'weight'] = float('nan')\n",
"\n",
"benchmark_df['initial_weight'] = benchmark_df['weight']\n",
"# drop weight\n",
"benchmark_df = benchmark_df.drop(columns=['weight'])"
]
},
{
"cell_type": "code",
"execution_count": 144,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Series([], Name: initial_weight, dtype: int64)"
]
},
"execution_count": 144,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# check if all unique ticker has an weight\n",
"count_list = benchmark_df.groupby('ticker')['initial_weight'].count().sort_values(ascending=False)\n",
"count_list[count_list != 1]\n"
]
},
{
"cell_type": "code",
"execution_count": 145,
"metadata": {},
"outputs": [],
"source": [
"update_profile_df = profile_df.copy()\n",
"update_profile_df['date'] = datetime(2021,1,10)\n",
"update_profile_df['weight'] = [50,100,200,300,400,500]\n",
"profile_df = pd.concat([profile_df, update_profile_df])\n"
]
},
{
"cell_type": "code",
"execution_count": 146,
"metadata": {},
"outputs": [],
"source": [
"def calculate_pct(stock_df):\n",
" stock_df['pct'] = stock_df.groupby(['ticker'])['close'].pct_change()\n"
]
},
{
"cell_type": "code",
"execution_count": 147,
"metadata": {},
"outputs": [],
"source": [
"# step 1 pct\n",
"calculate_pct(portfolio_df)\n",
"calculate_pct(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 148,
"metadata": {},
"outputs": [],
"source": [
"def return_weighted_stock_df(stock_price_df, profile_df=None):\n",
" # TODO change later this a temporary solution\n",
" # initialize weight if profile_df is not none\n",
" merged_df = pd.DataFrame()\n",
" if profile_df is not None:\n",
" merged_df = stock_price_df.merge(profile_df[['weight', 'date', 'ticker']], on=['ticker', 'date'], how='outer')\n",
" merged_df.sort_values(by=['date'], inplace=True)\n",
" merged_df.rename(columns={'weight': 'initial_weight'}, inplace=True)\n",
" else:\n",
" merged_df = stock_price_df.copy()\n",
" merged_df['current_weight'] = float('nan')\n",
" merged_df['previous_weight'] = float('nan')\n",
" df_grouped = merged_df.groupby('ticker')\n",
" for _, group in df_grouped:\n",
" pre_w = float('nan')\n",
" ini_w = float('nan')\n",
" for index, row in group.iterrows():\n",
" cur_w = float('nan')\n",
" # if has initial weight, the following row all use this initial weight\n",
" if not pd.isna(row['initial_weight']):\n",
" ini_w = row['initial_weight']\n",
" cur_w = ini_w\n",
" # just calculate current weight based on previous weight\n",
" else:\n",
" cur_w = pre_w * (1 + row['pct'])\n",
"\n",
" merged_df.loc[index, 'current_weight'] = cur_w \n",
" merged_df.loc[index, 'previous_weight'] = pre_w\n",
" merged_df.loc[index, 'initial_weight'] = ini_w\n",
" pre_w = cur_w\n",
" \n",
" # drop row where closing price is none\n",
" merged_df = merged_df[~pd.isna(merged_df['close'])]\n",
" # drop index\n",
" return merged_df"
]
},
{
"cell_type": "code",
"execution_count": 149,
"metadata": {},
"outputs": [],
"source": [
"# TODO consider save the weight calculation\n",
"portfolio_df = return_weighted_stock_df(portfolio_df, profile_df)\n",
"benchmark_df = return_weighted_stock_df(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 150,
"metadata": {},
"outputs": [],
"source": [
"# benchmark_df[benchmark_df.ticker =='000008.XSHE']"
]
},
{
"cell_type": "code",
"execution_count": 151,
"metadata": {},
"outputs": [],
"source": [
"## normalize all weight\n",
"def normalize_weight(stock_df):\n",
" stock_df['current_weight'] = stock_df['current_weight'] / \\\n",
" stock_df.groupby('date')['current_weight'].transform('sum')\n",
"\n",
" stock_df['previous_weight'] = stock_df['previous_weight'] / \\\n",
" stock_df.groupby('date')['previous_weight'].transform('sum')\n",
"\n",
" stock_df['initial_weight'] = stock_df['initial_weight'] / \\\n",
" stock_df.groupby('date')['initial_weight'].transform('sum')\n"
]
},
{
"cell_type": "code",
"execution_count": 152,
"metadata": {},
"outputs": [],
"source": [
"normalize_weight(portfolio_df)\n",
"normalize_weight(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 153,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"260 0.032258\n",
"258 0.064516\n",
"262 0.129032\n",
"263 0.258065\n",
"259 0.322581\n",
"261 0.193548\n",
"Name: initial_weight, dtype: float64"
]
},
"execution_count": 153,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"portfolio_df[portfolio_df.date == datetime(2021, 3, 12)]['initial_weight']"
]
},
{
"cell_type": "code",
"execution_count": 154,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1.0\n",
"1.0\n",
"1.0\n"
]
}
],
"source": [
"print(benchmark_df[benchmark_df.date == datetime(2021, 3, 12)]['initial_weight'].sum())\n",
"print(benchmark_df[benchmark_df.date == datetime(2021, 3, 12)]['current_weight'].sum())\n",
"print(benchmark_df[benchmark_df.date == datetime(2021, 3, 12)]['previous_weight'].sum())"
]
},
{
"cell_type": "code",
"execution_count": 155,
"metadata": {},
"outputs": [],
"source": [
"# step 3 sector wegiht\n",
"\n",
"# add sector information first\n",
"def create_sector_weight(stock_df, profile_df=None):\n",
" # if profile_df is none assume the aggregate_sector stock info already in stock_df\n",
" merged_df = None\n",
" if profile_df is not None:\n",
" merged_df = stock_df.merge(profile_df[['ticker', 'aggregate_sector']], on='ticker', how='left')\n",
" else:\n",
" merged_df = stock_df.copy()\n",
" # set null to others\n",
" merged_df['aggregate_sector'] = merged_df['aggregate_sector'].fillna('其他')\n",
" # calculate previous_sector_weight\n",
" merged_df['previous_sector_weight'] = merged_df['previous_weight'] / \\\n",
" merged_df.groupby(['date', 'aggregate_sector'])['previous_weight'].transform('sum')\n",
" # calculate initial sectore weight\n",
" merged_df['initial_sector_weight'] = merged_df['initial_weight'] / \\\n",
" merged_df.groupby(['date', 'aggregate_sector'])['initial_weight'].transform('sum')\n",
" \n",
" return merged_df"
]
},
{
"cell_type": "code",
"execution_count": 156,
"metadata": {},
"outputs": [],
"source": [
"portfolio_df = create_sector_weight(stock_df = portfolio_df, profile_df = profile_df)\n",
"benchmark_df = create_sector_weight(benchmark_df)\n"
]
},
{
"cell_type": "code",
"execution_count": 157,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"aggregate_sector\n",
"信息与通信 1.0\n",
"公用事业 1.0\n",
"其他 1.0\n",
"医药卫生 1.0\n",
"原料与能源 1.0\n",
"工业 1.0\n",
"消费 1.0\n",
"金融与地产 1.0\n",
"Name: previous_sector_weight, dtype: float64\n",
"aggregate_sector\n",
"信息与通信 1.0\n",
"公用事业 1.0\n",
"其他 1.0\n",
"医药卫生 1.0\n",
"原料与能源 1.0\n",
"工业 1.0\n",
"消费 1.0\n",
"金融与地产 1.0\n",
"Name: initial_sector_weight, dtype: float64\n"
]
}
],
"source": [
"# check result \n",
"print(benchmark_df[benchmark_df.date == datetime(2021, 3, 12)].groupby('aggregate_sector')['previous_sector_weight'].sum())\n",
"print(benchmark_df[benchmark_df.date == datetime(2021, 3, 12)].groupby('aggregate_sector')['initial_sector_weight'].sum())\n"
]
},
{
"cell_type": "code",
"execution_count": 158,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"aggregate_sector\n",
"信息与通信 1.0\n",
"医药卫生 1.0\n",
"原料与能源 1.0\n",
"工业 1.0\n",
"消费 1.0\n",
"Name: previous_sector_weight, dtype: float64\n",
"aggregate_sector\n",
"信息与通信 1.0\n",
"医药卫生 1.0\n",
"原料与能源 1.0\n",
"工业 1.0\n",
"消费 1.0\n",
"Name: initial_sector_weight, dtype: float64\n"
]
}
],
"source": [
"# check result \n",
"print(portfolio_df[portfolio_df.date == datetime(2021, 3, 12)].groupby('aggregate_sector')['previous_sector_weight'].sum())\n",
"print(portfolio_df[portfolio_df.date == datetime(2021, 3, 12)].groupby('aggregate_sector')['initial_sector_weight'].sum())\n"
]
},
{
"cell_type": "code",
"execution_count": 159,
"metadata": {},
"outputs": [],
"source": [
"## return define as the total return since the portfolio created\n",
"def calcualte_return(stock_df):\n",
" stock_df['return'] = stock_df['close'] / stock_df.groupby(['ticker'])['close'].transform('first') - 1"
]
},
{
"cell_type": "code",
"execution_count": 160,
"metadata": {},
"outputs": [],
"source": [
"calcualte_return(portfolio_df)\n",
"calcualte_return(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 161,
"metadata": {},
"outputs": [],
"source": [
"def calculate_weighted_sector_return(stock_df):\n",
" stock_df['weighted_sectore_return'] = stock_df['return'] * stock_df['initial_sector_weight']"
]
},
{
"cell_type": "code",
"execution_count": 162,
"metadata": {},
"outputs": [],
"source": [
"calculate_weighted_sector_return(portfolio_df)\n",
"calculate_weighted_sector_return(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 163,
"metadata": {},
"outputs": [],
"source": [
"## weighted return and sector weighred return \n",
"def calculate_weighted_return(stock_df):\n",
" stock_df['weighted_return'] = stock_df['return'] * stock_df['initial_weight']"
]
},
{
"cell_type": "code",
"execution_count": 164,
"metadata": {},
"outputs": [],
"source": [
"# step\n",
"calculate_weighted_return(portfolio_df)\n",
"calculate_weighted_return(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 165,
"metadata": {},
"outputs": [],
"source": [
"def calculate_weighted_sector_return(stock_df):\n",
" stock_df['weighted_sector_return'] = stock_df['return'] * stock_df['initial_sector_weight']"
]
},
{
"cell_type": "code",
"execution_count": 166,
"metadata": {},
"outputs": [],
"source": [
"calculate_weighted_sector_return(portfolio_df)\n",
"calculate_weighted_sector_return(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 167,
"metadata": {},
"outputs": [],
"source": [
"## calcualte weighted pc\n",
"def calculate_weighted_pct(stock_df):\n",
" stock_df['weighted_pct'] = stock_df['pct'] * stock_df['previous_weight']\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 168,
"metadata": {},
"outputs": [],
"source": [
"def calculate_weighted_sector_pct(stock_df):\n",
" stock_df['weighted_sector_pct'] = stock_df['pct'] * stock_df['previous_sector_weight']\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 169,
"metadata": {},
"outputs": [],
"source": [
"calculate_weighted_sector_pct(portfolio_df)\n",
"calculate_weighted_sector_pct(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 170,
"metadata": {},
"outputs": [],
"source": [
"calculate_weighted_pct(portfolio_df)\n",
"calculate_weighted_pct(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 171,
"metadata": {},
"outputs": [],
"source": [
"calculate_weighted_sector_return(portfolio_df)\n",
"calculate_weighted_sector_return(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 172,
"metadata": {},
"outputs": [],
"source": [
"## aggregate by date\n",
"\n",
"# pct and weighted_return\n",
"# def agg_by_date(stock_df)\n",
"def agg_by_date(stock_df):\n",
" agg_on_date_df = pd.DataFrame(stock_df.groupby('date')[['weighted_return','weighted_pct']].sum())\n",
" agg_on_date_df.rename(columns={'weighted_return': 'return', 'weighted_pct': 'pct'}, inplace=True)\n",
" return agg_on_date_df\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 173,
"metadata": {},
"outputs": [],
"source": [
"p_total_view = agg_by_date(portfolio_df)\n",
"b_total_view = agg_by_date(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 174,
"metadata": {},
"outputs": [],
"source": [
"## aggregate by sector\n",
"def agg_by_sector(stock_df):\n",
" agg_on_sector_df = pd.DataFrame(stock_df.groupby(['aggregate_sector','date'])[['weighted_sector_return','weighted_sector_pct']].sum())\n",
" agg_on_sector_df.rename(columns={'weighted_sector_return': 'return', 'weighted_sector_pct': 'pct'}, inplace=True)\n",
" return agg_on_sector_df"
]
},
{
"cell_type": "code",
"execution_count": 175,
"metadata": {},
"outputs": [],
"source": [
"p_sector_view = agg_by_sector(portfolio_df)\n",
"b_sector_view = agg_by_sector(benchmark_df)"
]
},
{
"cell_type": "code",
"execution_count": 200,
"metadata": {},
"outputs": [],
"source": [
"def create_risk_table(portfolio_summary, benchmark_summary):\n",
" # total risk tracking error \n",
" merged_df = pd.merge(portfolio_summary, benchmark_summary, on='date', how='outer', suffixes=('_p', '_b'))\n",
" merged_df['risk_p'] = merged_df['return_p'].expanding().std() * math.sqrt(252)\n",
" merged_df['risk_b'] = merged_df['return_b'].expanding().std() * math.sqrt(252)\n",
" merged_df['active_return'] = merged_df['return_p'] - merged_df['return_b']\n",
" merged_df['tracking_error'] = merged_df['active_return'].expanding().std() * math.sqrt(252)\n",
" merged_df['date'] = merged_df.index\n",
" # drop index\n",
" merged_df.reset_index(drop=True, inplace=True)\n",
" return merged_df"
]
},
{
"cell_type": "code",
"execution_count": 201,
"metadata": {},
"outputs": [],
"source": [
"portfolio_risk_by_date_df = create_risk_table(p_total_view, b_total_view)"
]
},
{
"cell_type": "code",
"execution_count": 202,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" return_p | \n",
" pct_p | \n",
" return_b | \n",
" pct_b | \n",
" risk_p | \n",
" risk_b | \n",
" active_return | \n",
" tracking_error | \n",
" date | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 0.000000 | \n",
" 0.000000 | \n",
" 0.000000 | \n",
" 0.000000 | \n",
" NaN | \n",
" NaN | \n",
" 0.000000 | \n",
" NaN | \n",
" 2021-01-05 | \n",
"
\n",
" \n",
" 1 | \n",
" 0.012146 | \n",
" 0.012146 | \n",
" -0.001934 | \n",
" -0.001934 | \n",
" 0.136341 | \n",
" 0.021705 | \n",
" 0.014080 | \n",
" 0.158046 | \n",
" 2021-01-06 | \n",
"
\n",
" \n",
" 2 | \n",
" 0.086830 | \n",
" 0.074233 | \n",
" -0.000811 | \n",
" 0.001125 | \n",
" 0.746402 | \n",
" 0.015414 | \n",
" 0.087641 | \n",
" 0.747127 | \n",
" 2021-01-07 | \n",
"
\n",
" \n",
" 3 | \n",
" 0.089435 | \n",
" 0.002496 | \n",
" 0.002535 | \n",
" 0.003349 | \n",
" 0.756382 | \n",
" 0.030137 | \n",
" 0.086900 | \n",
" 0.740979 | \n",
" 2021-01-08 | \n",
"
\n",
" \n",
" 4 | \n",
" 0.148063 | \n",
" 0.029363 | \n",
" -0.013015 | \n",
" -0.015511 | \n",
" 0.970984 | \n",
" 0.095654 | \n",
" 0.161078 | \n",
" 1.032423 | \n",
" 2021-01-11 | \n",
"
\n",
" \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
"
\n",
" \n",
" 242 | \n",
" 0.028005 | \n",
" -0.071081 | \n",
" 0.086827 | \n",
" 0.000156 | \n",
" 2.097631 | \n",
" 0.886298 | \n",
" -0.058822 | \n",
" 1.856213 | \n",
" 2022-01-04 | \n",
"
\n",
" \n",
" 243 | \n",
" -0.033053 | \n",
" -0.059582 | \n",
" 0.067931 | \n",
" -0.017386 | \n",
" 2.099052 | \n",
" 0.884891 | \n",
" -0.100984 | \n",
" 1.861347 | \n",
" 2022-01-05 | \n",
"
\n",
" \n",
" 244 | \n",
" -0.042238 | \n",
" -0.008112 | \n",
" 0.069522 | \n",
" 0.001490 | \n",
" 2.101118 | \n",
" 0.883542 | \n",
" -0.111761 | \n",
" 1.867445 | \n",
" 2022-01-06 | \n",
"
\n",
" \n",
" 245 | \n",
" -0.073118 | \n",
" -0.031015 | \n",
" 0.062056 | \n",
" -0.006981 | \n",
" 2.105760 | \n",
" 0.881986 | \n",
" -0.135174 | \n",
" 1.875959 | \n",
" 2022-01-07 | \n",
"
\n",
" \n",
" 246 | \n",
" -0.029944 | \n",
" 0.044300 | \n",
" 0.064588 | \n",
" 0.002384 | \n",
" 2.106749 | \n",
" 0.880502 | \n",
" -0.094532 | \n",
" 1.880060 | \n",
" 2022-01-10 | \n",
"
\n",
" \n",
"
\n",
"
247 rows × 9 columns
\n",
"
"
],
"text/plain": [
" return_p pct_p return_b pct_b risk_p risk_b \\\n",
"0 0.000000 0.000000 0.000000 0.000000 NaN NaN \n",
"1 0.012146 0.012146 -0.001934 -0.001934 0.136341 0.021705 \n",
"2 0.086830 0.074233 -0.000811 0.001125 0.746402 0.015414 \n",
"3 0.089435 0.002496 0.002535 0.003349 0.756382 0.030137 \n",
"4 0.148063 0.029363 -0.013015 -0.015511 0.970984 0.095654 \n",
".. ... ... ... ... ... ... \n",
"242 0.028005 -0.071081 0.086827 0.000156 2.097631 0.886298 \n",
"243 -0.033053 -0.059582 0.067931 -0.017386 2.099052 0.884891 \n",
"244 -0.042238 -0.008112 0.069522 0.001490 2.101118 0.883542 \n",
"245 -0.073118 -0.031015 0.062056 -0.006981 2.105760 0.881986 \n",
"246 -0.029944 0.044300 0.064588 0.002384 2.106749 0.880502 \n",
"\n",
" active_return tracking_error date \n",
"0 0.000000 NaN 2021-01-05 \n",
"1 0.014080 0.158046 2021-01-06 \n",
"2 0.087641 0.747127 2021-01-07 \n",
"3 0.086900 0.740979 2021-01-08 \n",
"4 0.161078 1.032423 2021-01-11 \n",
".. ... ... ... \n",
"242 -0.058822 1.856213 2022-01-04 \n",
"243 -0.100984 1.861347 2022-01-05 \n",
"244 -0.111761 1.867445 2022-01-06 \n",
"245 -0.135174 1.875959 2022-01-07 \n",
"246 -0.094532 1.880060 2022-01-10 \n",
"\n",
"[247 rows x 9 columns]"
]
},
"execution_count": 202,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# add mkt cap\n",
"portfolio_risk_by_date_df"
]
},
{
"cell_type": "code",
"execution_count": 217,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"date\n",
"2021-01-05 600\n",
"2021-01-10 1550\n",
"Name: weight, dtype: int64"
]
},
"execution_count": 217,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"profile_df.groupby('date')['weight'].sum()\n",
"\n",
"# for i in range(1, len(portfolio_risk_by_date_df)):\n",
"# cur_mkt = portfolio_risk_by_date_df.loc[i, 'mkt_cap']\n",
"# if pd.isna(cur_mkt):\n",
"# portfolio_risk_by_date_df.loc[i, 'mkt_cap'] = portfolio_risk_by_date_df.loc[i-1, 'mkt_cap'] * (1 + portfolio_risk_by_date_df.loc[i, 'pct_p'])\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 216,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" return_p | \n",
" pct_p | \n",
" return_b | \n",
" pct_b | \n",
" risk_p | \n",
" risk_b | \n",
" active_return | \n",
" tracking_error | \n",
" date | \n",
" mkt_cap | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 0.0 | \n",
" 0.0 | \n",
" 0.0 | \n",
" 0.0 | \n",
" NaN | \n",
" NaN | \n",
" 0.0 | \n",
" NaN | \n",
" 2021-01-05 | \n",
" 600.0 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" return_p pct_p return_b pct_b risk_p risk_b active_return \\\n",
"0 0.0 0.0 0.0 0.0 NaN NaN 0.0 \n",
"\n",
" tracking_error date mkt_cap \n",
"0 NaN 2021-01-05 600.0 "
]
},
"execution_count": 216,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# display row where mkt_cap is not nana\n",
"portfolio_risk_by_date_df[portfolio_risk_by_date_df['mkt_cap'].notna()]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/v5/2108rh5964q9j741wg_s8r1w0000gn/T/ipykernel_23255/2871737262.py:10: SettingWithCopyWarning: \n",
"A value is trying to be set on a copy of a slice from a DataFrame.\n",
"Try using .loc[row_indexer,col_indexer] = value instead\n",
"\n",
"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
" pct['weighted_pct'] = pct['pct'] * pct['norm_weight']\n"
]
},
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" date | \n",
" return | \n",
" pct | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 2021-01-05 | \n",
" 0.000000 | \n",
" 0.000000 | \n",
"
\n",
" \n",
" 1 | \n",
" 2021-01-06 | \n",
" 0.007011 | \n",
" 0.036439 | \n",
"
\n",
" \n",
" 2 | \n",
" 2021-01-07 | \n",
" 0.047531 | \n",
" 0.218707 | \n",
"
\n",
" \n",
" 3 | \n",
" 2021-01-08 | \n",
" 0.047111 | \n",
" 0.013639 | \n",
"
\n",
" \n",
" 4 | \n",
" 2021-01-11 | \n",
" 0.052768 | \n",
" 0.014559 | \n",
"
\n",
" \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
"
\n",
" \n",
" 242 | \n",
" 2022-01-04 | \n",
" 0.363845 | \n",
" -0.199827 | \n",
"
\n",
" \n",
" 243 | \n",
" 2022-01-05 | \n",
" 0.306697 | \n",
" -0.193598 | \n",
"
\n",
" \n",
" 244 | \n",
" 2022-01-06 | \n",
" 0.331291 | \n",
" 0.023418 | \n",
"
\n",
" \n",
" 245 | \n",
" 2022-01-07 | \n",
" 0.313726 | \n",
" -0.080728 | \n",
"
\n",
" \n",
" 246 | \n",
" 2022-01-10 | \n",
" 0.313262 | \n",
" 0.110254 | \n",
"
\n",
" \n",
"
\n",
"
247 rows × 3 columns
\n",
"
"
],
"text/plain": [
" date return pct\n",
"0 2021-01-05 0.000000 0.000000\n",
"1 2021-01-06 0.007011 0.036439\n",
"2 2021-01-07 0.047531 0.218707\n",
"3 2021-01-08 0.047111 0.013639\n",
"4 2021-01-11 0.052768 0.014559\n",
".. ... ... ...\n",
"242 2022-01-04 0.363845 -0.199827\n",
"243 2022-01-05 0.306697 -0.193598\n",
"244 2022-01-06 0.331291 0.023418\n",
"245 2022-01-07 0.313726 -0.080728\n",
"246 2022-01-10 0.313262 0.110254\n",
"\n",
"[247 rows x 3 columns]"
]
},
"execution_count": 191,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"## aggregate by date\n",
"# step 7 aggregate (get portfolio return and pct(change of daily return))by date\n",
"def create_agg_by_date(stock_df):\n",
" # sum up weighted return to get return \n",
" agg_return = stock_df.groupby(['date'])['weighted_return'].sum().reset_index()\n",
" agg_return.rename(columns={'weighted_return':'return'}, inplace=True)\n",
"\n",
" # sum up weighted pct to get pct\n",
" pct = stock_df[['date','pct','norm_weight','ticker']]\n",
" pct['weighted_pct'] = pct['pct'] * pct['norm_weight']\n",
" agg_pct = pct.groupby(['date'])['pct'].sum().reset_index()\n",
"\n",
" agg_df = pd.merge(agg_return, agg_pct, on='date', how='outer')\n",
" return agg_df\n",
"\n",
"\n",
"\n",
"p_perform_result = create_agg_by_date(p_stock_df)\n",
"p_perform_result"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" ticker | \n",
" date | \n",
" open | \n",
" close | \n",
" high | \n",
" low | \n",
" volume | \n",
" money | \n",
" pct | \n",
" weight | \n",
" return | \n",
" norm_weight | \n",
" weighted_return | \n",
" aggregate_sector | \n",
" display_name | \n",
"
\n",
" \n",
" \n",
" \n",
" 1452 | \n",
" 603882.XSHG | \n",
" 2022-01-04 | \n",
" 106.89 | \n",
" 98.84 | \n",
" 106.89 | \n",
" 98.67 | \n",
" 5140406.0 | \n",
" 5.181929e+08 | \n",
" -0.076262 | \n",
" 79.300385 | \n",
" -0.206996 | \n",
" 0.107586 | \n",
" -0.022270 | \n",
" 医药卫生 | \n",
" 金域医学 | \n",
"
\n",
" \n",
" 1453 | \n",
" 002709.XSHE | \n",
" 2022-01-04 | \n",
" 57.64 | \n",
" 54.64 | \n",
" 57.87 | \n",
" 54.29 | \n",
" 42150916.0 | \n",
" 2.333429e+09 | \n",
" -0.028277 | \n",
" 161.227501 | \n",
" 0.612275 | \n",
" 0.218735 | \n",
" 0.133926 | \n",
" 工业 | \n",
" 天赐材料 | \n",
"
\n",
" \n",
" 1454 | \n",
" 600409.XSHG | \n",
" 2022-01-04 | \n",
" 8.16 | \n",
" 8.21 | \n",
" 8.25 | \n",
" 8.15 | \n",
" 27288613.0 | \n",
" 2.237925e+08 | \n",
" 0.007362 | \n",
" 85.788924 | \n",
" -0.142111 | \n",
" 0.116389 | \n",
" -0.016540 | \n",
" 原料与能源 | \n",
" 三友化工 | \n",
"
\n",
" \n",
" 1455 | \n",
" 002920.XSHE | \n",
" 2022-01-04 | \n",
" 139.71 | \n",
" 131.69 | \n",
" 140.91 | \n",
" 131.45 | \n",
" 5410083.0 | \n",
" 7.233361e+08 | \n",
" -0.060833 | \n",
" 150.934097 | \n",
" 0.509341 | \n",
" 0.204770 | \n",
" 0.104298 | \n",
" 信息与通信 | \n",
" 德赛西威 | \n",
"
\n",
" \n",
" 1456 | \n",
" 300274.XSHE | \n",
" 2022-01-04 | \n",
" 146.52 | \n",
" 134.96 | \n",
" 148.46 | \n",
" 134.61 | \n",
" 24205007.0 | \n",
" 3.333125e+09 | \n",
" -0.071291 | \n",
" 176.533682 | \n",
" 0.765337 | \n",
" 0.239501 | \n",
" 0.183299 | \n",
" 工业 | \n",
" 阳光电源 | \n",
"
\n",
" \n",
" 1457 | \n",
" 600415.XSHG | \n",
" 2022-01-04 | \n",
" 4.80 | \n",
" 4.89 | \n",
" 4.90 | \n",
" 4.78 | \n",
" 58291943.0 | \n",
" 2.832956e+08 | \n",
" 0.029474 | \n",
" 83.304940 | \n",
" -0.166951 | \n",
" 0.113019 | \n",
" -0.018869 | \n",
" 消费 | \n",
" 小商品城 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" ticker date open close high low volume \\\n",
"1452 603882.XSHG 2022-01-04 106.89 98.84 106.89 98.67 5140406.0 \n",
"1453 002709.XSHE 2022-01-04 57.64 54.64 57.87 54.29 42150916.0 \n",
"1454 600409.XSHG 2022-01-04 8.16 8.21 8.25 8.15 27288613.0 \n",
"1455 002920.XSHE 2022-01-04 139.71 131.69 140.91 131.45 5410083.0 \n",
"1456 300274.XSHE 2022-01-04 146.52 134.96 148.46 134.61 24205007.0 \n",
"1457 600415.XSHG 2022-01-04 4.80 4.89 4.90 4.78 58291943.0 \n",
"\n",
" money pct weight return norm_weight \\\n",
"1452 5.181929e+08 -0.076262 79.300385 -0.206996 0.107586 \n",
"1453 2.333429e+09 -0.028277 161.227501 0.612275 0.218735 \n",
"1454 2.237925e+08 0.007362 85.788924 -0.142111 0.116389 \n",
"1455 7.233361e+08 -0.060833 150.934097 0.509341 0.204770 \n",
"1456 3.333125e+09 -0.071291 176.533682 0.765337 0.239501 \n",
"1457 2.832956e+08 0.029474 83.304940 -0.166951 0.113019 \n",
"\n",
" weighted_return aggregate_sector display_name \n",
"1452 -0.022270 医药卫生 金域医学 \n",
"1453 0.133926 工业 天赐材料 \n",
"1454 -0.016540 原料与能源 三友化工 \n",
"1455 0.104298 信息与通信 德赛西威 \n",
"1456 0.183299 工业 阳光电源 \n",
"1457 -0.018869 消费 小商品城 "
]
},
"execution_count": 194,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"p_stock_df[p_stock_df.date==datetime(2022,1,4)]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" date | \n",
" mkt_cap | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 2021-01-05 | \n",
" 600 | \n",
"
\n",
" \n",
" 1 | \n",
" 2021-01-10 | \n",
" 1550 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" date mkt_cap\n",
"0 2021-01-05 600\n",
"1 2021-01-10 1550"
]
},
"execution_count": 102,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mkt_cap_df = pd.DataFrame(profile_df.groupby(['date'])['weight'].sum()).reset_index()\n",
"mkt_cap_df.rename(columns={'weight':'mkt_cap'}, inplace=True)\n",
"mkt_cap_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" date | \n",
" return | \n",
" pct | \n",
" mkt_cap | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 2021-01-05 | \n",
" 0.000000 | \n",
" 0.000000 | \n",
" 600.000000 | \n",
"
\n",
" \n",
" 1 | \n",
" 2021-01-06 | \n",
" 0.007011 | \n",
" 0.036439 | \n",
" 621.863161 | \n",
"
\n",
" \n",
" 2 | \n",
" 2021-01-07 | \n",
" 0.047531 | \n",
" 0.218707 | \n",
" 757.869005 | \n",
"
\n",
" \n",
" 3 | \n",
" 2021-01-08 | \n",
" 0.047111 | \n",
" 0.013639 | \n",
" 768.205269 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" date return pct mkt_cap\n",
"0 2021-01-05 0.000000 0.000000 600.000000\n",
"1 2021-01-06 0.007011 0.036439 621.863161\n",
"2 2021-01-07 0.047531 0.218707 757.869005\n",
"3 2021-01-08 0.047111 0.013639 768.205269"
]
},
"execution_count": 103,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# get mkt adjustment (weight is the fund in a stock)\n",
"mkt_adjustment = pd.DataFrame(profile_df.groupby(['date'])['weight'].sum()).reset_index()\n",
"mkt_adjustment.rename(columns={'weight':'mkt_cap'}, inplace=True)\n",
"merge_df = p_perform_result.merge(mkt_adjustment, on='date', how='outer')\n",
"\n",
"\n",
"for i in range(1, len(merge_df)):\n",
" merge_df.loc[i, 'mkt_cap'] = merge_df.loc[i-1, 'mkt_cap'] * (1 + merge_df.loc[i, 'pct'])\n",
"\n",
"# # calculate daily mkt_cap\n",
"# # initial_mkt_cap = merge_df.loc[0, 'mkt_cap']\n",
"# for i in range(1, len(merge_df)):\n",
"# row = merge_df.loc[i]\n",
"# if pd.isna(row['mkt_cap']):\n",
"# merge_df.loc[i, 'mkt_cap'] = merge_df.loc[i-1, 'mkt_cap'] * (1 + merge_df.loc[i, 'pct_portfolio'])\n",
" \n",
"# # step 8 calculate daily mkt cap\n",
"\n",
"merge_df[merge_df.date < datetime(2021,1,10)]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" ticker | \n",
" date | \n",
" open | \n",
" close | \n",
" high | \n",
" low | \n",
" volume | \n",
" money | \n",
" pct | \n",
" weight | \n",
" return | \n",
" norm_weight | \n",
" weighted_return | \n",
" aggregate_sector | \n",
" display_name | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 002709.XSHE | \n",
" 2021-01-05 | \n",
" 32.54 | \n",
" 33.89 | \n",
" 34.22 | \n",
" 31.39 | \n",
" 59152352.0 | \n",
" 1.942406e+09 | \n",
" NaN | \n",
" 100.000000 | \n",
" 0.000000 | \n",
" 0.166667 | \n",
" 0.000000 | \n",
" 工业 | \n",
" 天赐材料 | \n",
"
\n",
" \n",
" 1 | \n",
" 600415.XSHG | \n",
" 2021-01-05 | \n",
" 5.33 | \n",
" 5.87 | \n",
" 5.87 | \n",
" 5.22 | \n",
" 180936477.0 | \n",
" 1.010225e+09 | \n",
" NaN | \n",
" 100.000000 | \n",
" 0.000000 | \n",
" 0.166667 | \n",
" 0.000000 | \n",
" 消费 | \n",
" 小商品城 | \n",
"
\n",
" \n",
" 2 | \n",
" 600409.XSHG | \n",
" 2021-01-05 | \n",
" 9.23 | \n",
" 9.57 | \n",
" 9.66 | \n",
" 9.08 | \n",
" 82669289.0 | \n",
" 7.803391e+08 | \n",
" NaN | \n",
" 100.000000 | \n",
" 0.000000 | \n",
" 0.166667 | \n",
" 0.000000 | \n",
" 原料与能源 | \n",
" 三友化工 | \n",
"
\n",
" \n",
" 3 | \n",
" 300274.XSHE | \n",
" 2021-01-05 | \n",
" 76.03 | \n",
" 76.45 | \n",
" 80.20 | \n",
" 75.27 | \n",
" 51384827.0 | \n",
" 3.961995e+09 | \n",
" NaN | \n",
" 100.000000 | \n",
" 0.000000 | \n",
" 0.166667 | \n",
" 0.000000 | \n",
" 工业 | \n",
" 阳光电源 | \n",
"
\n",
" \n",
" 4 | \n",
" 002920.XSHE | \n",
" 2021-01-05 | \n",
" 85.44 | \n",
" 87.25 | \n",
" 87.95 | \n",
" 84.07 | \n",
" 3852674.0 | \n",
" 3.322598e+08 | \n",
" NaN | \n",
" 100.000000 | \n",
" 0.000000 | \n",
" 0.166667 | \n",
" 0.000000 | \n",
" 信息与通信 | \n",
" 德赛西威 | \n",
"
\n",
" \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
" ... | \n",
"
\n",
" \n",
" 1477 | \n",
" 600409.XSHG | \n",
" 2022-01-10 | \n",
" 8.24 | \n",
" 8.35 | \n",
" 8.39 | \n",
" 8.21 | \n",
" 32516017.0 | \n",
" 2.699300e+08 | \n",
" 0.015815 | \n",
" 87.251829 | \n",
" -0.127482 | \n",
" 0.121949 | \n",
" -0.015546 | \n",
" 原料与能源 | \n",
" 三友化工 | \n",
"
\n",
" \n",
" 1478 | \n",
" 002920.XSHE | \n",
" 2022-01-10 | \n",
" 130.36 | \n",
" 138.43 | \n",
" 141.96 | \n",
" 130.11 | \n",
" 5005400.0 | \n",
" 6.901614e+08 | \n",
" 0.046888 | \n",
" 158.659026 | \n",
" 0.586590 | \n",
" 0.221752 | \n",
" 0.130077 | \n",
" 信息与通信 | \n",
" 德赛西威 | \n",
"
\n",
" \n",
" 1479 | \n",
" 002709.XSHE | \n",
" 2022-01-10 | \n",
" 51.63 | \n",
" 50.73 | \n",
" 51.93 | \n",
" 50.03 | \n",
" 29821246.0 | \n",
" 1.518902e+09 | \n",
" -0.019142 | \n",
" 149.690174 | \n",
" 0.496902 | \n",
" 0.209216 | \n",
" 0.103960 | \n",
" 工业 | \n",
" 天赐材料 | \n",
"
\n",
" \n",
" 1480 | \n",
" 600415.XSHG | \n",
" 2022-01-10 | \n",
" 4.70 | \n",
" 4.75 | \n",
" 4.85 | \n",
" 4.67 | \n",
" 39278041.0 | \n",
" 1.859827e+08 | \n",
" 0.010638 | \n",
" 80.919932 | \n",
" -0.190801 | \n",
" 0.113099 | \n",
" -0.021579 | \n",
" 消费 | \n",
" 小商品城 | \n",
"
\n",
" \n",
" 1481 | \n",
" 603882.XSHG | \n",
" 2022-01-10 | \n",
" 88.45 | \n",
" 95.53 | \n",
" 95.59 | \n",
" 88.39 | \n",
" 6991445.0 | \n",
" 6.468392e+08 | \n",
" 0.085692 | \n",
" 76.644737 | \n",
" -0.233553 | \n",
" 0.107123 | \n",
" -0.025019 | \n",
" 医药卫生 | \n",
" 金域医学 | \n",
"
\n",
" \n",
"
\n",
"
1482 rows × 15 columns
\n",
"
"
],
"text/plain": [
" ticker date open close high low volume \\\n",
"0 002709.XSHE 2021-01-05 32.54 33.89 34.22 31.39 59152352.0 \n",
"1 600415.XSHG 2021-01-05 5.33 5.87 5.87 5.22 180936477.0 \n",
"2 600409.XSHG 2021-01-05 9.23 9.57 9.66 9.08 82669289.0 \n",
"3 300274.XSHE 2021-01-05 76.03 76.45 80.20 75.27 51384827.0 \n",
"4 002920.XSHE 2021-01-05 85.44 87.25 87.95 84.07 3852674.0 \n",
"... ... ... ... ... ... ... ... \n",
"1477 600409.XSHG 2022-01-10 8.24 8.35 8.39 8.21 32516017.0 \n",
"1478 002920.XSHE 2022-01-10 130.36 138.43 141.96 130.11 5005400.0 \n",
"1479 002709.XSHE 2022-01-10 51.63 50.73 51.93 50.03 29821246.0 \n",
"1480 600415.XSHG 2022-01-10 4.70 4.75 4.85 4.67 39278041.0 \n",
"1481 603882.XSHG 2022-01-10 88.45 95.53 95.59 88.39 6991445.0 \n",
"\n",
" money pct weight return norm_weight \\\n",
"0 1.942406e+09 NaN 100.000000 0.000000 0.166667 \n",
"1 1.010225e+09 NaN 100.000000 0.000000 0.166667 \n",
"2 7.803391e+08 NaN 100.000000 0.000000 0.166667 \n",
"3 3.961995e+09 NaN 100.000000 0.000000 0.166667 \n",
"4 3.322598e+08 NaN 100.000000 0.000000 0.166667 \n",
"... ... ... ... ... ... \n",
"1477 2.699300e+08 0.015815 87.251829 -0.127482 0.121949 \n",
"1478 6.901614e+08 0.046888 158.659026 0.586590 0.221752 \n",
"1479 1.518902e+09 -0.019142 149.690174 0.496902 0.209216 \n",
"1480 1.859827e+08 0.010638 80.919932 -0.190801 0.113099 \n",
"1481 6.468392e+08 0.085692 76.644737 -0.233553 0.107123 \n",
"\n",
" weighted_return aggregate_sector display_name \n",
"0 0.000000 工业 天赐材料 \n",
"1 0.000000 消费 小商品城 \n",
"2 0.000000 原料与能源 三友化工 \n",
"3 0.000000 工业 阳光电源 \n",
"4 0.000000 信息与通信 德赛西威 \n",
"... ... ... ... \n",
"1477 -0.015546 原料与能源 三友化工 \n",
"1478 0.130077 信息与通信 德赛西威 \n",
"1479 0.103960 工业 天赐材料 \n",
"1480 -0.021579 消费 小商品城 \n",
"1481 -0.025019 医药卫生 金域医学 \n",
"\n",
"[1482 rows x 15 columns]"
]
},
"execution_count": 127,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"## agg by sector and day\n",
"p_stock_df['weight_in_sector'] = p_stock_df.groupby"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def creaet_portfolio_return(stock_df):\n",
" portfolio_df = stock_df.groupby(['date'])['weighted_return'].sum().reset_index()\n",
" portfolio_df.rename(columns={'weighted_return':'portfolio_return'}, inplace=True)\n",
" return portfolio_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" date | \n",
" portfolio_return | \n",
"
\n",
" \n",
" \n",
" \n",
" 0 | \n",
" 2021-01-05 | \n",
" 0.000000 | \n",
"
\n",
" \n",
" 1 | \n",
" 2021-01-06 | \n",
" 0.007011 | \n",
"
\n",
" \n",
" 2 | \n",
" 2021-01-07 | \n",
" 0.047531 | \n",
"
\n",
" \n",
" 3 | \n",
" 2021-01-08 | \n",
" 0.047111 | \n",
"
\n",
" \n",
" 4 | \n",
" 2021-01-11 | \n",
" 0.052768 | \n",
"
\n",
" \n",
" ... | \n",
" ... | \n",
" ... | \n",
"
\n",
" \n",
" 242 | \n",
" 2022-01-04 | \n",
" 0.363845 | \n",
"
\n",
" \n",
" 243 | \n",
" 2022-01-05 | \n",
" 0.306697 | \n",
"
\n",
" \n",
" 244 | \n",
" 2022-01-06 | \n",
" 0.331291 | \n",
"
\n",
" \n",
" 245 | \n",
" 2022-01-07 | \n",
" 0.313726 | \n",
"
\n",
" \n",
" 246 | \n",
" 2022-01-10 | \n",
" 0.313262 | \n",
"
\n",
" \n",
"
\n",
"
247 rows × 2 columns
\n",
"
"
],
"text/plain": [
" date portfolio_return\n",
"0 2021-01-05 0.000000\n",
"1 2021-01-06 0.007011\n",
"2 2021-01-07 0.047531\n",
"3 2021-01-08 0.047111\n",
"4 2021-01-11 0.052768\n",
".. ... ...\n",
"242 2022-01-04 0.363845\n",
"243 2022-01-05 0.306697\n",
"244 2022-01-06 0.331291\n",
"245 2022-01-07 0.313726\n",
"246 2022-01-10 0.313262\n",
"\n",
"[247 rows x 2 columns]"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"portfolio_df = creaet_portfolio_return(p_stock_df)\n",
"portfolio_df"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "portfolio_risk_assesment",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}