Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
A
alpha-mind
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dr.李
alpha-mind
Commits
7f11b195
Commit
7f11b195
authored
Oct 17, 2017
by
Dr.李
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added notebook example
parent
8a214aec
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
690 additions
and
0 deletions
+690
-0
target_vol_executor.ipynb
notebooks/target_vol_executor.ipynb
+690
-0
No files found.
notebooks/target_vol_executor.ipynb
0 → 100644
View file @
7f11b195
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import pandas as pd\n",
"from alphamind.api import *\n",
"from PyFin.api import *\n",
"from matplotlib import pyplot as plt\n",
"plt.style.use('fivethirtyeight')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Parameter Setting\n",
"----------------------"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def _map_freq(freq):\n",
"\n",
" if freq == '1m':\n",
" horizon = 21\n",
" elif freq == '1w':\n",
" horizon = 4\n",
" elif freq == '2w':\n",
" horizon = 8\n",
" elif freq == '3w':\n",
" horizon = 12\n",
" elif freq == '1d':\n",
" horizon = 0\n",
" else:\n",
" raise ValueError(\"Unrecognized freq: {0}\".format(freq))\n",
" return horizon"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"factors = ['VAL', 'ROEDiluted', 'GREV', 'EPS', 'CHV', 'CFinc1', 'BDTO', 'IVR']\n",
"factor_weights = np.array([0.034129344,\n",
" 0.048765746,\n",
" 0.042747382,\n",
" -0.015900173,\n",
" 0.019044573,\n",
" -0.001792638,\n",
" 0.014277867,\n",
" 0.04])\n",
"\n",
"engine = SqlEngine(\"postgres+psycopg2://postgres:A12345678!@10.63.6.220/alpha\")\n",
"universe = Universe('custom', ['zz500'])\n",
"benchmark_code = 905\n",
"neutralize_risk = ['SIZE'] + industry_styles\n",
"constraint_risk = ['SIZE'] + industry_styles\n",
"start_date = '2012-01-01'\n",
"end_date = '2017-10-13'\n",
"industry_lower = 0.75\n",
"industry_upper = 1.25\n",
"\n",
"freq = '1w'\n",
"horizon = _map_freq(freq)\n",
"dates = makeSchedule(start_date, end_date, tenor=freq, calendar='china.sse', dateGenerationRule=DateGeneration.Backward)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"all_data = engine.fetch_data_range(universe, factors, dates=dates, benchmark=905)\n",
"factor_all_data = all_data['factor']\n",
"factor_groups = factor_all_data.groupby('trade_date')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Naive Executor Strategy\n",
"---------------------------------"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"rets = []\n",
"turn_overs = []\n",
"executor = NaiveExecutor()\n",
"leverags = []\n",
"\n",
"for i, value in enumerate(factor_groups):\n",
" date = value[0]\n",
" data = value[1]\n",
" codes = data.code.tolist()\n",
" ref_date = date.strftime('%Y-%m-%d')\n",
" total_data = data.dropna()\n",
" dx_return = None\n",
" risk_exp = total_data[neutralize_risk].values.astype(float)\n",
" industry = total_data.industry.values\n",
" benchmark_w = total_data.weight.values\n",
" \n",
" constraint_exp = total_data[constraint_risk].values\n",
" risk_exp_expand = np.concatenate((constraint_exp, np.ones((len(risk_exp), 1))), axis=1).astype(float)\n",
" risk_names = constraint_risk + ['total']\n",
" risk_target = risk_exp_expand.T @ benchmark_w\n",
" lbound = np.zeros(len(total_data))\n",
" ubound = 0.01 + benchmark_w\n",
"\n",
" constraint = Constraints(risk_exp_expand, risk_names)\n",
" for i, name in enumerate(risk_names):\n",
" if name == 'total' or name == 'SIZE':\n",
" constraint.set_constraints(name, lower_bound=risk_target[i], upper_bound=risk_target[i])\n",
" else:\n",
" constraint.set_constraints(name, lower_bound=risk_target[i]*industry_lower, upper_bound=risk_target[i]*industry_upper)\n",
" \n",
" er = factor_processing(total_data[factors].values,\n",
" pre_process=[winsorize_normal, standardize],\n",
" post_process=[standardize]) @ factor_weights\n",
" \n",
" target_pos, _ = er_portfolio_analysis(er,\n",
" industry,\n",
" dx_return,\n",
" constraint,\n",
" False,\n",
" benchmark_w)\n",
" target_pos['code'] = total_data['code'].values\n",
" \n",
" turn_over, executed_pos = executor.execute(target_pos=target_pos)\n",
" \n",
" executed_codes = executed_pos.code.tolist()\n",
" dx_retuns = engine.fetch_dx_return(date, executed_codes, horizon=horizon)\n",
" \n",
" result = pd.merge(executed_pos, total_data, on=['code'], how='inner')\n",
" result = pd.merge(result, dx_retuns, on=['code'])\n",
" \n",
" leverage = result.weight_x.abs().sum()\n",
" \n",
" ret = (result.weight_x - result.weight_y * leverage / result.weight_y.sum()).values @ result.dx.values\n",
" rets.append(ret)\n",
" executor.set_current(executed_pos)\n",
" turn_overs.append(turn_over)\n",
" leverags.append(leverage)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"ret_df1 = pd.DataFrame({'returns': rets, 'turn_over': turn_overs, 'leverage': leverage}, index=dates)\n",
"ret_df1.loc[advanceDateByCalendar('china.sse', dates[-1], freq)] = 0.\n",
"ret_df1 = ret_df1.shift(1)\n",
"ret_df1.iloc[0] = 0.\n",
"ret_df1['tc_cost'] = ret_df1.turn_over * 0.002"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_df1[['returns', 'tc_cost']].cumsum().plot(figsize=(12, 6), title='Fixed frequency rebalanced: {0}'.format(freq), secondary_y='tc_cost')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_atfer_tc = ret_df1.returns - ret_df1.tc_cost\n",
"print(\"sharp: \", ret_atfer_tc.mean() / ret_atfer_tc.std() * np.sqrt(52))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_df1[['returns', 'leverage']].rolling(window=60).std().plot(figsize=(12, 6), title='rolling std', secondary_y='leverage')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Threshold Turn Over + Strategy\n",
"------------------------------------"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"freq = '1d'\n",
"horizon = _map_freq(freq)\n",
"dates = makeSchedule(start_date, end_date, tenor=freq, calendar='china.sse', dateGenerationRule=DateGeneration.Backward)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"all_data = engine.fetch_data_range(universe, factors, dates=dates, benchmark=905)\n",
"factor_all_data = all_data['factor']\n",
"factor_groups = factor_all_data.groupby('trade_date')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"rets = []\n",
"turn_overs = []\n",
"turn_over_threshold = 0.90\n",
"executor = ThresholdExecutor(turn_over_threshold=turn_over_threshold)\n",
"execution_pipeline = ExecutionPipeline(executors=[executor])\n",
"leverags = []\n",
"\n",
"for i, value in enumerate(factor_groups):\n",
" date = value[0]\n",
" data = value[1]\n",
" codes = data.code.tolist()\n",
" ref_date = date.strftime('%Y-%m-%d')\n",
" total_data = data.dropna()\n",
" dx_return = None\n",
" risk_exp = total_data[neutralize_risk].values.astype(float)\n",
" industry = total_data.industry.values\n",
" benchmark_w = total_data.weight.values\n",
" \n",
" constraint_exp = total_data[constraint_risk].values\n",
" risk_exp_expand = np.concatenate((constraint_exp, np.ones((len(risk_exp), 1))), axis=1).astype(float)\n",
" risk_names = constraint_risk + ['total']\n",
" risk_target = risk_exp_expand.T @ benchmark_w\n",
" lbound = np.zeros(len(total_data))\n",
" ubound = 0.01 + benchmark_w\n",
"\n",
" constraint = Constraints(risk_exp_expand, risk_names)\n",
" for i, name in enumerate(risk_names):\n",
" if name == 'total' or name == 'SIZE':\n",
" constraint.set_constraints(name, lower_bound=risk_target[i], upper_bound=risk_target[i])\n",
" else:\n",
" constraint.set_constraints(name, lower_bound=risk_target[i]*industry_lower, upper_bound=risk_target[i]*industry_upper)\n",
" \n",
" er = factor_processing(total_data[factors].values,\n",
" pre_process=[winsorize_normal, standardize],\n",
" post_process=[standardize]) @ factor_weights\n",
" \n",
" target_pos, _ = er_portfolio_analysis(er,\n",
" industry,\n",
" dx_return,\n",
" constraint,\n",
" False,\n",
" benchmark_w)\n",
" target_pos['code'] = total_data['code'].values\n",
" \n",
" turn_over, executed_pos = execution_pipeline.execute(target_pos=target_pos)\n",
" \n",
" executed_codes = executed_pos.code.tolist()\n",
" dx_retuns = engine.fetch_dx_return(date, executed_codes, horizon=horizon)\n",
" \n",
" result = pd.merge(executed_pos, total_data, on=['code'], how='inner')\n",
" result = pd.merge(result, dx_retuns, on=['code'])\n",
" leverage = result.weight_x.abs().sum()\n",
" \n",
" ret = (result.weight_x - result.weight_y * leverage / result.weight_y.sum()).values @ result.dx.values\n",
" rets.append(ret)\n",
" leverags.append(executed_pos.weight.abs().sum())\n",
" turn_overs.append(turn_over)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"ret_df2 = pd.DataFrame({'returns': rets, 'turn_over': turn_overs, 'leverage': leverags}, index=dates)\n",
"ret_df2.loc[advanceDateByCalendar('china.sse', dates[-1], freq)] = 0.\n",
"ret_df2 = ret_df2.shift(1)\n",
"ret_df2.iloc[0] = 0.\n",
"ret_df2['tc_cost'] = ret_df2.turn_over * 0.002"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_df2[['returns', 'tc_cost']].cumsum().plot(figsize=(12, 6),\n",
" title='Threshold tc rebalanced: Monitored freq {0}, {1} tc'.format(freq,\n",
" turn_over_threshold),\n",
" secondary_y='tc_cost')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_atfer_tc = ret_df2.returns - ret_df2.tc_cost\n",
"print(\"sharp: \", ret_atfer_tc.mean() / ret_atfer_tc.std() * np.sqrt(252))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_df2[['returns', 'leverage']].rolling(window=60).std().plot(figsize=(12, 6), title='rolling std', secondary_y='leverage')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Target Vol + Threshold Turn Over + Strategy\n",
"------------------------"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"rets = []\n",
"turn_overs = []\n",
"target_vol = 0.002\n",
"turn_over_threshold = 0.70\n",
"window = 30\n",
"executor1 = TargetVolExecutor(window=window, target_vol=target_vol)\n",
"executor2 = ThresholdExecutor(turn_over_threshold=turn_over_threshold, is_relative=False)\n",
"execution_pipeline = ExecutionPipeline(executors=[executor1, executor2])\n",
"leverags = []\n",
"\n",
"for i, value in enumerate(factor_groups):\n",
" date = value[0]\n",
" data = value[1]\n",
" codes = data.code.tolist()\n",
" ref_date = date.strftime('%Y-%m-%d')\n",
" total_data = data.dropna()\n",
" dx_return = None\n",
" risk_exp = total_data[neutralize_risk].values.astype(float)\n",
" industry = total_data.industry.values\n",
" benchmark_w = total_data.weight.values\n",
" \n",
" constraint_exp = total_data[constraint_risk].values\n",
" risk_exp_expand = np.concatenate((constraint_exp, np.ones((len(risk_exp), 1))), axis=1).astype(float)\n",
" risk_names = constraint_risk + ['total']\n",
" risk_target = risk_exp_expand.T @ benchmark_w\n",
" lbound = np.zeros(len(total_data))\n",
" ubound = 0.01 + benchmark_w\n",
"\n",
" constraint = Constraints(risk_exp_expand, risk_names)\n",
" for i, name in enumerate(risk_names):\n",
" if name == 'total' or name == 'SIZE':\n",
" constraint.set_constraints(name, lower_bound=risk_target[i], upper_bound=risk_target[i])\n",
" else:\n",
" constraint.set_constraints(name, lower_bound=risk_target[i]*industry_lower, upper_bound=risk_target[i]*industry_upper)\n",
" \n",
" er = factor_processing(total_data[factors].values,\n",
" pre_process=[winsorize_normal, standardize],\n",
" post_process=[standardize]) @ factor_weights\n",
" \n",
" target_pos, _ = er_portfolio_analysis(er,\n",
" industry,\n",
" dx_return,\n",
" constraint,\n",
" False,\n",
" benchmark_w)\n",
" target_pos['code'] = total_data['code'].values\n",
" \n",
" turn_over, executed_pos = execution_pipeline.execute(target_pos=target_pos)\n",
" \n",
" executed_codes = executed_pos.code.tolist()\n",
" dx_retuns = engine.fetch_dx_return(date, executed_codes, horizon=horizon)\n",
" \n",
" result = pd.merge(executed_pos, total_data, on=['code'], how='inner')\n",
" result = pd.merge(result, dx_retuns, on=['code'])\n",
" \n",
" leverage = result.weight_x.abs().sum()\n",
" \n",
" ret = (result.weight_x - result.weight_y * leverage / result.weight_y.sum()).values @ result.dx.values\n",
" rets.append(ret)\n",
" execution_pipeline.update({'return': ret})\n",
" turn_overs.append(turn_over)\n",
" leverags.append(executed_pos.weight.abs().sum())\n",
" print(date, leverage, execution_pipeline.executors[0].m_vol.result())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"ret_df3 = pd.DataFrame({'returns': rets, 'turn_over': turn_overs, 'leverage': leverags}, index=dates)\n",
"ret_df3.loc[advanceDateByCalendar('china.sse', dates[-1], freq)] = 0.\n",
"ret_df3 = ret_df3.shift(1)\n",
"ret_df3.iloc[0] = 0.\n",
"ret_df3['tc_cost'] = ret_df3.turn_over * 0.002"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_df3[['returns', 'tc_cost']].cumsum().plot(figsize=(12, 6),\n",
" title='Threshold tc + Target vol rebalanced: Monitored freq {0}, {1} tc, {2} vol target'.format(freq,\n",
" turn_over_threshold,\n",
" target_vol),\n",
" secondary_y='tc_cost')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_df3[['returns', 'leverage']].rolling(window=60).std().plot(figsize=(12, 6), title='rolling std', secondary_y='leverage')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_atfer_tc = ret_df3.returns - ret_df3.tc_cost\n",
"print(\"sharp: \", ret_atfer_tc.mean() / ret_atfer_tc.std() * np.sqrt(252))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_df3.tail()"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Target Turn Over + Strategy\n",
"------------------------"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"rets = []\n",
"turn_overs = []\n",
"turn_over_target_base = 0.04\n",
"executor = NaiveExecutor()\n",
"execution_pipeline = ExecutionPipeline(executors=[executor])\n",
"leverags = []\n",
"previous_pos = pd.DataFrame()\n",
"\n",
"for i, value in enumerate(factor_groups):\n",
" date = value[0]\n",
" data = value[1]\n",
" codes = data.code.tolist()\n",
" ref_date = date.strftime('%Y-%m-%d')\n",
" total_data = data.dropna()\n",
" dx_return = None\n",
" risk_exp = total_data[neutralize_risk].values.astype(float)\n",
" industry = total_data.industry.values\n",
" benchmark_w = total_data.weight.values\n",
" \n",
" constraint_exp = total_data[constraint_risk].values\n",
" risk_exp_expand = np.concatenate((constraint_exp, np.ones((len(risk_exp), 1))), axis=1).astype(float)\n",
" risk_names = constraint_risk + ['total']\n",
" risk_target = risk_exp_expand.T @ benchmark_w\n",
" lbound = np.zeros(len(total_data))\n",
" ubound = 0.01 + benchmark_w\n",
"\n",
" constraint = Constraints(risk_exp_expand, risk_names)\n",
" for i, name in enumerate(risk_names):\n",
" if name == 'total' or name == 'SIZE':\n",
" constraint.set_constraints(name, lower_bound=risk_target[i], upper_bound=risk_target[i])\n",
" else:\n",
" constraint.set_constraints(name, lower_bound=risk_target[i]*industry_lower, upper_bound=risk_target[i]*industry_upper)\n",
" \n",
" er = factor_processing(total_data[factors].values,\n",
" pre_process=[winsorize_normal, standardize],\n",
" post_process=[standardize]) @ factor_weights\n",
" \n",
" codes = total_data['code'].values\n",
" \n",
" \n",
" if previous_pos.empty:\n",
" current_position = None\n",
" turn_over_target = None\n",
" else:\n",
" previous_pos.set_index('code', inplace=True)\n",
" remained_pos = previous_pos.loc[codes]\n",
" \n",
" remained_pos.fillna(0., inplace=True)\n",
" turn_over_target = turn_over_target_base\n",
" \n",
" current_position = remained_pos.weight.values\n",
" \n",
" try:\n",
" target_pos, _ = er_portfolio_analysis(er,\n",
" industry,\n",
" dx_return,\n",
" constraint,\n",
" False,\n",
" benchmark_w,\n",
" current_position=current_position,\n",
" turn_over_target=turn_over_target)\n",
" except ValueError:\n",
" print('{0} full rebalance'.format(date))\n",
" target_pos, _ = er_portfolio_analysis(er,\n",
" industry,\n",
" dx_return,\n",
" constraint,\n",
" False,\n",
" benchmark_w)\n",
" \n",
" target_pos['code'] = codes\n",
" \n",
" turn_over, executed_pos = execution_pipeline.execute(target_pos=target_pos)\n",
" \n",
" executed_codes = executed_pos.code.tolist()\n",
" dx_retuns = engine.fetch_dx_return(date, executed_codes, horizon=horizon)\n",
" \n",
" result = pd.merge(executed_pos, total_data, on=['code'], how='inner')\n",
" result = pd.merge(result, dx_retuns, on=['code'])\n",
" leverage = result.weight_x.abs().sum()\n",
" \n",
" ret = (result.weight_x - result.weight_y * leverage / result.weight_y.sum()).values @ result.dx.values\n",
" rets.append(ret)\n",
" leverags.append(executed_pos.weight.abs().sum())\n",
" turn_overs.append(turn_over)\n",
" previous_pos = executed_pos"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"ret_df4 = pd.DataFrame({'returns': rets, 'turn_over': turn_overs, 'leverage': leverags}, index=dates)\n",
"ret_df4.loc[advanceDateByCalendar('china.sse', dates[-1], freq)] = 0.\n",
"ret_df4 = ret_df4.shift(1)\n",
"ret_df4.iloc[0] = 0.\n",
"ret_df4['tc_cost'] = ret_df4.turn_over * 0.002"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_df4[['returns', 'tc_cost']].cumsum().plot(figsize=(12, 6),\n",
" title='Target turn over rebalanced: Rebalance freq {0}, {1} turnover_target'.format(freq,\n",
" turn_over_target_base,\n",
" target_vol),\n",
" secondary_y='tc_cost')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_atfer_tc = ret_df4.returns - ret_df4.tc_cost\n",
"print(\"sharp: \", ret_atfer_tc.mean() / ret_atfer_tc.std() * np.sqrt(252))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ret_df4[['returns', 'leverage']].rolling(window=60).std().plot(figsize=(12, 6), title='rolling std', secondary_y='leverage')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.6.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment