Commit 6eee2118 authored by Dr.李's avatar Dr.李

added first version of brinson

parent e41f62d2
......@@ -245,7 +245,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 5,
"id": "c75c33b2-c69e-412c-9151-8f6671c00cec",
"metadata": {},
"outputs": [
......@@ -339,7 +339,7 @@
"[1 rows x 39 columns]"
]
},
"execution_count": 6,
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
......@@ -351,7 +351,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 6,
"id": "e06d5836-1e39-47e7-9e1e-3ba77433aa05",
"metadata": {},
"outputs": [
......@@ -384,17 +384,60 @@
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000045</td>\n",
" <td>海信视像</td>\n",
" <td>600060.XSHG</td>\n",
" <td>-1.983</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000121</td>\n",
" <td>中青旅</td>\n",
" <td>600138.XSHG</td>\n",
" <td>-0.743</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000171</td>\n",
" <td>复星医药</td>\n",
" <td>600196.XSHG</td>\n",
" <td>-0.642</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000377</td>\n",
" <td>片仔癀</td>\n",
" <td>600436.XSHG</td>\n",
" <td>-1.400</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000438</td>\n",
" <td>贵州茅台</td>\n",
" <td>600519.XSHG</td>\n",
" <td>-0.835</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
"Empty DataFrame\n",
"Columns: [trade_date, security_code, secShortName, symbol, spret]\n",
"Index: []"
" trade_date security_code secShortName symbol spret\n",
"0 2021-06-30 2010000045 海信视像 600060.XSHG -1.983\n",
"1 2021-06-30 2010000121 中青旅 600138.XSHG -0.743\n",
"2 2021-06-30 2010000171 复星医药 600196.XSHG -0.642\n",
"3 2021-06-30 2010000377 片仔癀 600436.XSHG -1.400\n",
"4 2021-06-30 2010000438 贵州茅台 600519.XSHG -0.835"
]
},
"execution_count": 8,
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
......@@ -406,7 +449,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 7,
"id": "48d5bd0f-d33b-4b5e-bc91-b622cba7c0be",
"metadata": {},
"outputs": [
......@@ -612,7 +655,7 @@
"[5 rows x 40 columns]"
]
},
"execution_count": 11,
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
......@@ -624,7 +667,7 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 8,
"id": "88854e5b-ad2e-4629-bb8c-9e58a851a298",
"metadata": {},
"outputs": [
......@@ -710,7 +753,7 @@
"4 2021-06-30 2010000438 贵州茅台 600519.XSHG 24.513"
]
},
"execution_count": 15,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
......@@ -723,7 +766,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "6102c448-5515-4f1d-ab82-91c0ce98a7ee",
"id": "017644bc-3053-4170-89fb-3e87d7ee9339",
"metadata": {},
"outputs": [],
"source": []
......
......@@ -9,32 +9,140 @@
"source": [
"import sys\n",
"sys.path.append(\"../\")\n",
"from fof.utility import *"
"import numpy as np\n",
"from fof.utility import *\n",
"from fof.brinson import single_brinson\n",
"from PyFin.api import *"
]
},
{
"cell_type": "markdown",
"id": "ad04be2c-6a66-4c91-b58d-c4e29ad1fc64",
"metadata": {},
"source": [
"# 1. Prepare Data\n",
"-----------------------"
]
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 2,
"id": "1813212b-9027-4831-a245-1a19a44e787b",
"metadata": {},
"outputs": [],
"source": [
"trade_dt = \"20210630\""
"trade_dt = adjustDateByCalendar(\"china.sse\", \"2021-06-30\").strftime(\"%Y%m%d\")\n",
"portfolio_date = \"20211231\""
]
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 3,
"id": "21a0b455-1eb3-47f0-8a9c-15533dd12939",
"metadata": {},
"outputs": [],
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>pid</th>\n",
" <th>security_code</th>\n",
" <th>SKNAME</th>\n",
" <th>HOLDMKTCAP</th>\n",
" <th>HOLDAMT</th>\n",
" <th>weight</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1030000006</td>\n",
" <td>2010000377</td>\n",
" <td>片仔癀</td>\n",
" <td>69974163.35</td>\n",
" <td>160069.0</td>\n",
" <td>0.092948</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1030000006</td>\n",
" <td>2010000671</td>\n",
" <td>中航沈飞</td>\n",
" <td>57228444.00</td>\n",
" <td>841100.0</td>\n",
" <td>0.076017</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>1030000006</td>\n",
" <td>2010000773</td>\n",
" <td>中航高科</td>\n",
" <td>59143464.00</td>\n",
" <td>1653900.0</td>\n",
" <td>0.078561</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>1030000006</td>\n",
" <td>2010000804</td>\n",
" <td>航发动力</td>\n",
" <td>76564490.00</td>\n",
" <td>1206500.0</td>\n",
" <td>0.101702</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1030000006</td>\n",
" <td>2010003333</td>\n",
" <td>爱尔眼科</td>\n",
" <td>61072572.12</td>\n",
" <td>1444479.0</td>\n",
" <td>0.081123</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" pid security_code SKNAME HOLDMKTCAP HOLDAMT weight\n",
"0 1030000006 2010000377 片仔癀 69974163.35 160069.0 0.092948\n",
"1 1030000006 2010000671 中航沈飞 57228444.00 841100.0 0.076017\n",
"2 1030000006 2010000773 中航高科 59143464.00 1653900.0 0.078561\n",
"3 1030000006 2010000804 航发动力 76564490.00 1206500.0 0.101702\n",
"4 1030000006 2010003333 爱尔眼科 61072572.12 1444479.0 0.081123"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"portfolio = fd_skdetail(\"1030000006\", trade_dt=trade_dt)"
"# 获取指定基金在基准日的持仓(portfolio date)\n",
"portfolio = fd_skdetail(\"1030000006\", trade_dt=portfolio_date)\n",
"portfolio.head()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 4,
"id": "82ca3a10-45e2-4b49-8be1-1e77104a1a52",
"metadata": {},
"outputs": [
......@@ -62,71 +170,72 @@
" <th>trade_date</th>\n",
" <th>security_code</th>\n",
" <th>sname</th>\n",
" <th>weighing</th>\n",
" <th>weight</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>115</th>\n",
" <td>2021-06-30</td>\n",
" <th>0</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000001</td>\n",
" <td>浦发银行</td>\n",
" <td>0.55</td>\n",
" <td>0.0045</td>\n",
" </tr>\n",
" <tr>\n",
" <th>116</th>\n",
" <td>2021-06-30</td>\n",
" <th>1</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000010</td>\n",
" <td>上海机场</td>\n",
" <td>0.22</td>\n",
" <td>0.0020</td>\n",
" </tr>\n",
" <tr>\n",
" <th>117</th>\n",
" <td>2021-06-30</td>\n",
" <th>2</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000011</td>\n",
" <td>包钢股份</td>\n",
" <td>0.17</td>\n",
" <td>0.0029</td>\n",
" </tr>\n",
" <tr>\n",
" <th>118</th>\n",
" <td>2021-06-30</td>\n",
" <th>3</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000012</td>\n",
" <td>华能国际</td>\n",
" <td>0.09</td>\n",
" <td>0.0019</td>\n",
" </tr>\n",
" <tr>\n",
" <th>119</th>\n",
" <td>2021-06-30</td>\n",
" <th>4</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000014</td>\n",
" <td>华夏银行</td>\n",
" <td>0.18</td>\n",
" <td>0.0016</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" trade_date security_code sname weighing\n",
"115 2021-06-30 2010000001 浦发银行 0.55\n",
"116 2021-06-30 2010000010 上海机场 0.22\n",
"117 2021-06-30 2010000011 包钢股份 0.17\n",
"118 2021-06-30 2010000012 华能国际 0.09\n",
"119 2021-06-30 2010000014 华夏银行 0.18"
" trade_date security_code sname weight\n",
"0 2021-12-31 2010000001 浦发银行 0.0045\n",
"1 2021-12-31 2010000010 上海机场 0.0020\n",
"2 2021-12-31 2010000011 包钢股份 0.0029\n",
"3 2021-12-31 2010000012 华能国际 0.0019\n",
"4 2021-12-31 2010000014 华夏银行 0.0016"
]
},
"execution_count": 6,
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"idx_comp = index_component(\"2070000060\", trade_dt, trade_dt)\n",
"# 获取基准指数的成分\n",
"idx_comp = index_component(\"2070000060\", portfolio_date, portfolio_date)\n",
"idx_comp.head()"
]
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 5,
"id": "6d57d8f9-6c1c-4a41-ae64-87763648a22b",
"metadata": {},
"outputs": [
......@@ -159,77 +268,121 @@
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>20</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000045</td>\n",
" <td>330200</td>\n",
" <td>视听器材</td>\n",
" <th>0</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000001</td>\n",
" <td>480000</td>\n",
" <td>银行</td>\n",
" </tr>\n",
" <tr>\n",
" <th>79</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000121</td>\n",
" <td>460300</td>\n",
" <td>旅游综合</td>\n",
" <th>1</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000005</td>\n",
" <td>420000</td>\n",
" <td>交通运输</td>\n",
" </tr>\n",
" <tr>\n",
" <th>62</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000171</td>\n",
" <td>370300</td>\n",
" <td>生物制品</td>\n",
" <th>2</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000007</td>\n",
" <td>280000</td>\n",
" <td>汽车</td>\n",
" </tr>\n",
" <tr>\n",
" <th>63</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000377</td>\n",
" <td>370200</td>\n",
" <td>中药</td>\n",
" <th>3</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000008</td>\n",
" <td>430000</td>\n",
" <td>房地产</td>\n",
" </tr>\n",
" <tr>\n",
" <th>28</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000438</td>\n",
" <td>340300</td>\n",
" <td>饮料制造</td>\n",
" <th>4</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010000009</td>\n",
" <td>410000</td>\n",
" <td>公用事业</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" trade_date security_code industry_code industry_name\n",
"20 2021-06-30 2010000045 330200 视听器材\n",
"79 2021-06-30 2010000121 460300 旅游综合\n",
"62 2021-06-30 2010000171 370300 生物制品\n",
"63 2021-06-30 2010000377 370200 中药\n",
"28 2021-06-30 2010000438 340300 饮料制造"
" trade_date security_code industry_code industry_name\n",
"0 2021-12-31 2010000001 480000 银行\n",
"1 2021-12-31 2010000005 420000 交通运输\n",
"2 2021-12-31 2010000007 280000 汽车\n",
"3 2021-12-31 2010000008 430000 房地产\n",
"4 2021-12-31 2010000009 410000 公用事业"
]
},
"execution_count": 10,
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ind = sw_industry_daily(df.SKCODE, trade_dt, trade_dt, 2)\n",
"# 获取行业分类信息\n",
"ind = sw_industry_daily(portfolio_date, portfolio_date, 1)\n",
"ind.head()"
]
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 6,
"id": "fa17cc0d-61ad-4958-bf01-7cdc1c17230d",
"metadata": {},
"outputs": [],
"source": [
"rtns = stk_return(df.SKCODE, trade_dt, \"20211231\")"
"# 获取股票的区间收益,brison分解针对指定区间进行\n",
"# 下面的例子,分解20210630到20211231区间的收益\n",
"\n",
"rtns = stk_return(trade_dt, \"20211231\")"
]
},
{
"cell_type": "markdown",
"id": "bd900e8e-9054-4182-8d7f-396a7199eb73",
"metadata": {},
"source": [
"# 2. 进行brinson归因\n",
"--------------------------\n",
"\n",
"定义见:\n",
"* [brinsion-model](https://breakingdownfinance.com/finance-topics/modern-portfolio-theory/brinson-model/)\n",
"* [单期Brinson业绩归因分析](https://zhuanlan.zhihu.com/p/33550387)\n",
"\n",
"其中:\n",
"\n",
"* **TR**: 总超额收益\n",
"* **SR**: 证券选择收益\n",
"* **AR**: 行业配置收益\n",
"* **IR**: 交互收益"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "41d6c18d-2ff7-4592-89cb-d19c4e8cc3e1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Wall time: 27 ms\n"
]
}
],
"source": [
"%%time\n",
"\n",
"res = single_brinson(portfolio, idx_comp, ind, rtns)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "7743d37c-eb35-4294-95ef-da1007e3c175",
"execution_count": 8,
"id": "b31e7832-ee3f-43c8-b931-022e2f855919",
"metadata": {},
"outputs": [
{
......@@ -253,113 +406,417 @@
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>trade_date</th>\n",
" <th>security_code</th>\n",
" <th>change_pct / 100.</th>\n",
" <th>portfolio</th>\n",
" <th>benchmark</th>\n",
" <th>p_ret</th>\n",
" <th>b_ret</th>\n",
" <th>TR</th>\n",
" <th>SR</th>\n",
" <th>AR</th>\n",
" <th>IR</th>\n",
" </tr>\n",
" <tr>\n",
" <th>industry</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000045</td>\n",
" <td>-0.009379</td>\n",
" <th>交通运输</th>\n",
" <td>0.000000</td>\n",
" <td>0.0264</td>\n",
" <td>0.000000</td>\n",
" <td>0.005835</td>\n",
" <td>-0.000154</td>\n",
" <td>-0.000154</td>\n",
" <td>-0.000154</td>\n",
" <td>0.000154</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000121</td>\n",
" <td>-0.009434</td>\n",
" <th>休闲服务</th>\n",
" <td>0.000000</td>\n",
" <td>0.0107</td>\n",
" <td>0.000000</td>\n",
" <td>-0.260787</td>\n",
" <td>0.002790</td>\n",
" <td>0.002790</td>\n",
" <td>0.002790</td>\n",
" <td>-0.002790</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000171</td>\n",
" <td>0.000694</td>\n",
" <th>传媒</th>\n",
" <td>0.000000</td>\n",
" <td>0.0107</td>\n",
" <td>0.000000</td>\n",
" <td>-0.051229</td>\n",
" <td>0.000548</td>\n",
" <td>0.000548</td>\n",
" <td>0.000548</td>\n",
" <td>-0.000548</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000377</td>\n",
" <td>-0.002692</td>\n",
" <th>公用事业</th>\n",
" <td>0.000000</td>\n",
" <td>0.0240</td>\n",
" <td>0.000000</td>\n",
" <td>0.319549</td>\n",
" <td>-0.007669</td>\n",
" <td>-0.007669</td>\n",
" <td>-0.007669</td>\n",
" <td>0.007669</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2021-06-30</td>\n",
" <td>2010000438</td>\n",
" <td>-0.002570</td>\n",
" <th>农林牧渔</th>\n",
" <td>0.000000</td>\n",
" <td>0.0163</td>\n",
" <td>0.000000</td>\n",
" <td>-0.005454</td>\n",
" <td>0.000089</td>\n",
" <td>0.000089</td>\n",
" <td>0.000089</td>\n",
" <td>-0.000089</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <th>化工</th>\n",
" <td>0.000000</td>\n",
" <td>0.0382</td>\n",
" <td>0.000000</td>\n",
" <td>-0.006631</td>\n",
" <td>0.000253</td>\n",
" <td>0.000253</td>\n",
" <td>0.000253</td>\n",
" <td>-0.000253</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14830</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010034355</td>\n",
" <td>-0.002386</td>\n",
" <th>医药生物</th>\n",
" <td>0.646329</td>\n",
" <td>0.0971</td>\n",
" <td>-0.129286</td>\n",
" <td>-0.217245</td>\n",
" <td>-0.062466</td>\n",
" <td>0.008541</td>\n",
" <td>-0.119317</td>\n",
" <td>0.048310</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14831</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010034356</td>\n",
" <td>-0.012759</td>\n",
" <th>商业贸易</th>\n",
" <td>0.000000</td>\n",
" <td>0.0021</td>\n",
" <td>0.000000</td>\n",
" <td>-0.170496</td>\n",
" <td>0.000358</td>\n",
" <td>0.000358</td>\n",
" <td>0.000358</td>\n",
" <td>-0.000358</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14763</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010034358</td>\n",
" <td>0.006309</td>\n",
" <th>国防军工</th>\n",
" <td>0.256280</td>\n",
" <td>0.0149</td>\n",
" <td>0.185243</td>\n",
" <td>0.258038</td>\n",
" <td>0.043629</td>\n",
" <td>-0.001085</td>\n",
" <td>0.062285</td>\n",
" <td>-0.017571</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14764</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010034359</td>\n",
" <td>-0.018512</td>\n",
" <th>家用电器</th>\n",
" <td>0.000000</td>\n",
" <td>0.0318</td>\n",
" <td>0.000000</td>\n",
" <td>-0.009740</td>\n",
" <td>0.000310</td>\n",
" <td>0.000310</td>\n",
" <td>0.000310</td>\n",
" <td>-0.000310</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14765</th>\n",
" <td>2021-12-31</td>\n",
" <td>2010034373</td>\n",
" <td>-0.015939</td>\n",
" <th>建筑材料</th>\n",
" <td>0.000000</td>\n",
" <td>0.0126</td>\n",
" <td>0.000000</td>\n",
" <td>0.004792</td>\n",
" <td>-0.000060</td>\n",
" <td>-0.000060</td>\n",
" <td>-0.000060</td>\n",
" <td>0.000060</td>\n",
" </tr>\n",
" <tr>\n",
" <th>建筑装饰</th>\n",
" <td>0.000000</td>\n",
" <td>0.0160</td>\n",
" <td>0.000000</td>\n",
" <td>0.342439</td>\n",
" <td>-0.005479</td>\n",
" <td>-0.005479</td>\n",
" <td>-0.005479</td>\n",
" <td>0.005479</td>\n",
" </tr>\n",
" <tr>\n",
" <th>房地产</th>\n",
" <td>0.000000</td>\n",
" <td>0.0183</td>\n",
" <td>0.000000</td>\n",
" <td>0.090156</td>\n",
" <td>-0.001650</td>\n",
" <td>-0.001650</td>\n",
" <td>-0.001650</td>\n",
" <td>0.001650</td>\n",
" </tr>\n",
" <tr>\n",
" <th>有色金属</th>\n",
" <td>0.097392</td>\n",
" <td>0.0345</td>\n",
" <td>0.387793</td>\n",
" <td>0.344770</td>\n",
" <td>0.025873</td>\n",
" <td>0.001484</td>\n",
" <td>0.021683</td>\n",
" <td>0.002706</td>\n",
" </tr>\n",
" <tr>\n",
" <th>机械设备</th>\n",
" <td>0.000000</td>\n",
" <td>0.0236</td>\n",
" <td>0.000000</td>\n",
" <td>-0.063295</td>\n",
" <td>0.001494</td>\n",
" <td>0.001494</td>\n",
" <td>0.001494</td>\n",
" <td>-0.001494</td>\n",
" </tr>\n",
" <tr>\n",
" <th>汽车</th>\n",
" <td>0.000000</td>\n",
" <td>0.0326</td>\n",
" <td>0.000000</td>\n",
" <td>0.012605</td>\n",
" <td>-0.000411</td>\n",
" <td>-0.000411</td>\n",
" <td>-0.000411</td>\n",
" <td>0.000411</td>\n",
" </tr>\n",
" <tr>\n",
" <th>电子</th>\n",
" <td>0.000000</td>\n",
" <td>0.0971</td>\n",
" <td>0.000000</td>\n",
" <td>0.014911</td>\n",
" <td>-0.001448</td>\n",
" <td>-0.001448</td>\n",
" <td>-0.001448</td>\n",
" <td>0.001448</td>\n",
" </tr>\n",
" <tr>\n",
" <th>电气设备</th>\n",
" <td>0.000000</td>\n",
" <td>0.1042</td>\n",
" <td>0.000000</td>\n",
" <td>0.237483</td>\n",
" <td>-0.024746</td>\n",
" <td>-0.024746</td>\n",
" <td>-0.024746</td>\n",
" <td>0.024746</td>\n",
" </tr>\n",
" <tr>\n",
" <th>纺织服装</th>\n",
" <td>0.000000</td>\n",
" <td>0.0003</td>\n",
" <td>0.000000</td>\n",
" <td>-0.341612</td>\n",
" <td>0.000102</td>\n",
" <td>0.000102</td>\n",
" <td>0.000102</td>\n",
" <td>-0.000102</td>\n",
" </tr>\n",
" <tr>\n",
" <th>综合</th>\n",
" <td>0.000000</td>\n",
" <td>0.0000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>计算机</th>\n",
" <td>0.000000</td>\n",
" <td>0.0259</td>\n",
" <td>0.000000</td>\n",
" <td>-0.016668</td>\n",
" <td>0.000432</td>\n",
" <td>0.000432</td>\n",
" <td>0.000432</td>\n",
" <td>-0.000432</td>\n",
" </tr>\n",
" <tr>\n",
" <th>轻工制造</th>\n",
" <td>0.000000</td>\n",
" <td>0.0029</td>\n",
" <td>0.000000</td>\n",
" <td>-0.116237</td>\n",
" <td>0.000337</td>\n",
" <td>0.000337</td>\n",
" <td>0.000337</td>\n",
" <td>-0.000337</td>\n",
" </tr>\n",
" <tr>\n",
" <th>通信</th>\n",
" <td>0.000000</td>\n",
" <td>0.0103</td>\n",
" <td>0.000000</td>\n",
" <td>-0.016761</td>\n",
" <td>0.000173</td>\n",
" <td>0.000173</td>\n",
" <td>0.000173</td>\n",
" <td>-0.000173</td>\n",
" </tr>\n",
" <tr>\n",
" <th>采掘</th>\n",
" <td>0.000000</td>\n",
" <td>0.0085</td>\n",
" <td>0.000000</td>\n",
" <td>0.118131</td>\n",
" <td>-0.001004</td>\n",
" <td>-0.001004</td>\n",
" <td>-0.001004</td>\n",
" <td>0.001004</td>\n",
" </tr>\n",
" <tr>\n",
" <th>钢铁</th>\n",
" <td>0.000000</td>\n",
" <td>0.0067</td>\n",
" <td>0.000000</td>\n",
" <td>0.335245</td>\n",
" <td>-0.002246</td>\n",
" <td>-0.002246</td>\n",
" <td>-0.002246</td>\n",
" <td>0.002246</td>\n",
" </tr>\n",
" <tr>\n",
" <th>银行</th>\n",
" <td>0.000000</td>\n",
" <td>0.1077</td>\n",
" <td>0.000000</td>\n",
" <td>-0.066267</td>\n",
" <td>0.007137</td>\n",
" <td>0.007137</td>\n",
" <td>0.007137</td>\n",
" <td>-0.007137</td>\n",
" </tr>\n",
" <tr>\n",
" <th>非银金融</th>\n",
" <td>0.000000</td>\n",
" <td>0.1017</td>\n",
" <td>0.000000</td>\n",
" <td>0.019102</td>\n",
" <td>-0.001943</td>\n",
" <td>-0.001943</td>\n",
" <td>-0.001943</td>\n",
" <td>0.001943</td>\n",
" </tr>\n",
" <tr>\n",
" <th>食品饮料</th>\n",
" <td>0.000000</td>\n",
" <td>0.1251</td>\n",
" <td>0.000000</td>\n",
" <td>-0.050269</td>\n",
" <td>0.006289</td>\n",
" <td>0.006289</td>\n",
" <td>0.006289</td>\n",
" <td>-0.006289</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>14832 rows × 3 columns</p>\n",
"</div>"
],
"text/plain": [
" trade_date security_code change_pct / 100.\n",
"0 2021-06-30 2010000045 -0.009379\n",
"1 2021-06-30 2010000121 -0.009434\n",
"2 2021-06-30 2010000171 0.000694\n",
"3 2021-06-30 2010000377 -0.002692\n",
"4 2021-06-30 2010000438 -0.002570\n",
"... ... ... ...\n",
"14830 2021-12-31 2010034355 -0.002386\n",
"14831 2021-12-31 2010034356 -0.012759\n",
"14763 2021-12-31 2010034358 0.006309\n",
"14764 2021-12-31 2010034359 -0.018512\n",
"14765 2021-12-31 2010034373 -0.015939\n",
" portfolio benchmark p_ret b_ret TR SR \\\n",
"industry \n",
"交通运输 0.000000 0.0264 0.000000 0.005835 -0.000154 -0.000154 \n",
"休闲服务 0.000000 0.0107 0.000000 -0.260787 0.002790 0.002790 \n",
"传媒 0.000000 0.0107 0.000000 -0.051229 0.000548 0.000548 \n",
"公用事业 0.000000 0.0240 0.000000 0.319549 -0.007669 -0.007669 \n",
"农林牧渔 0.000000 0.0163 0.000000 -0.005454 0.000089 0.000089 \n",
"化工 0.000000 0.0382 0.000000 -0.006631 0.000253 0.000253 \n",
"医药生物 0.646329 0.0971 -0.129286 -0.217245 -0.062466 0.008541 \n",
"商业贸易 0.000000 0.0021 0.000000 -0.170496 0.000358 0.000358 \n",
"国防军工 0.256280 0.0149 0.185243 0.258038 0.043629 -0.001085 \n",
"家用电器 0.000000 0.0318 0.000000 -0.009740 0.000310 0.000310 \n",
"建筑材料 0.000000 0.0126 0.000000 0.004792 -0.000060 -0.000060 \n",
"建筑装饰 0.000000 0.0160 0.000000 0.342439 -0.005479 -0.005479 \n",
"房地产 0.000000 0.0183 0.000000 0.090156 -0.001650 -0.001650 \n",
"有色金属 0.097392 0.0345 0.387793 0.344770 0.025873 0.001484 \n",
"机械设备 0.000000 0.0236 0.000000 -0.063295 0.001494 0.001494 \n",
"汽车 0.000000 0.0326 0.000000 0.012605 -0.000411 -0.000411 \n",
"电子 0.000000 0.0971 0.000000 0.014911 -0.001448 -0.001448 \n",
"电气设备 0.000000 0.1042 0.000000 0.237483 -0.024746 -0.024746 \n",
"纺织服装 0.000000 0.0003 0.000000 -0.341612 0.000102 0.000102 \n",
"综合 0.000000 0.0000 0.000000 0.000000 0.000000 0.000000 \n",
"计算机 0.000000 0.0259 0.000000 -0.016668 0.000432 0.000432 \n",
"轻工制造 0.000000 0.0029 0.000000 -0.116237 0.000337 0.000337 \n",
"通信 0.000000 0.0103 0.000000 -0.016761 0.000173 0.000173 \n",
"采掘 0.000000 0.0085 0.000000 0.118131 -0.001004 -0.001004 \n",
"钢铁 0.000000 0.0067 0.000000 0.335245 -0.002246 -0.002246 \n",
"银行 0.000000 0.1077 0.000000 -0.066267 0.007137 0.007137 \n",
"非银金融 0.000000 0.1017 0.000000 0.019102 -0.001943 -0.001943 \n",
"食品饮料 0.000000 0.1251 0.000000 -0.050269 0.006289 0.006289 \n",
"\n",
"[14832 rows x 3 columns]"
" AR IR \n",
"industry \n",
"交通运输 -0.000154 0.000154 \n",
"休闲服务 0.002790 -0.002790 \n",
"传媒 0.000548 -0.000548 \n",
"公用事业 -0.007669 0.007669 \n",
"农林牧渔 0.000089 -0.000089 \n",
"化工 0.000253 -0.000253 \n",
"医药生物 -0.119317 0.048310 \n",
"商业贸易 0.000358 -0.000358 \n",
"国防军工 0.062285 -0.017571 \n",
"家用电器 0.000310 -0.000310 \n",
"建筑材料 -0.000060 0.000060 \n",
"建筑装饰 -0.005479 0.005479 \n",
"房地产 -0.001650 0.001650 \n",
"有色金属 0.021683 0.002706 \n",
"机械设备 0.001494 -0.001494 \n",
"汽车 -0.000411 0.000411 \n",
"电子 -0.001448 0.001448 \n",
"电气设备 -0.024746 0.024746 \n",
"纺织服装 0.000102 -0.000102 \n",
"综合 0.000000 0.000000 \n",
"计算机 0.000432 -0.000432 \n",
"轻工制造 0.000337 -0.000337 \n",
"通信 0.000173 -0.000173 \n",
"采掘 -0.001004 0.001004 \n",
"钢铁 -0.002246 0.002246 \n",
"银行 0.007137 -0.007137 \n",
"非银金融 -0.001943 0.001943 \n",
"食品饮料 0.006289 -0.006289 "
]
},
"execution_count": 14,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"rtns"
"res"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "41d6c18d-2ff7-4592-89cb-d19c4e8cc3e1",
"id": "e97cd6d4-f302-4e61-95a1-1e6580aebfc5",
"metadata": {},
"outputs": [],
"source": []
......
import pandas as pd
def single_brinson(portfolio, index_component, industry, stk_rtns):
total_df = pd.merge(industry[["security_code", "industry_name"]], portfolio[["security_code", "weight"]],
on=["security_code"], how="left").fillna(0.0)
total_df = pd.merge(total_df, index_component[["security_code", "weight"]], on=["security_code"], how="left").fillna(0.0)
total_df = pd.merge(total_df, stk_rtns[["security_code", "chg_pct"]], on=["security_code"], how="inner")
total_df.columns = ["security_code", "industry", "portfolio", "benchmark", "chg_pct"]
total_df["p_ret"] = total_df["portfolio"] * total_df["chg_pct"]
total_df["b_ret"] = total_df["benchmark"] * total_df["chg_pct"]
total_df = total_df.groupby("industry")[["portfolio", "benchmark", "p_ret", "b_ret"]].agg("sum")
total_df["p_ret"] = total_df["p_ret"] / (total_df["portfolio"] + 1e-16)
total_df["b_ret"] = total_df["b_ret"] / (total_df["benchmark"] + 1e-16)
total_df["TR"] = total_df["p_ret"] * total_df["portfolio"] - total_df["b_ret"] * total_df["benchmark"]
total_df["SR"] = (total_df["p_ret"] - total_df["b_ret"]) * total_df["benchmark"]
total_df["AR"] = total_df["b_ret"] * (total_df["portfolio"] - total_df["benchmark"])
total_df["IR"] = (total_df["p_ret"] - total_df["b_ret"]) * (total_df["portfolio"] - total_df["benchmark"])
return total_df
......@@ -181,7 +181,7 @@ def fd_hshkiport(security_ids, report_dates_begin, report_dates_end=None):
INDCLASSCODE = '2102' AND
SECODE in ({sec_id_strs})
"""
return read_sql(query).sort_values("SECURITYID")
return read_sql(query).sort_values("SECURITYID").reset_index(drop=True)
def fd_assetportfolio(security_ids, report_dates_begin, report_dates_end=None):
......@@ -207,7 +207,7 @@ def fd_assetportfolio(security_ids, report_dates_begin, report_dates_end=None):
ISVALID = 1 AND
SECODE in ({sec_id_strs})
"""
return read_sql(query).sort_values("SECURITYID")
return read_sql(query).sort_values("SECURITYID").reset_index(drop=True)
def fd_qtfdnav(security_ids, trade_dt):
......@@ -223,50 +223,63 @@ def fd_qtfdnav(security_ids, trade_dt):
ISVALID = 1 AND
SECODE in ({sec_id_strs})
"""
return read_sql(query).sort_values("SECURITYID")
return read_sql(query).sort_values("SECURITYID").reset_index(drop=True)
def fd_skdetail(security_ids, trade_dt):
def _to_yyyy_mm_dd(trade_dt):
if len(trade_dt) == 10:
return trade_dt
else:
return f"{trade_dt[0:4]}-{trade_dt[4:6]}-{trade_dt[6:8]}"
def _security_ids(security_ids):
if isinstance(security_ids, str):
sec_id_strs = security_ids
elif security_ids is None:
sec_id_strs = None
else:
sec_id_strs = ",".join(["'" + s + "'" for s in security_ids])
return sec_id_strs
def fd_skdetail(security_ids, trade_dt):
p_id_strs = _security_ids(security_ids)
query = f"""
SELECT SECODE as SECURITYID, SKCODE, SKNAME, HOLDMKTCAP, HOLDAMT, NAVRTO FROM TQ_FD_SKDETAIL
SELECT SECODE as pid, SKCODE as security_code, SKNAME, HOLDMKTCAP, HOLDAMT FROM TQ_FD_SKDETAIL
WHERE
ENDDATE = '{trade_dt}' AND
ISVALID = 1 AND
SECODE in ({sec_id_strs})
SECODE in ({p_id_strs})
"""
df = read_sql(query).sort_values("SECURITYID")
df = read_sql(query).sort_values("security_code")
total_value = df.HOLDMKTCAP.sum()
df["pct"] = df.HOLDMKTCAP / total_value
return df
df["weight"] = df.HOLDMKTCAP / total_value
return df.reset_index(drop=True)
def _to_yyyy_mm_dd(trade_dt):
if len(trade_dt) == 10:
return trade_dt
else:
return f"{trade_dt[0:4]}-{trade_dt[4:6]}-{trade_dt[6:8]}"
def risk_exposure(start_date, end_date, security_ids=None):
sec_id_strs = _security_ids(security_ids)
def risk_exposure(security_ids, start_date, end_date):
if isinstance(security_ids, str):
sec_id_strs = security_ids
if sec_id_strs:
query = f"""
SELECT trade_date, security_code, {','.join(RISK_STYLES)}, {','.join(INDUSTRY_STYLES)} FROM risk_exposure
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1 AND
security_code IN ({sec_id_strs})
"""
else:
sec_id_strs = ",".join(["'" + s + "'" for s in security_ids])
query = f"""
SELECT trade_date, security_code, {','.join(RISK_STYLES)}, {','.join(INDUSTRY_STYLES)} FROM risk_exposure
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1 AND
security_code IN ({sec_id_strs})
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"])
query = f"""
SELECT trade_date, security_code, {','.join(RISK_STYLES)}, {','.join(INDUSTRY_STYLES)} FROM risk_exposure
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"]).reset_index(drop=True)
def risk_return(start_date, end_date):
......@@ -277,24 +290,30 @@ def risk_return(start_date, end_date):
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1
"""
return read_sql(query, source="mysql").sort_values(["trade_date"])
return read_sql(query, source="mysql").sort_values(["trade_date"]).reset_index(drop=True)
def specific_return(security_ids, start_date, end_date):
if isinstance(security_ids, str):
sec_id_strs = security_ids
else:
sec_id_strs = ",".join(["'" + s + "'" for s in security_ids])
def specific_return(start_date, end_date, security_ids=None):
sec_id_strs = _security_ids(security_ids)
query = f"""
SELECT trade_date, security_code, secShortName, symbol, spret FROM specific_return
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1 AND
security_code IN ({sec_id_strs})
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"])
if sec_id_strs:
query = f"""
SELECT trade_date, security_code, secShortName, symbol, spret FROM specific_return
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1 AND
security_code IN ({sec_id_strs})
"""
else:
query = f"""
SELECT trade_date, security_code, secShortName, symbol, spret FROM specific_return
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"]).reset_index(drop=True)
def risk_cov(start_date, end_date, model="short"):
......@@ -305,29 +324,35 @@ def risk_cov(start_date, end_date, model="short"):
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "FactorID"])
return read_sql(query, source="mysql").sort_values(["trade_date", "FactorID"]).reset_index(drop=True)
def special_risk(security_ids, start_date, end_date, model="short"):
if isinstance(security_ids, str):
sec_id_strs = security_ids
else:
sec_id_strs = ",".join(["'" + s + "'" for s in security_ids])
def special_risk(start_date, end_date, model="short", security_ids=None):
sec_id_strs = _security_ids(security_ids)
query = f"""
SELECT trade_date, security_code, secShortName, symbol, SRISK FROM specific_risk_{model}
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1 AND
security_code IN ({sec_id_strs})
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"])
if sec_id_strs:
query = f"""
SELECT trade_date, security_code, secShortName, symbol, SRISK FROM specific_risk_{model}
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1 AND
security_code IN ({sec_id_strs})
"""
else:
query = f"""
SELECT trade_date, security_code, secShortName, symbol, SRISK FROM specific_risk_{model}
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"]).reset_index(drop=True)
def index_component(index_code, start_date, end_date):
query = f"""
SELECT index_component.trade_date, index_component.security_code, `index`.sname, `index`.weighing FROM `index`
SELECT index_component.trade_date, index_component.security_code, `index`.sname, `index`.weighing / 100.0 AS weight FROM `index`
JOIN index_component ON
`index`.trade_date = index_component.trade_date AND
`index`.isymbol = index_component.isymbol AND
......@@ -339,38 +364,50 @@ def index_component(index_code, start_date, end_date):
index_component.flag = 1 AND
index_component.isecurity_code = '{index_code}';
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"])
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"]).reset_index(drop=True)
def sw_industry_daily(security_ids, start_date, end_date, level=1):
if isinstance(security_ids, str):
sec_id_strs = security_ids
else:
sec_id_strs = ",".join(["'" + s + "'" for s in security_ids])
query = f"""
SELECT trade_date, security_code, industry_code{level} as industry_code, industry_name{level} as industry_name FROM sw_industry_daily
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1 AND
security_code IN ({sec_id_strs})
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"])
def stk_return(security_ids, start_date, end_date):
if isinstance(security_ids, str):
sec_id_strs = security_ids
def sw_industry_daily(start_date, end_date, level=1, security_ids=None):
sec_id_strs = _security_ids(security_ids)
if sec_id_strs:
query = f"""
SELECT trade_date, security_code, industry_code{level} as industry_code, industry_name{level} as industry_name FROM sw_industry_daily
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1 AND
security_code IN ({sec_id_strs})
"""
else:
sec_id_strs = ",".join(["'" + s + "'" for s in security_ids])
query = f"""
SELECT trade_date, security_code, change_pct / 100. FROM stk_daily_price_pro
WHERE
query = f"""
SELECT trade_date, security_code, industry_code{level} as industry_code, industry_name{level} as industry_name FROM sw_industry_daily
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
security_code IN ({sec_id_strs}) AND
flag = 1
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"])
"""
return read_sql(query, source="mysql").sort_values(["trade_date", "security_code"]).reset_index(drop=True)
def stk_return(start_date, end_date, security_ids=None):
sec_id_strs = _security_ids(security_ids)
if sec_id_strs:
query = f"""
SELECT security_code, min(trade_date) as start_date, max(trade_date) as end_date, exp(sum(log(1.0 + change_pct / 100.))) AS chg_pct FROM stk_daily_price_pro
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
security_code IN ({sec_id_strs}) AND
flag = 1
GROUP BY security_code;
"""
else:
query = f"""
SELECT security_code, min(trade_date) as start_date, max(trade_date) as end_date, exp(sum(log(1.0 + change_pct / 100.))) - 1.0 AS chg_pct FROM stk_daily_price_pro
WHERE
trade_date >= '{_to_yyyy_mm_dd(start_date)}' AND
trade_date <= '{_to_yyyy_mm_dd(end_date)}' AND
flag = 1
GROUP BY security_code;
"""
return read_sql(query, source="mysql").sort_values("security_code").reset_index(drop=True)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment