Unverified Commit 233c92ad authored by lion-sing's avatar lion-sing Committed by GitHub

Merge pull request #6 from alpha-miner/master

update
parents 9b36cc04 79792651
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
<tr> <tr>
<td>Build Status</td> <td>Build Status</td>
<td> <td>
   <a href="https://travis-ci.org/wegamekinglc/alpha-mind">    <a href="https://travis-ci.org/alpha-miner/alpha-mind">
<img src="https://travis-ci.org/wegamekinglc/alpha-mind.svg?branch=master" alt="travis build status" /> <img src="https://travis-ci.org/alpha-miner/alpha-mind.svg?branch=master" alt="travis build status" />
</a> </a>
</td> </td>
</tr> </tr>
......
# -*- coding: utf-8 -*-
"""
Created on 2018-3-5
@author: cheng.li
"""
import numpy as np
import pandas as pd
import statsmodels.api as sm
from alphamind.utilities import alpha_logger
from alphamind.data.processing import factor_processing
def cs_impl(ref_date,
factor_data,
factor_name,
risk_exposure,
constraint_risk,
industry_matrix,
dx_returns):
total_data = pd.merge(factor_data, risk_exposure, on='code')
total_data = pd.merge(total_data, industry_matrix, on='code').dropna()
if len(total_data) < 0.33 * len(factor_data):
alpha_logger.warning(f"valid data point({len(total_data)}) "
f"is less than 33% of the total sample ({len(factor_data)}). Omit this run")
return np.nan, np.nan, np.nan
total_risk_exp = total_data[constraint_risk]
er = total_data[factor_name].values.astype(float)
er = factor_processing(er, [], total_risk_exp.values, []).flatten()
industry = total_data.industry_name.values
codes = total_data.code.tolist()
target_pos = pd.DataFrame({'code': codes,
'weight': er,
'industry': industry})
target_pos['weight'] = target_pos['weight'] / target_pos['weight'].abs().sum()
target_pos = pd.merge(target_pos, dx_returns, on=['code'])
target_pos = pd.merge(target_pos, total_data[['code'] + constraint_risk], on=['code'])
activate_weight = target_pos.weight.values
excess_return = np.exp(target_pos.dx.values) - 1.
port_ret = np.log(activate_weight @ excess_return + 1.)
ic = np.corrcoef(excess_return, activate_weight)[0, 1]
x = sm.add_constant(activate_weight)
results = sm.OLS(excess_return, x).fit()
t_stats = results.tvalues[1]
alpha_logger.info(f"{ref_date} is finished with {len(target_pos)} stocks for {factor_name}")
alpha_logger.info(f"{ref_date} risk_exposure: "
f"{np.sum(np.square(target_pos.weight.values @ target_pos[constraint_risk].values))}")
return port_ret, ic, t_stats
def cross_section_analysis(ref_date,
factor_name,
universe,
horizon,
constraint_risk,
engine):
codes = engine.fetch_codes(ref_date, universe)
risk_exposure = engine.fetch_risk_model(ref_date, codes)[1][['code'] + constraint_risk]
factor_data = engine.fetch_factor(ref_date, factor_name, codes)
industry_matrix = engine.fetch_industry_matrix(ref_date, codes, 'sw_adj', 1)
dx_returns = engine.fetch_dx_return(ref_date, codes, horizon=horizon, offset=1)
return cs_impl(ref_date, factor_data, factor_name, risk_exposure, constraint_risk, industry_matrix, dx_returns)
if __name__ == '__main__':
import numpy as np
import pandas as pd
import statsmodels.api as sm
from alphamind.api import *
factor_name = 'SIZE'
data_source = 'postgres+psycopg2://postgres:A12345678!@10.63.6.220/alpha'
engine = SqlEngine(data_source)
risk_names = list(set(risk_styles).difference({factor_name}))
industry_names = list(set(industry_styles).difference({factor_name}))
constraint_risk = risk_names + industry_names
universe = Universe('custom', ['ashare_ex'])
horizon = 9
x = cross_section_analysis('2018-02-08',
factor_name,
universe,
horizon,
constraint_risk,
engine=engine)
print(x)
...@@ -90,7 +90,7 @@ def er_portfolio_analysis(er: np.ndarray, ...@@ -90,7 +90,7 @@ def er_portfolio_analysis(er: np.ndarray,
cons_exp = constraints.risk_exp cons_exp = constraints.risk_exp
return lbound, ubound, cons_exp, risk_lbound, risk_ubound return lbound, ubound, cons_exp, risk_lbound, risk_ubound
if benchmark is not None and method == 'risk_neutral': if method == 'risk_neutral':
lbound, ubound, cons_exp, risk_lbound, risk_ubound = create_constraints(benchmark, **kwargs) lbound, ubound, cons_exp, risk_lbound, risk_ubound = create_constraints(benchmark, **kwargs)
turn_over_target = kwargs.get('turn_over_target') turn_over_target = kwargs.get('turn_over_target')
......
...@@ -1921,18 +1921,6 @@ class Gogoal(Base): ...@@ -1921,18 +1921,6 @@ class Gogoal(Base):
tcap = Column(Float(53)) tcap = Column(Float(53))
class OutrightTmp(Base):
__tablename__ = 'outright_tmp'
__table_args__ = (
Index('outright_trade_date_code_portfolio_name_uindex', 'trade_date', 'code', 'portfolio_name', unique=True),
)
trade_date = Column(DateTime, primary_key=True, nullable=False)
code = Column(Integer, primary_key=True, nullable=False)
portfolio_name = Column(String(50), primary_key=True, nullable=False)
volume = Column(Integer, nullable=False)
class Outright(Base): class Outright(Base):
__tablename__ = 'outright' __tablename__ = 'outright'
__table_args__ = ( __table_args__ = (
...@@ -1956,5 +1944,5 @@ class Outright(Base): ...@@ -1956,5 +1944,5 @@ class Outright(Base):
if __name__ == '__main__': if __name__ == '__main__':
from sqlalchemy import create_engine from sqlalchemy import create_engine
engine = create_engine('postgres+psycopg2://postgres:we083826@101.132.104.118/alpha') engine = create_engine('postgres+psycopg2://postgres:we083826@192.168.0.102/alpha')
Base.metadata.create_all(engine) Base.metadata.create_all(engine)
...@@ -16,16 +16,12 @@ import alphamind.utilities as utils ...@@ -16,16 +16,12 @@ import alphamind.utilities as utils
def neutralize(x: np.ndarray, def neutralize(x: np.ndarray,
y: np.ndarray, y: np.ndarray,
groups: np.ndarray=None, groups: np.ndarray=None,
detail: bool=False, detail: bool=False) \
weights: np.ndarray=None) \
-> Union[np.ndarray, Tuple[np.ndarray, Dict]]: -> Union[np.ndarray, Tuple[np.ndarray, Dict]]:
if y.ndim == 1: if y.ndim == 1:
y = y.reshape((-1, 1)) y = y.reshape((-1, 1))
if weights is None:
weights = np.ones(len(y), dtype=float)
output_dict = {} output_dict = {}
if detail: if detail:
...@@ -41,17 +37,17 @@ def neutralize(x: np.ndarray, ...@@ -41,17 +37,17 @@ def neutralize(x: np.ndarray,
if detail: if detail:
for diff_loc in index_diff: for diff_loc in index_diff:
curr_idx = order[start:diff_loc + 1] curr_idx = order[start:diff_loc + 1]
curr_x, b = _sub_step(x, y, weights, curr_idx, res) curr_x, b = _sub_step(x, y, curr_idx, res)
exposure[curr_idx, :, :] = b exposure[curr_idx, :, :] = b
explained[curr_idx] = ls_explain(curr_x, b) explained[curr_idx] = ls_explain(curr_x, b)
start = diff_loc + 1 start = diff_loc + 1
else: else:
for diff_loc in index_diff: for diff_loc in index_diff:
curr_idx = order[start:diff_loc + 1] curr_idx = order[start:diff_loc + 1]
_sub_step(x, y, weights, curr_idx, res) _sub_step(x, y, curr_idx, res)
start = diff_loc + 1 start = diff_loc + 1
else: else:
b = ls_fit(x, y, weights) b = ls_fit(x, y)
res = ls_res(x, y, b) res = ls_res(x, y, b)
if detail: if detail:
...@@ -65,17 +61,16 @@ def neutralize(x: np.ndarray, ...@@ -65,17 +61,16 @@ def neutralize(x: np.ndarray,
@nb.njit(nogil=True, cache=True) @nb.njit(nogil=True, cache=True)
def _sub_step(x, y, w, curr_idx, res) -> Tuple[np.ndarray, np.ndarray]: def _sub_step(x, y, curr_idx, res) -> Tuple[np.ndarray, np.ndarray]:
curr_x, curr_y, curr_w = x[curr_idx], y[curr_idx], w[curr_idx] curr_x, curr_y= x[curr_idx], y[curr_idx]
b = ls_fit(curr_x, curr_y, curr_w) b = ls_fit(curr_x, curr_y)
res[curr_idx] = ls_res(curr_x, curr_y, b) res[curr_idx] = ls_res(curr_x, curr_y, b)
return curr_x, b return curr_x, b
@nb.njit(nogil=True, cache=True) @nb.njit(nogil=True, cache=True)
def ls_fit(x: np.ndarray, y: np.ndarray, w: np.ndarray) -> np.ndarray: def ls_fit(x: np.ndarray, y: np.ndarray) -> np.ndarray:
x_bar = x.T * w b = np.linalg.lstsq(x, y, rcond=-1)[0]
b = np.linalg.solve(x_bar @ x, x_bar @ y)
return b return b
......
...@@ -24,6 +24,6 @@ def rank(x: np.ndarray, groups: Optional[np.ndarray]=None) -> np.ndarray: ...@@ -24,6 +24,6 @@ def rank(x: np.ndarray, groups: Optional[np.ndarray]=None) -> np.ndarray:
curr_idx = order[start:diff_loc + 1] curr_idx = order[start:diff_loc + 1]
res[curr_idx] = x[curr_idx].argsort(axis=0) res[curr_idx] = x[curr_idx].argsort(axis=0)
start = diff_loc + 1 start = diff_loc + 1
return res
else: else:
return x.argsort(axis=0) return x.argsort(axis=0).argsort(axis=0)
\ No newline at end of file
...@@ -7,12 +7,9 @@ Created on 2017-11-8 ...@@ -7,12 +7,9 @@ Created on 2017-11-8
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from matplotlib import pyplot as plt
from alphamind.api import *
from PyFin.api import * from PyFin.api import *
from PyFin.Math.Accumulators.StatefulAccumulators import MovingAverage from alphamind.api import *
from PyFin.Math.Accumulators.StatefulAccumulators import MovingSharp from matplotlib import pyplot as plt
from PyFin.Math.Accumulators.StatefulAccumulators import MovingMaxDrawdown
plt.style.use('ggplot') plt.style.use('ggplot')
...@@ -21,126 +18,143 @@ Back test parameter settings ...@@ -21,126 +18,143 @@ Back test parameter settings
""" """
start_date = '2010-01-01' start_date = '2010-01-01'
end_date = '2018-01-29' end_date = '2018-02-27'
frequency = '10b' freq = '10b'
method = 'risk_neutral'
industry_lower = 1. industry_lower = 1.
industry_upper = 1. industry_upper = 1.
neutralize_risk = ['SIZE'] + industry_styles neutralized_risk = industry_styles
constraint_risk = ['SIZE'] + industry_styles industry_name = 'sw'
size_risk_lower = 0 industry_level = 1
size_risk_upper = 0 turn_over_target_base = 2.0
turn_over_target_base = 0.30
benchmark_total_lower = 0.8 benchmark_total_lower = 0.8
benchmark_total_upper = 1.0 benchmark_total_upper = 1.0
horizon = map_freq(frequency) batch = 0
horizon = map_freq(freq)
weight_gap = 0.01
universe = Universe("custom", ['zz800'])
data_source = 'postgres+psycopg2://postgres:A12345678!@10.63.6.220/alpha'
benchmark_code = 905
offset = 1
executor = NaiveExecutor() executor = NaiveExecutor()
ref_dates = makeSchedule(start_date, end_date, freq, 'china.sse')
engine = SqlEngine(data_source)
alpha_factors = {
'f01': LAST('ep_q'),
'f02': LAST('roe_q'),
'f03': LAST('market_confidence_25d'),
'f04': LAST('ILLIQUIDITY'),
'f05': LAST('cfinc1_q'),
'f06': LAST('CFO2EV'),
'f07': LAST('IVR'),
'f08': LAST('con_pe_rolling_order'),
'f09': LAST('con_pb_rolling_order'),
}
weights = dict(f01=1.,
f02=1.,
f03=0.25,
f04=0.25,
f05=0.25,
f06=0.25,
f07=0.25,
f08=-0.25,
f09=-0.25)
alpha_model = ConstLinearModel(features=alpha_factors, weights=weights)
def train_worker(ref_date):
data_meta = DataMeta(freq=freq,
universe=universe,
batch=batch,
neutralized_risk=neutralized_risk,
risk_model='short',
pre_process=[winsorize_normal, standardize],
post_process=[winsorize_normal, standardize, rank],
warm_start=0,
data_source=data_source)
return train_model(ref_date, alpha_model, data_meta)
def predict_worker(params):
data_meta = DataMeta(freq=freq,
universe=universe,
batch=batch,
neutralized_risk=neutralized_risk,
risk_model='short',
pre_process=[winsorize_normal, standardize],
post_process=[winsorize_normal, standardize, rank],
warm_start=0,
data_source=data_source)
ref_date, model = params
er = predict_by_model(ref_date, model, data_meta)
return er
predicts = [predict_worker((d.strftime('%Y-%m-%d'), alpha_model)) for d in ref_dates]
# rebalance
industry_names = industry_list(industry_name, industry_level)
constraint_risk = ['SIZE', 'SIZENL', 'BETA'] + industry_names
total_risk_names = constraint_risk + ['benchmark', 'total']
b_type = []
l_val = []
u_val = []
previous_pos = pd.DataFrame()
rets = []
turn_overs = []
leverags = []
for name in total_risk_names:
if name == 'benchmark':
b_type.append(BoundaryType.RELATIVE)
l_val.append(benchmark_total_lower)
u_val.append(benchmark_total_upper)
elif name in {'SIZE', 'SIZENL', 'BETA'}:
b_type.append(BoundaryType.ABSOLUTE)
l_val.append(0.0)
u_val.append(0.0)
else:
b_type.append(BoundaryType.RELATIVE)
l_val.append(industry_lower)
u_val.append(industry_upper)
def factor_analysis(engine, factor_name, universe, benchmark_code, positive=True, neutralize_factors=None): bounds = create_box_bounds(total_risk_names, b_type, l_val, u_val)
""" industry_total = engine.fetch_industry_matrix_range(universe, dates=ref_dates, category=industry_name,
Model phase: we need 1 constant linear model and one linear regression model level=industry_level)
""" benchmark_total = engine.fetch_benchmark_range(dates=ref_dates, benchmark=benchmark_code)
alpha_name = [str(factor_name) + '_' + ('pos' if positive else 'neg')] risk_total = engine.fetch_risk_model_range(universe, dates=ref_dates)[1]
if neutralize_factors: for i, ref_date in enumerate(ref_dates):
prev_factors = [] ref_date = ref_date.strftime('%Y-%m-%d')
for i, f in enumerate(neutralize_factors): industry_matrix = industry_total[industry_total.trade_date == ref_date]
pure_factor = LAST(f) benchmark_w = benchmark_total[benchmark_total.trade_date == ref_date]
for j in range(i): risk_matrix = risk_total[risk_total.trade_date == ref_date]
pure_factor = CSRes(pure_factor, prev_factors[j])
prev_factors.append(pure_factor)
simple_expression = LAST(factor_name) res = pd.merge(industry_matrix, benchmark_w, on=['code'], how='left').fillna(0.)
for f in prev_factors: res = pd.merge(res, risk_matrix, on=['code'])
simple_expression = CSRes(simple_expression, f) res = res.dropna()
else: codes = res.code.values.tolist()
simple_expression = LAST(factor_name)
if not positive:
simple_expression = -simple_expression
const_features = {alpha_name[0]: simple_expression}
const_weights = np.array([1.])
const_model = ConstLinearModel(features=alpha_name,
weights=const_weights)
ref_dates = makeSchedule(start_date, end_date, frequency, 'china.sse')
const_model_factor_data = engine.fetch_data_range(universe,
const_features,
dates=ref_dates,
benchmark=benchmark_code)['factor'].dropna()
horizon = map_freq(frequency)
rets = []
turn_overs = []
leverags = []
previous_pos = pd.DataFrame()
index_dates = []
factor_groups = const_model_factor_data.groupby('trade_date')
for i, value in enumerate(factor_groups):
date = value[0]
data = value[1]
index_dates.append(date)
total_data = data.fillna(data[alpha_name].median())
alpha_logger.info('{0}: {1}'.format(date, len(total_data)))
risk_exp = total_data[neutralize_risk].values.astype(float)
industry = total_data.industry_code.values
benchmark_w = total_data.weight.values
constraint_exp = total_data[constraint_risk].values
risk_exp_expand = np.concatenate((constraint_exp, np.ones((len(risk_exp), 1))), axis=1).astype(float)
risk_names = constraint_risk + ['total']
risk_target = risk_exp_expand.T @ benchmark_w
lbound = np.maximum(0., benchmark_w - 0.02) # np.zeros(len(total_data))
ubound = 0.02 + benchmark_w
is_in_benchmark = (benchmark_w > 0.).astype(float)
risk_exp_expand = np.concatenate((risk_exp_expand, is_in_benchmark.reshape((-1, 1))), axis=1).astype(float)
risk_names.append('benchmark_total')
constraint = Constraints(risk_exp_expand, risk_names)
for j, name in enumerate(risk_names):
if name == 'total':
constraint.set_constraints(name,
lower_bound=risk_target[j],
upper_bound=risk_target[j])
elif name == 'SIZE':
base_target = abs(risk_target[j])
constraint.set_constraints(name,
lower_bound=risk_target[j] + base_target * size_risk_lower,
upper_bound=risk_target[j] + base_target * size_risk_upper)
elif name == 'benchmark_total':
base_target = benchmark_w.sum()
constraint.set_constraints(name,
lower_bound=benchmark_total_lower * base_target,
upper_bound=benchmark_total_upper * base_target)
else:
constraint.set_constraints(name,
lower_bound=risk_target[j] * industry_lower,
upper_bound=risk_target[j] * industry_upper)
factor_values = factor_processing(total_data[alpha_name].values, benchmark_w = res.weight.values
pre_process=[winsorize_normal, standardize], is_in_benchmark = (benchmark_w > 0.).astype(float).reshape((-1, 1))
risk_factors=risk_exp,
post_process=[winsorize_normal, standardize])
# const linear model total_risk_exp = np.concatenate([res[constraint_risk].values.astype(float),
er = const_model.predict(factor_values) is_in_benchmark,
np.ones_like(is_in_benchmark)],
axis=1)
total_risk_exp = pd.DataFrame(total_risk_exp, columns=total_risk_names)
constraints = LinearConstraints(bounds, total_risk_exp, benchmark_w)
codes = total_data['code'].values lbound = np.maximum(0., benchmark_w - weight_gap) # np.zeros(len(total_data))
ubound = weight_gap + benchmark_w
if previous_pos.empty: if previous_pos.empty:
current_position = None current_position = None
...@@ -153,116 +167,71 @@ def factor_analysis(engine, factor_name, universe, benchmark_code, positive=True ...@@ -153,116 +167,71 @@ def factor_analysis(engine, factor_name, universe, benchmark_code, positive=True
turn_over_target = turn_over_target_base turn_over_target = turn_over_target_base
current_position = remained_pos.weight.values current_position = remained_pos.weight.values
er = predicts[i].loc[codes].values
try: try:
alpha_logger.info('{0} partial re-balance: {1}'.format(ref_date, len(er)))
target_pos, _ = er_portfolio_analysis(er, target_pos, _ = er_portfolio_analysis(er,
industry, industry_matrix.industry_name.values,
None, None,
constraint, constraints,
False, False,
benchmark_w, benchmark_w,
method=method, method='risk_neutral',
turn_over_target=turn_over_target, turn_over_target=turn_over_target,
current_position=current_position, current_position=current_position,
lbound=lbound, lbound=lbound,
ubound=ubound) ubound=ubound)
except ValueError: except ValueError:
alpha_logger.info('{0} full re-balance'.format(date)) alpha_logger.info('{0} full re-balance: {1}'.format(ref_date, len(er)))
target_pos, _ = er_portfolio_analysis(er, target_pos, _ = er_portfolio_analysis(er,
industry, industry_matrix.industry_name.values,
None, None,
constraint, constraints,
False, False,
benchmark_w, benchmark_w,
method=method, method='risk_neutral',
lbound=lbound, lbound=lbound,
ubound=ubound) ubound=ubound)
target_pos['code'] = total_data['code'].values target_pos['code'] = codes
turn_over, executed_pos = executor.execute(target_pos=target_pos) turn_over, executed_pos = executor.execute(target_pos=target_pos)
executed_codes = executed_pos.code.tolist() executed_codes = executed_pos.code.tolist()
dx_returns = engine.fetch_dx_return(date, executed_codes, horizon=horizon, offset=1) dx_returns = engine.fetch_dx_return(ref_date, executed_codes, horizon=horizon, offset=offset)
result = pd.merge(executed_pos, dx_returns, on=['code'])
result = pd.merge(executed_pos, total_data[['code', 'weight']], on=['code'], how='inner') leverage = result.weight.abs().sum()
result = pd.merge(result, dx_returns, on=['code'])
leverage = result.weight_x.abs().sum() ret = result.weight.values @ (np.exp(result.dx.values) - 1.)
ret = result.weight_x.values @ (np.exp(result.dx.values) - 1.)
rets.append(np.log(1. + ret)) rets.append(np.log(1. + ret))
executor.set_current(executed_pos) executor.set_current(executed_pos)
turn_overs.append(turn_over) turn_overs.append(turn_over)
leverags.append(leverage) leverags.append(leverage)
previous_pos = executed_pos previous_pos = executed_pos
alpha_logger.info('{0} is finished'.format(date)) alpha_logger.info('{0} is finished'.format(ref_date))
ret_df = pd.DataFrame({'returns': rets, 'turn_over': turn_overs, 'leverage': leverags}, index=index_dates)
# index return
index_return = engine.fetch_dx_return_index_range(benchmark_code, start_date, end_date, horizon=horizon,
offset=1).set_index('trade_date')
ret_df['index'] = index_return['dx']
ret_df.loc[advanceDateByCalendar('china.sse', ref_dates[-1], frequency)] = 0.
ret_df = ret_df.shift(1)
ret_df.iloc[0] = 0.
ret_df['tc_cost'] = ret_df.turn_over * 0.002
ret_df['returns'] = ret_df['returns'] - ret_df['index'] * ret_df['leverage']
return alpha_name[0], ret_df
def worker_func_positive(factor_name):
from alphamind.api import SqlEngine, Universe
neutralize_factors = ['roe_q', 'ep_q']
engine = SqlEngine()
benchmark_code = 905
universe_name = ['zz500']
universe = Universe('custom', universe_name)
return factor_analysis(engine, factor_name, universe, benchmark_code, positive=True, neutralize_factors=neutralize_factors)
def worker_func_negative(factor_name):
from alphamind.api import SqlEngine, Universe
neutralize_factors = ['roe_q', 'ep_q']
engine = SqlEngine()
benchmark_code = 905
universe_name = ['zz500']
universe = Universe('custom', universe_name)
return factor_analysis(engine, factor_name, universe, benchmark_code, positive=False, neutralize_factors=neutralize_factors)
if __name__ == '__main__':
from dask.distributed import Client
client = Client('192.168.0.102:8786')
engine = SqlEngine()
df = engine.fetch_factor_coverage()
df = df[df.universe == 'zz800'].groupby('factor').mean()
df = df[df.coverage >= 0.98]
tasks = client.map(worker_func_positive, df.index.tolist()) ret_df = pd.DataFrame({'returns': rets, 'turn_over': turn_overs, 'leverage': leverags}, index=ref_dates)
res1 = client.gather(tasks)
tasks = client.map(worker_func_negative, df.index.tolist()) # index return
res2 = client.gather(tasks) index_return = engine.fetch_dx_return_index_range(benchmark_code, start_date, end_date, horizon=horizon,
offset=offset).set_index('trade_date')
ret_df['index'] = index_return['dx']
factor_df = pd.DataFrame() ret_df.loc[advanceDateByCalendar('china.sse', ref_dates[-1], freq)] = 0.
ret_df = ret_df.shift(1)
ret_df.iloc[0] = 0.
ret_df['tc_cost'] = ret_df.turn_over * 0.002
ret_df['returns'] = ret_df['returns'] - ret_df['index'] * ret_df['leverage']
for f_name, df in res1: ret_df[['returns', 'tc_cost']].cumsum().plot(figsize=(12, 6),
factor_df[f_name] = df['returns'] title='Fixed freq rebalanced: {0} with benchmark {1}'.format(freq, 905),
secondary_y='tc_cost')
for f_name, df in res2: ret_df[['returns', 'tc_cost']][-30:].cumsum().plot(figsize=(12, 6),
factor_df[f_name] = df['returns'] title='Fixed freq rebalanced: {0} with benchmark {1}'.format(freq,
905),
secondary_y='tc_cost')
# factor_name = LAST('EBITDA') / LAST('ev') plt.show()
# f_name, ret_df = worker_func_positive(factor_name)
#
# ret_df[['returns', 'tc_cost']].cumsum().plot(figsize=(12, 6),
# title='Fixed frequency rebalanced: {0} for {1} with benchmark {2}'.format(
# frequency, factor_name, 905),
# secondary_y='tc_cost')
# plt.show()
...@@ -94,7 +94,7 @@ def factor_analysis(f_name): ...@@ -94,7 +94,7 @@ def factor_analysis(f_name):
if __name__ == '__main__': if __name__ == '__main__':
from dask.distributed import Client from dask.distributed import Client
client = Client('10.63.6.176:8786') client = Client('10.63.6.13:8786')
engine = SqlEngine() engine = SqlEngine()
df = engine.fetch_factor_coverage() df = engine.fetch_factor_coverage()
......
...@@ -16,12 +16,14 @@ from alphamind.model.data_preparing import fetch_predict_phase ...@@ -16,12 +16,14 @@ from alphamind.model.data_preparing import fetch_predict_phase
from alphamind.data.engines.universe import Universe from alphamind.data.engines.universe import Universe
from alphamind.data.engines.sqlengine import SqlEngine from alphamind.data.engines.sqlengine import SqlEngine
from alphamind.data.winsorize import winsorize_normal from alphamind.data.winsorize import winsorize_normal
from alphamind.data.rank import rank
from alphamind.data.standardize import standardize from alphamind.data.standardize import standardize
from alphamind.model.loader import load_model from alphamind.model.loader import load_model
PROCESS_MAPPING = { PROCESS_MAPPING = {
'winsorize_normal': winsorize_normal, 'winsorize_normal': winsorize_normal,
'standardize': standardize 'standardize': standardize,
'rank': rank,
} }
......
...@@ -105,21 +105,28 @@ class LinearConstraints(object): ...@@ -105,21 +105,28 @@ class LinearConstraints(object):
def __init__(self, def __init__(self,
bounds: Dict[str, BoxBoundary], bounds: Dict[str, BoxBoundary],
cons_mat: pd.DataFrame, cons_mat: pd.DataFrame,
backbone: np.ndarray): backbone: np.ndarray=None):
pyFinAssert(len(bounds) == cons_mat.shape[1], "Number of bounds should be same as number of col of cons_mat") pyFinAssert(len(bounds) == cons_mat.shape[1], "Number of bounds should be same as number of col of cons_mat")
pyFinAssert(cons_mat.shape[0] == len(backbone),
"length of back bond should be same as number of rows of cons_mat")
self.names = list(bounds.keys()) self.names = list(bounds.keys())
self.bounds = bounds self.bounds = bounds
self.cons_mat = cons_mat self.cons_mat = cons_mat
self.backbone = backbone self.backbone = backbone
pyFinAssert(cons_mat.shape[0] == len(backbone) if backbone is not None else True,
"length of back bond should be same as number of rows of cons_mat")
def risk_targets(self) -> Tuple[np.ndarray, np.ndarray]: def risk_targets(self) -> Tuple[np.ndarray, np.ndarray]:
lower_bounds = [] lower_bounds = []
upper_bounds = [] upper_bounds = []
if self.backbone is None:
backbone = np.zeros(len(self.cons_mat))
else:
backbone = self.backbone
for name in self.names: for name in self.names:
center = self.backbone @ self.cons_mat[name].values center = backbone @ self.cons_mat[name].values
l, u = self.bounds[name].bounds(center) l, u = self.bounds[name].bounds(center)
lower_bounds.append(l) lower_bounds.append(l)
upper_bounds.append(u) upper_bounds.append(u)
......
...@@ -6,9 +6,33 @@ Created on 2017-8-8 ...@@ -6,9 +6,33 @@ Created on 2017-8-8
""" """
import unittest import unittest
import numpy as np
import pandas as pd
from alphamind.data.rank import rank
class TestRank(unittest.TestCase): class TestRank(unittest.TestCase):
def setUp(self):
self.x = np.random.randn(1000, 1)
self.groups = np.random.randint(0, 10, 1000)
def test_rank(self): def test_rank(self):
pass data_rank = rank(self.x)
\ No newline at end of file
sorted_array = np.zeros_like(self.x)
for i in range(self.x.shape[0]):
for j in range(self.x.shape[1]):
sorted_array[data_rank[i, j], j] = self.x[i, j]
arr_diff = np.diff(sorted_array, axis=0)
np.testing.assert_array_less(0, arr_diff)
def test_rank_with_groups(self):
data_rank = rank(self.x, groups=self.groups)
df = pd.DataFrame(self.x, index=self.groups)
expected_rank = df.groupby(level=0).apply(lambda x: x.values.argsort(axis=0).argsort(axis=0))
print(expected_rank)
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 1, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 2, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
"industry_lower = 1.0\n", "industry_lower = 1.0\n",
"industry_upper = 1.0\n", "industry_upper = 1.0\n",
"method = 'risk_neutral'\n", "method = 'risk_neutral'\n",
"neutralize_risk = ['SIZE'] + industry_styles\n", "neutralize_risk = industry_styles\n",
"industry_name = 'sw_adj'\n", "industry_name = 'sw_adj'\n",
"industry_level = 1\n", "industry_level = 1\n",
"benchmark_total_lower = 0.8\n", "benchmark_total_lower = 0.8\n",
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 3, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
"\"\"\"\n", "\"\"\"\n",
"\n", "\n",
"industry_names = industry_list(industry_name, industry_level)\n", "industry_names = industry_list(industry_name, industry_level)\n",
"constraint_risk = ['SIZE', 'SIZENL', 'BETA']\n", "constraint_risk = ['SIZE', 'SIZENL', 'BETA'] + industry_names\n",
"total_risk_names = constraint_risk + ['benchmark', 'total']\n", "total_risk_names = constraint_risk + ['benchmark', 'total']\n",
"\n", "\n",
"b_type = []\n", "b_type = []\n",
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -160,7 +160,7 @@ ...@@ -160,7 +160,7 @@
" factor_values = factor_processing(total_data[alpha_name].values,\n", " factor_values = factor_processing(total_data[alpha_name].values,\n",
" pre_process=[winsorize_normal, standardize],\n", " pre_process=[winsorize_normal, standardize],\n",
" risk_factors=risk_exp,\n", " risk_factors=risk_exp,\n",
" post_process=[winsorize_normal, standardize, rank])\n", " post_process=[winsorize_normal, standardize])\n",
"\n", "\n",
" # const linear model\n", " # const linear model\n",
" er = const_model.predict(pd.DataFrame(data={alpha_name[0]: factor_values.flatten()}))\n", " er = const_model.predict(pd.DataFrame(data={alpha_name[0]: factor_values.flatten()}))\n",
...@@ -222,10 +222,12 @@ ...@@ -222,10 +222,12 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"%%time\n",
"\n",
"df = engine.fetch_factor_coverage(start_date='2011-01-01',\n", "df = engine.fetch_factor_coverage(start_date='2011-01-01',\n",
" end_date='2018-02-12',\n", " end_date='2018-02-12',\n",
" universe=universe_name[0])\n", " universe=universe_name[0])\n",
...@@ -236,17 +238,9 @@ ...@@ -236,17 +238,9 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [],
{
"name": "stdout",
"output_type": "stream",
"text": [
"Wall time: 1h 25min 5s\n"
]
}
],
"source": [ "source": [
"%%time\n", "%%time\n",
"\n", "\n",
...@@ -274,7 +268,7 @@ ...@@ -274,7 +268,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -287,26 +281,33 @@ ...@@ -287,26 +281,33 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"with pd.ExcelWriter(f'{universe_name[0]}_{benchmark_code}.xlsx', engine='xlsxwriter') as writer:\n", "with pd.ExcelWriter(f'{universe_name[0]}_{benchmark_code}.xlsx', engine='xlsxwriter') as writer:\n",
" factor_df.to_excel(writer, sheet_name='ret')\n", " factor_df.to_excel(writer, sheet_name='ret')\n",
" factor_res.to_excel(writer, sheet_name='ic')\n", " ic_df.to_excel(writer, sheet_name='ic')\n",
" factor_df.to_excel(writer, sheet_name='ret_stat')\n", " factor_res.to_excel(writer, sheet_name='ret_stat')\n",
" ic_res.to_excel(writer, sheet_name='ic_stat')" " ic_res.to_excel(writer, sheet_name='ic_stat')"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 9, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"client.close()" "client.close()"
] ]
}, },
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
...@@ -332,6 +333,35 @@ ...@@ -332,6 +333,35 @@
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.6.4" "version": "3.6.4"
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
} }
}, },
"nbformat": 4, "nbformat": 4,
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
"universe = Universe(\"custom\", ['zz800'])\n", "universe = Universe(\"custom\", ['zz800'])\n",
"data_source = 'postgres+psycopg2://postgres:A12345678!@10.63.6.220/alpha'\n", "data_source = 'postgres+psycopg2://postgres:A12345678!@10.63.6.220/alpha'\n",
"benchmark_code = 905\n", "benchmark_code = 905\n",
"offset = 0\n",
"\n", "\n",
"executor = NaiveExecutor()\n", "executor = NaiveExecutor()\n",
"ref_dates = makeSchedule(start_date, end_date, freq, 'china.sse')\n", "ref_dates = makeSchedule(start_date, end_date, freq, 'china.sse')\n",
...@@ -112,12 +113,12 @@ ...@@ -112,12 +113,12 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"# %%time\n", "%%time\n",
"\n", "\n",
"# training / predict on dask executor\n", "# training / predict on dask executor\n",
"\n", "\n",
"from dask.distributed import Client\n", "from dask.distributed import Client\n",
"client = Client('10.63.6.13:8786')\n", "client = Client('10.63.6.176:8786')\n",
"\n", "\n",
"tasks = client.map(predict_worker, [(d.strftime('%Y-%m-%d'), alpha_model) for d in ref_dates], pure=False)\n", "tasks = client.map(predict_worker, [(d.strftime('%Y-%m-%d'), alpha_model) for d in ref_dates], pure=False)\n",
"predicts = client.gather(tasks)" "predicts = client.gather(tasks)"
...@@ -230,7 +231,7 @@ ...@@ -230,7 +231,7 @@
" turn_over, executed_pos = executor.execute(target_pos=target_pos)\n", " turn_over, executed_pos = executor.execute(target_pos=target_pos)\n",
"\n", "\n",
" executed_codes = executed_pos.code.tolist()\n", " executed_codes = executed_pos.code.tolist()\n",
" dx_returns = engine.fetch_dx_return(ref_date, executed_codes, horizon=horizon, offset=1)\n", " dx_returns = engine.fetch_dx_return(ref_date, executed_codes, horizon=horizon, offset=offset)\n",
" result = pd.merge(executed_pos, dx_returns, on=['code'])\n", " result = pd.merge(executed_pos, dx_returns, on=['code'])\n",
"\n", "\n",
" leverage = result.weight.abs().sum()\n", " leverage = result.weight.abs().sum()\n",
...@@ -247,36 +248,15 @@ ...@@ -247,36 +248,15 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 75, "execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x1b2d29094a8>"
]
},
"execution_count": 75,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x1b2d1759ac8>"
]
},
"metadata": {}, "metadata": {},
"output_type": "display_data" "outputs": [],
}
],
"source": [ "source": [
"ret_df = pd.DataFrame({'returns': rets, 'turn_over': turn_overs, 'leverage': leverags}, index=ref_dates)\n", "ret_df = pd.DataFrame({'returns': rets, 'turn_over': turn_overs, 'leverage': leverags}, index=ref_dates)\n",
"\n", "\n",
"# index return\n", "# index return\n",
"index_return = engine.fetch_dx_return_index_range(benchmark_code, start_date, end_date, horizon=horizon,\n", "index_return = engine.fetch_dx_return_index_range(benchmark_code, start_date, end_date, horizon=horizon,\n",
" offset=1).set_index('trade_date')\n", " offset=offset).set_index('trade_date')\n",
"ret_df['index'] = index_return['dx']\n", "ret_df['index'] = index_return['dx']\n",
"\n", "\n",
"ret_df.loc[advanceDateByCalendar('china.sse', ref_dates[-1], freq)] = 0.\n", "ret_df.loc[advanceDateByCalendar('china.sse', ref_dates[-1], freq)] = 0.\n",
...@@ -295,7 +275,11 @@ ...@@ -295,7 +275,11 @@
"execution_count": null, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [] "source": [
"ret_df[['returns', 'tc_cost']][-30:].cumsum().plot(figsize=(12, 6),\n",
" title='Fixed freq rebalanced: {0} with benchmark {1}'.format(freq, 905),\n",
" secondary_y='tc_cost')"
]
}, },
{ {
"cell_type": "code", "cell_type": "code",
...@@ -322,6 +306,35 @@ ...@@ -322,6 +306,35 @@
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.6.4" "version": "3.6.4"
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
} }
}, },
"nbformat": 4, "nbformat": 4,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 1, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 12, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 13, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -62,27 +62,50 @@ ...@@ -62,27 +62,50 @@
"Factor Model\n", "Factor Model\n",
"\"\"\"\n", "\"\"\"\n",
"\n", "\n",
"# alpha_factors = {\n",
"# 'f01': LAST('ep_q'),\n",
"# 'f02': LAST('roe_q'),\n",
"# 'f03': LAST('market_confidence_25d'),\n",
"# 'f04': LAST('ILLIQUIDITY'),\n",
"# 'f05': LAST('cfinc1_q'),\n",
"# 'f06': LAST('CFO2EV'),\n",
"# 'f07': LAST('IVR'),\n",
"# 'f08': LAST('con_pe_rolling_order'),\n",
"# 'f09': LAST('con_pb_rolling_order')\n",
"# }\n",
"\n",
"\n",
"# weights = dict(f01=1.,\n",
"# f02=0.5,\n",
"# f03=0.5,\n",
"# f04=0.5,\n",
"# f05=0.5,\n",
"# f06=0.5,\n",
"# f07=0.5,\n",
"# f08=-0.5,\n",
"# f09=-0.5)\n",
"\n",
"alpha_factors = {\n", "alpha_factors = {\n",
" 'f01': LAST('ep_q'),\n", " 'f01': LAST('ep_q'),\n",
" 'f02': LAST('roe_q'),\n", " 'f02': LAST('roe_q'),\n",
" 'f03': LAST('market_confidence_25d'),\n", " 'f03': LAST('market_confidence_75d'),\n",
" 'f04': LAST('DivP'),\n", " 'f04': LAST('DivP'),\n",
" 'f05': LAST('BP'),\n", " 'f05': LAST('val_q'),\n",
" 'f06': LAST('val_q'),\n", " 'f06': LAST('con_np_rolling'),\n",
" 'f07': LAST('VOL60'),\n", " 'f07': LAST('GREV'),\n",
" 'f08': LAST('con_pe_rolling_order'),\n", " 'f08': LAST('con_pe_rolling_order'),\n",
" 'f09': LAST('con_pb_rolling_order')\n", " 'f09': LAST('con_pb_rolling_order')\n",
"}\n", "}\n",
"\n", "\n",
"weights = dict(f01=1.,\n", "weights = dict(f01=1.,\n",
" f02=1.,\n", " f02=0.5,\n",
" f03=0.25,\n", " f03=0.5,\n",
" f04=0.25,\n", " f04=0.5,\n",
" f05=0.25,\n", " f05=0.5,\n",
" f06=0.25,\n", " f06=0.5,\n",
" f07=-0.25,\n", " f07=0.5,\n",
" f08=-0.25,\n", " f08=-0.5,\n",
" f09=-0.25)\n", " f09=-0.5)\n",
"\n", "\n",
"alpha_model = ConstLinearModel(features=alpha_factors, weights=weights)\n", "alpha_model = ConstLinearModel(features=alpha_factors, weights=weights)\n",
"\n", "\n",
...@@ -103,17 +126,9 @@ ...@@ -103,17 +126,9 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 14, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [],
{
"name": "stdout",
"output_type": "stream",
"text": [
"Wall time: 0 ns\n"
]
}
],
"source": [ "source": [
"%%time\n", "%%time\n",
"\n", "\n",
...@@ -126,17 +141,9 @@ ...@@ -126,17 +141,9 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 15, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [],
{
"name": "stdout",
"output_type": "stream",
"text": [
"Wall time: 8.42 s\n"
]
}
],
"source": [ "source": [
"%%time\n", "%%time\n",
"\n", "\n",
...@@ -145,15 +152,16 @@ ...@@ -145,15 +152,16 @@
"\"\"\"\n", "\"\"\"\n",
"\n", "\n",
"from dask.distributed import Client\n", "from dask.distributed import Client\n",
"client = Client('10.63.6.13:8786')\n", "client = Client('10.63.6.176:8786')\n",
"\n", "\n",
"tasks = client.map(predict_worker, [(d.strftime('%Y-%m-%d'), alpha_model) for d in ref_dates], pure=False)\n", "tasks = client.map(predict_worker, [(d.strftime('%Y-%m-%d'), alpha_model) for d in ref_dates], pure=False)\n",
"predicts = client.gather(tasks)" "predicts = client.gather(tasks)\n",
"client.close()"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 16, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -174,7 +182,7 @@ ...@@ -174,7 +182,7 @@
" b_type.append(BoundaryType.RELATIVE)\n", " b_type.append(BoundaryType.RELATIVE)\n",
" l_val.append(benchmark_total_lower)\n", " l_val.append(benchmark_total_lower)\n",
" u_val.append(benchmark_total_upper)\n", " u_val.append(benchmark_total_upper)\n",
" elif name in {'SIZE', 'SIZENL', 'BETA'}:\n", " elif name in {'SIZE', 'SIZENL', 'BETA', 'total'}:\n",
" b_type.append(BoundaryType.ABSOLUTE)\n", " b_type.append(BoundaryType.ABSOLUTE)\n",
" l_val.append(0.0)\n", " l_val.append(0.0)\n",
" u_val.append(0.0)\n", " u_val.append(0.0)\n",
...@@ -192,7 +200,7 @@ ...@@ -192,7 +200,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 17, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -298,7 +306,7 @@ ...@@ -298,7 +306,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 18, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
...@@ -332,26 +340,9 @@ ...@@ -332,26 +340,9 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 19, "execution_count": null,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [],
{
"name": "stderr",
"output_type": "stream",
"text": [
"d:\\ProgramData\\Anaconda3\\lib\\site-packages\\ipykernel_launcher.py:39: FutureWarning: \n",
"Passing list-likes to .loc or [] with any missing label will raise\n",
"KeyError in the future, you can use .reindex() as an alternative.\n",
"\n",
"See the documentation here:\n",
"http://pandas.pydata.org/pandas-docs/stable/indexing.html#deprecate-loc-reindex-listlike\n",
"2018-02-27 15:42:15,597 - ALPHA_MIND - INFO - 0.005 finished\n",
"2018-02-27 15:43:09,157 - ALPHA_MIND - INFO - 0.01 finished\n",
"2018-02-27 15:44:02,819 - ALPHA_MIND - INFO - 0.015 finished\n",
"2018-02-27 15:44:56,827 - ALPHA_MIND - INFO - 0.02 finished\n"
]
}
],
"source": [ "source": [
"weight_gaps = [0.005, 0.010, 0.015, 0.020]\n", "weight_gaps = [0.005, 0.010, 0.015, 0.020]\n",
"\n", "\n",
...@@ -363,1356 +354,6 @@ ...@@ -363,1356 +354,6 @@
" alpha_logger.info(f\"{weight_gap} finished\")" " alpha_logger.info(f\"{weight_gap} finished\")"
] ]
}, },
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"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>daily_return</th>\n",
" <th>cum_ret</th>\n",
" <th>sharp</th>\n",
" <th>drawdown</th>\n",
" <th>max_drawn</th>\n",
" <th>leverage</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2010-01-04</th>\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.00000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-01-18</th>\n",
" <td>0.002456</td>\n",
" <td>0.002456</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.98210</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-02-01</th>\n",
" <td>-0.003691</td>\n",
" <td>-0.001234</td>\n",
" <td>0.000000</td>\n",
" <td>-0.003691</td>\n",
" <td>-0.003691</td>\n",
" <td>0.98080</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-02-22</th>\n",
" <td>-0.001398</td>\n",
" <td>-0.002632</td>\n",
" <td>0.000000</td>\n",
" <td>-0.005088</td>\n",
" <td>-0.005088</td>\n",
" <td>0.99880</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-03-08</th>\n",
" <td>0.001219</td>\n",
" <td>-0.001413</td>\n",
" <td>0.000000</td>\n",
" <td>-0.005088</td>\n",
" <td>-0.005088</td>\n",
" <td>1.00070</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-03-22</th>\n",
" <td>-0.003154</td>\n",
" <td>-0.004567</td>\n",
" <td>-1.564942</td>\n",
" <td>-0.007023</td>\n",
" <td>-0.007023</td>\n",
" <td>0.99990</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-04-06</th>\n",
" <td>0.005331</td>\n",
" <td>0.000764</td>\n",
" <td>0.170609</td>\n",
" <td>-0.007023</td>\n",
" <td>-0.007023</td>\n",
" <td>1.00030</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-04-20</th>\n",
" <td>-0.011791</td>\n",
" <td>-0.011027</td>\n",
" <td>-1.339591</td>\n",
" <td>-0.013484</td>\n",
" <td>-0.013484</td>\n",
" <td>1.00090</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-05-05</th>\n",
" <td>0.000307</td>\n",
" <td>-0.010720</td>\n",
" <td>-1.229194</td>\n",
" <td>-0.013484</td>\n",
" <td>-0.013484</td>\n",
" <td>0.99920</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-05-19</th>\n",
" <td>0.007962</td>\n",
" <td>-0.002758</td>\n",
" <td>-0.255024</td>\n",
" <td>-0.013484</td>\n",
" <td>-0.013484</td>\n",
" <td>0.99890</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-06-02</th>\n",
" <td>-0.007907</td>\n",
" <td>-0.010665</td>\n",
" <td>-0.862161</td>\n",
" <td>-0.013484</td>\n",
" <td>-0.013484</td>\n",
" <td>0.99980</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-06-21</th>\n",
" <td>0.007624</td>\n",
" <td>-0.003041</td>\n",
" <td>-0.214491</td>\n",
" <td>-0.013484</td>\n",
" <td>-0.013484</td>\n",
" <td>0.99990</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-07-05</th>\n",
" <td>0.008193</td>\n",
" <td>0.005152</td>\n",
" <td>0.323698</td>\n",
" <td>-0.013484</td>\n",
" <td>-0.013484</td>\n",
" <td>0.99960</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-07-19</th>\n",
" <td>0.001580</td>\n",
" <td>0.006732</td>\n",
" <td>0.408208</td>\n",
" <td>-0.013484</td>\n",
" <td>-0.013484</td>\n",
" <td>0.97430</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-08-02</th>\n",
" <td>0.005666</td>\n",
" <td>0.012399</td>\n",
" <td>0.708708</td>\n",
" <td>-0.013484</td>\n",
" <td>-0.013484</td>\n",
" <td>0.97470</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-08-16</th>\n",
" <td>-0.013279</td>\n",
" <td>-0.000880</td>\n",
" <td>-0.041376</td>\n",
" <td>-0.013484</td>\n",
" <td>-0.013484</td>\n",
" <td>1.00010</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-08-30</th>\n",
" <td>-0.002519</td>\n",
" <td>-0.003399</td>\n",
" <td>-0.154688</td>\n",
" <td>-0.015798</td>\n",
" <td>-0.015798</td>\n",
" <td>0.99900</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-09-13</th>\n",
" <td>-0.012298</td>\n",
" <td>-0.015698</td>\n",
" <td>-0.633037</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99960</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-09-30</th>\n",
" <td>0.003504</td>\n",
" <td>-0.012194</td>\n",
" <td>-0.474059</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99970</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-10-21</th>\n",
" <td>0.032270</td>\n",
" <td>0.020077</td>\n",
" <td>0.508137</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99950</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-11-04</th>\n",
" <td>-0.001903</td>\n",
" <td>0.018173</td>\n",
" <td>0.448466</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99990</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-11-18</th>\n",
" <td>0.004564</td>\n",
" <td>0.022737</td>\n",
" <td>0.546899</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99980</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-12-02</th>\n",
" <td>0.005751</td>\n",
" <td>0.028489</td>\n",
" <td>0.667092</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99970</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-12-16</th>\n",
" <td>-0.001650</td>\n",
" <td>0.026839</td>\n",
" <td>0.614522</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99950</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-12-30</th>\n",
" <td>0.007329</td>\n",
" <td>0.034168</td>\n",
" <td>0.759830</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99960</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-01-14</th>\n",
" <td>0.013885</td>\n",
" <td>0.048052</td>\n",
" <td>1.030272</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99960</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-01-28</th>\n",
" <td>0.010359</td>\n",
" <td>0.058411</td>\n",
" <td>1.180538</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.95740</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-02-18</th>\n",
" <td>0.001031</td>\n",
" <td>0.059442</td>\n",
" <td>1.290546</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.95720</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-03-04</th>\n",
" <td>0.001280</td>\n",
" <td>0.060722</td>\n",
" <td>1.351831</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99890</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-03-18</th>\n",
" <td>0.018729</td>\n",
" <td>0.079451</td>\n",
" <td>1.632026</td>\n",
" <td>-0.028096</td>\n",
" <td>-0.028096</td>\n",
" <td>0.99900</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2016-12-19</th>\n",
" <td>0.001795</td>\n",
" <td>0.619046</td>\n",
" <td>1.928640</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99999</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-01-03</th>\n",
" <td>0.006513</td>\n",
" <td>0.625559</td>\n",
" <td>1.833167</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99999</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-01-17</th>\n",
" <td>0.006098</td>\n",
" <td>0.631657</td>\n",
" <td>1.732021</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00003</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-02-07</th>\n",
" <td>0.003424</td>\n",
" <td>0.635081</td>\n",
" <td>2.190087</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00003</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-02-21</th>\n",
" <td>0.010451</td>\n",
" <td>0.645533</td>\n",
" <td>2.439619</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99996</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-03-07</th>\n",
" <td>-0.001086</td>\n",
" <td>0.644447</td>\n",
" <td>2.149152</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99798</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-03-21</th>\n",
" <td>0.016426</td>\n",
" <td>0.660873</td>\n",
" <td>2.235613</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00003</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-04-06</th>\n",
" <td>-0.001144</td>\n",
" <td>0.659729</td>\n",
" <td>2.356093</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00003</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-04-20</th>\n",
" <td>0.007881</td>\n",
" <td>0.667610</td>\n",
" <td>2.448745</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00004</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-05-05</th>\n",
" <td>-0.008892</td>\n",
" <td>0.658717</td>\n",
" <td>2.227092</td>\n",
" <td>-0.013104</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00004</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-05-19</th>\n",
" <td>-0.002924</td>\n",
" <td>0.655793</td>\n",
" <td>2.487909</td>\n",
" <td>-0.012972</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99990</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-06-06</th>\n",
" <td>0.003434</td>\n",
" <td>0.659228</td>\n",
" <td>2.593745</td>\n",
" <td>-0.012802</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99990</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-06-20</th>\n",
" <td>0.011297</td>\n",
" <td>0.670525</td>\n",
" <td>2.860042</td>\n",
" <td>-0.011817</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00004</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-07-04</th>\n",
" <td>0.009526</td>\n",
" <td>0.680050</td>\n",
" <td>3.041796</td>\n",
" <td>-0.011817</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00004</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-07-18</th>\n",
" <td>0.016175</td>\n",
" <td>0.696225</td>\n",
" <td>3.055025</td>\n",
" <td>-0.011817</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99999</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-08-01</th>\n",
" <td>0.002968</td>\n",
" <td>0.699193</td>\n",
" <td>2.857844</td>\n",
" <td>-0.011817</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99999</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-08-15</th>\n",
" <td>-0.007531</td>\n",
" <td>0.691662</td>\n",
" <td>2.289452</td>\n",
" <td>-0.011817</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00004</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-08-29</th>\n",
" <td>0.001026</td>\n",
" <td>0.692688</td>\n",
" <td>2.742168</td>\n",
" <td>-0.011817</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00004</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-09-12</th>\n",
" <td>0.000855</td>\n",
" <td>0.693543</td>\n",
" <td>2.830931</td>\n",
" <td>-0.011817</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00004</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-09-26</th>\n",
" <td>-0.000392</td>\n",
" <td>0.693151</td>\n",
" <td>2.633798</td>\n",
" <td>-0.011817</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99997</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-10-17</th>\n",
" <td>-0.006175</td>\n",
" <td>0.686976</td>\n",
" <td>2.390601</td>\n",
" <td>-0.012217</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99997</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-10-31</th>\n",
" <td>-0.004647</td>\n",
" <td>0.682329</td>\n",
" <td>2.037490</td>\n",
" <td>-0.016864</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00002</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-11-14</th>\n",
" <td>0.005732</td>\n",
" <td>0.688061</td>\n",
" <td>2.259706</td>\n",
" <td>-0.016864</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00011</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-11-28</th>\n",
" <td>0.014009</td>\n",
" <td>0.702070</td>\n",
" <td>2.700705</td>\n",
" <td>-0.016864</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00011</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-12-12</th>\n",
" <td>0.000715</td>\n",
" <td>0.702786</td>\n",
" <td>2.437891</td>\n",
" <td>-0.016864</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99994</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-12-26</th>\n",
" <td>0.003241</td>\n",
" <td>0.706027</td>\n",
" <td>2.481936</td>\n",
" <td>-0.016864</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99996</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018-01-10</th>\n",
" <td>0.009375</td>\n",
" <td>0.715401</td>\n",
" <td>2.536598</td>\n",
" <td>-0.016864</td>\n",
" <td>-0.035754</td>\n",
" <td>1.00008</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018-01-24</th>\n",
" <td>0.001758</td>\n",
" <td>0.717159</td>\n",
" <td>2.417740</td>\n",
" <td>-0.016864</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99998</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018-02-07</th>\n",
" <td>0.003852</td>\n",
" <td>0.721011</td>\n",
" <td>2.429653</td>\n",
" <td>-0.016864</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99993</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018-02-28</th>\n",
" <td>0.000391</td>\n",
" <td>0.721402</td>\n",
" <td>2.185579</td>\n",
" <td>-0.016864</td>\n",
" <td>-0.035754</td>\n",
" <td>0.99994</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>199 rows × 6 columns</p>\n",
"</div>"
],
"text/plain": [
" daily_return cum_ret sharp drawdown max_drawn leverage\n",
"2010-01-04 0.000000 0.000000 0.000000 0.000000 0.000000 0.00000\n",
"2010-01-18 0.002456 0.002456 0.000000 0.000000 0.000000 0.98210\n",
"2010-02-01 -0.003691 -0.001234 0.000000 -0.003691 -0.003691 0.98080\n",
"2010-02-22 -0.001398 -0.002632 0.000000 -0.005088 -0.005088 0.99880\n",
"2010-03-08 0.001219 -0.001413 0.000000 -0.005088 -0.005088 1.00070\n",
"2010-03-22 -0.003154 -0.004567 -1.564942 -0.007023 -0.007023 0.99990\n",
"2010-04-06 0.005331 0.000764 0.170609 -0.007023 -0.007023 1.00030\n",
"2010-04-20 -0.011791 -0.011027 -1.339591 -0.013484 -0.013484 1.00090\n",
"2010-05-05 0.000307 -0.010720 -1.229194 -0.013484 -0.013484 0.99920\n",
"2010-05-19 0.007962 -0.002758 -0.255024 -0.013484 -0.013484 0.99890\n",
"2010-06-02 -0.007907 -0.010665 -0.862161 -0.013484 -0.013484 0.99980\n",
"2010-06-21 0.007624 -0.003041 -0.214491 -0.013484 -0.013484 0.99990\n",
"2010-07-05 0.008193 0.005152 0.323698 -0.013484 -0.013484 0.99960\n",
"2010-07-19 0.001580 0.006732 0.408208 -0.013484 -0.013484 0.97430\n",
"2010-08-02 0.005666 0.012399 0.708708 -0.013484 -0.013484 0.97470\n",
"2010-08-16 -0.013279 -0.000880 -0.041376 -0.013484 -0.013484 1.00010\n",
"2010-08-30 -0.002519 -0.003399 -0.154688 -0.015798 -0.015798 0.99900\n",
"2010-09-13 -0.012298 -0.015698 -0.633037 -0.028096 -0.028096 0.99960\n",
"2010-09-30 0.003504 -0.012194 -0.474059 -0.028096 -0.028096 0.99970\n",
"2010-10-21 0.032270 0.020077 0.508137 -0.028096 -0.028096 0.99950\n",
"2010-11-04 -0.001903 0.018173 0.448466 -0.028096 -0.028096 0.99990\n",
"2010-11-18 0.004564 0.022737 0.546899 -0.028096 -0.028096 0.99980\n",
"2010-12-02 0.005751 0.028489 0.667092 -0.028096 -0.028096 0.99970\n",
"2010-12-16 -0.001650 0.026839 0.614522 -0.028096 -0.028096 0.99950\n",
"2010-12-30 0.007329 0.034168 0.759830 -0.028096 -0.028096 0.99960\n",
"2011-01-14 0.013885 0.048052 1.030272 -0.028096 -0.028096 0.99960\n",
"2011-01-28 0.010359 0.058411 1.180538 -0.028096 -0.028096 0.95740\n",
"2011-02-18 0.001031 0.059442 1.290546 -0.028096 -0.028096 0.95720\n",
"2011-03-04 0.001280 0.060722 1.351831 -0.028096 -0.028096 0.99890\n",
"2011-03-18 0.018729 0.079451 1.632026 -0.028096 -0.028096 0.99900\n",
"... ... ... ... ... ... ...\n",
"2016-12-19 0.001795 0.619046 1.928640 -0.013104 -0.035754 0.99999\n",
"2017-01-03 0.006513 0.625559 1.833167 -0.013104 -0.035754 0.99999\n",
"2017-01-17 0.006098 0.631657 1.732021 -0.013104 -0.035754 1.00003\n",
"2017-02-07 0.003424 0.635081 2.190087 -0.013104 -0.035754 1.00003\n",
"2017-02-21 0.010451 0.645533 2.439619 -0.013104 -0.035754 0.99996\n",
"2017-03-07 -0.001086 0.644447 2.149152 -0.013104 -0.035754 0.99798\n",
"2017-03-21 0.016426 0.660873 2.235613 -0.013104 -0.035754 1.00003\n",
"2017-04-06 -0.001144 0.659729 2.356093 -0.013104 -0.035754 1.00003\n",
"2017-04-20 0.007881 0.667610 2.448745 -0.013104 -0.035754 1.00004\n",
"2017-05-05 -0.008892 0.658717 2.227092 -0.013104 -0.035754 1.00004\n",
"2017-05-19 -0.002924 0.655793 2.487909 -0.012972 -0.035754 0.99990\n",
"2017-06-06 0.003434 0.659228 2.593745 -0.012802 -0.035754 0.99990\n",
"2017-06-20 0.011297 0.670525 2.860042 -0.011817 -0.035754 1.00004\n",
"2017-07-04 0.009526 0.680050 3.041796 -0.011817 -0.035754 1.00004\n",
"2017-07-18 0.016175 0.696225 3.055025 -0.011817 -0.035754 0.99999\n",
"2017-08-01 0.002968 0.699193 2.857844 -0.011817 -0.035754 0.99999\n",
"2017-08-15 -0.007531 0.691662 2.289452 -0.011817 -0.035754 1.00004\n",
"2017-08-29 0.001026 0.692688 2.742168 -0.011817 -0.035754 1.00004\n",
"2017-09-12 0.000855 0.693543 2.830931 -0.011817 -0.035754 1.00004\n",
"2017-09-26 -0.000392 0.693151 2.633798 -0.011817 -0.035754 0.99997\n",
"2017-10-17 -0.006175 0.686976 2.390601 -0.012217 -0.035754 0.99997\n",
"2017-10-31 -0.004647 0.682329 2.037490 -0.016864 -0.035754 1.00002\n",
"2017-11-14 0.005732 0.688061 2.259706 -0.016864 -0.035754 1.00011\n",
"2017-11-28 0.014009 0.702070 2.700705 -0.016864 -0.035754 1.00011\n",
"2017-12-12 0.000715 0.702786 2.437891 -0.016864 -0.035754 0.99994\n",
"2017-12-26 0.003241 0.706027 2.481936 -0.016864 -0.035754 0.99996\n",
"2018-01-10 0.009375 0.715401 2.536598 -0.016864 -0.035754 1.00008\n",
"2018-01-24 0.001758 0.717159 2.417740 -0.016864 -0.035754 0.99998\n",
"2018-02-07 0.003852 0.721011 2.429653 -0.016864 -0.035754 0.99993\n",
"2018-02-28 0.000391 0.721402 2.185579 -0.016864 -0.035754 0.99994\n",
"\n",
"[199 rows x 6 columns]"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"res_df"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"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>leverage</th>\n",
" <th>returns</th>\n",
" <th>turn_over</th>\n",
" <th>index</th>\n",
" <th>tc_cost</th>\n",
" <th>ret_after_tc</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2010-01-04</th>\n",
" <td>0.00000</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>2010-01-18</th>\n",
" <td>1.00000</td>\n",
" <td>0.053401</td>\n",
" <td>1.824820</td>\n",
" <td>0.058077</td>\n",
" <td>0.003650</td>\n",
" <td>-0.008325</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-02-01</th>\n",
" <td>1.00000</td>\n",
" <td>-0.119192</td>\n",
" <td>0.294016</td>\n",
" <td>-0.115825</td>\n",
" <td>0.000588</td>\n",
" <td>-0.003955</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-02-22</th>\n",
" <td>0.99839</td>\n",
" <td>0.030674</td>\n",
" <td>0.416928</td>\n",
" <td>0.035925</td>\n",
" <td>0.000834</td>\n",
" <td>-0.006026</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-03-08</th>\n",
" <td>0.99537</td>\n",
" <td>0.055301</td>\n",
" <td>0.400000</td>\n",
" <td>0.044084</td>\n",
" <td>0.000800</td>\n",
" <td>0.010621</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-03-22</th>\n",
" <td>0.99846</td>\n",
" <td>-0.005239</td>\n",
" <td>0.400000</td>\n",
" <td>-0.000303</td>\n",
" <td>0.000800</td>\n",
" <td>-0.005736</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-04-06</th>\n",
" <td>0.99846</td>\n",
" <td>0.041815</td>\n",
" <td>0.400000</td>\n",
" <td>0.043072</td>\n",
" <td>0.000800</td>\n",
" <td>-0.001990</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-04-20</th>\n",
" <td>0.99837</td>\n",
" <td>0.007571</td>\n",
" <td>0.400000</td>\n",
" <td>0.006209</td>\n",
" <td>0.000800</td>\n",
" <td>0.000573</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-05-05</th>\n",
" <td>0.99837</td>\n",
" <td>-0.086619</td>\n",
" <td>0.400000</td>\n",
" <td>-0.101913</td>\n",
" <td>0.000800</td>\n",
" <td>0.014328</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-05-19</th>\n",
" <td>0.99857</td>\n",
" <td>-0.120487</td>\n",
" <td>0.400000</td>\n",
" <td>-0.124804</td>\n",
" <td>0.000800</td>\n",
" <td>0.003338</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-06-02</th>\n",
" <td>0.99857</td>\n",
" <td>0.038966</td>\n",
" <td>0.400000</td>\n",
" <td>0.038406</td>\n",
" <td>0.000800</td>\n",
" <td>-0.000185</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-06-21</th>\n",
" <td>0.99856</td>\n",
" <td>0.027008</td>\n",
" <td>0.400000</td>\n",
" <td>0.016080</td>\n",
" <td>0.000800</td>\n",
" <td>0.010152</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-07-05</th>\n",
" <td>0.99856</td>\n",
" <td>-0.101685</td>\n",
" <td>0.400000</td>\n",
" <td>-0.116871</td>\n",
" <td>0.000800</td>\n",
" <td>0.014218</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-07-19</th>\n",
" <td>0.99864</td>\n",
" <td>0.090080</td>\n",
" <td>0.357899</td>\n",
" <td>0.075146</td>\n",
" <td>0.000716</td>\n",
" <td>0.014320</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-08-02</th>\n",
" <td>0.99864</td>\n",
" <td>0.065057</td>\n",
" <td>0.400000</td>\n",
" <td>0.059507</td>\n",
" <td>0.000800</td>\n",
" <td>0.004831</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-08-16</th>\n",
" <td>0.99857</td>\n",
" <td>0.060243</td>\n",
" <td>0.442552</td>\n",
" <td>0.061077</td>\n",
" <td>0.000885</td>\n",
" <td>-0.001633</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-08-30</th>\n",
" <td>0.99857</td>\n",
" <td>0.020421</td>\n",
" <td>0.400000</td>\n",
" <td>0.029092</td>\n",
" <td>0.000800</td>\n",
" <td>-0.009430</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-09-13</th>\n",
" <td>0.99857</td>\n",
" <td>0.038996</td>\n",
" <td>0.400000</td>\n",
" <td>0.046005</td>\n",
" <td>0.000800</td>\n",
" <td>-0.007743</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-09-30</th>\n",
" <td>0.99870</td>\n",
" <td>0.017004</td>\n",
" <td>0.400000</td>\n",
" <td>-0.007814</td>\n",
" <td>0.000800</td>\n",
" <td>0.024007</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-10-21</th>\n",
" <td>0.99882</td>\n",
" <td>0.060105</td>\n",
" <td>0.400000</td>\n",
" <td>0.022792</td>\n",
" <td>0.000800</td>\n",
" <td>0.036541</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-11-04</th>\n",
" <td>0.99882</td>\n",
" <td>0.091060</td>\n",
" <td>0.400000</td>\n",
" <td>0.085199</td>\n",
" <td>0.000800</td>\n",
" <td>0.005161</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-11-18</th>\n",
" <td>0.99871</td>\n",
" <td>-0.033025</td>\n",
" <td>0.400000</td>\n",
" <td>-0.044425</td>\n",
" <td>0.000800</td>\n",
" <td>0.010543</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-12-02</th>\n",
" <td>0.99871</td>\n",
" <td>0.011059</td>\n",
" <td>0.400000</td>\n",
" <td>0.003812</td>\n",
" <td>0.000800</td>\n",
" <td>0.006452</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-12-16</th>\n",
" <td>0.99889</td>\n",
" <td>0.037539</td>\n",
" <td>0.342705</td>\n",
" <td>0.024784</td>\n",
" <td>0.000685</td>\n",
" <td>0.012097</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2010-12-30</th>\n",
" <td>0.99889</td>\n",
" <td>-0.051308</td>\n",
" <td>0.400000</td>\n",
" <td>-0.057394</td>\n",
" <td>0.000800</td>\n",
" <td>0.005223</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-01-14</th>\n",
" <td>0.99889</td>\n",
" <td>-0.067475</td>\n",
" <td>0.320836</td>\n",
" <td>-0.088210</td>\n",
" <td>0.000642</td>\n",
" <td>0.019996</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-01-28</th>\n",
" <td>0.99886</td>\n",
" <td>0.032970</td>\n",
" <td>0.400000</td>\n",
" <td>0.019919</td>\n",
" <td>0.000800</td>\n",
" <td>0.012274</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-02-18</th>\n",
" <td>0.99886</td>\n",
" <td>0.096319</td>\n",
" <td>0.400000</td>\n",
" <td>0.089584</td>\n",
" <td>0.000800</td>\n",
" <td>0.006037</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-03-04</th>\n",
" <td>0.99669</td>\n",
" <td>0.027365</td>\n",
" <td>0.445334</td>\n",
" <td>0.018727</td>\n",
" <td>0.000891</td>\n",
" <td>0.007809</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2011-03-18</th>\n",
" <td>0.99883</td>\n",
" <td>-0.007476</td>\n",
" <td>0.400000</td>\n",
" <td>-0.010187</td>\n",
" <td>0.000800</td>\n",
" <td>0.001899</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2016-12-19</th>\n",
" <td>0.99999</td>\n",
" <td>-0.033943</td>\n",
" <td>0.400000</td>\n",
" <td>-0.029751</td>\n",
" <td>0.000800</td>\n",
" <td>-0.004993</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-01-03</th>\n",
" <td>0.99999</td>\n",
" <td>0.017296</td>\n",
" <td>0.400000</td>\n",
" <td>0.014796</td>\n",
" <td>0.000800</td>\n",
" <td>0.001700</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-01-17</th>\n",
" <td>1.00015</td>\n",
" <td>-0.052533</td>\n",
" <td>0.495767</td>\n",
" <td>-0.052350</td>\n",
" <td>0.000992</td>\n",
" <td>-0.001166</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-02-07</th>\n",
" <td>1.00015</td>\n",
" <td>0.043140</td>\n",
" <td>0.400000</td>\n",
" <td>0.036071</td>\n",
" <td>0.000800</td>\n",
" <td>0.006263</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-02-21</th>\n",
" <td>0.99995</td>\n",
" <td>0.037631</td>\n",
" <td>0.298597</td>\n",
" <td>0.028620</td>\n",
" <td>0.000597</td>\n",
" <td>0.008415</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-03-07</th>\n",
" <td>0.99995</td>\n",
" <td>0.008797</td>\n",
" <td>0.392917</td>\n",
" <td>0.003521</td>\n",
" <td>0.000786</td>\n",
" <td>0.004491</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-03-21</th>\n",
" <td>0.99992</td>\n",
" <td>0.013840</td>\n",
" <td>0.400000</td>\n",
" <td>0.000433</td>\n",
" <td>0.000800</td>\n",
" <td>0.012607</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-04-06</th>\n",
" <td>0.99992</td>\n",
" <td>0.018937</td>\n",
" <td>0.400000</td>\n",
" <td>0.012087</td>\n",
" <td>0.000800</td>\n",
" <td>0.006052</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-04-20</th>\n",
" <td>1.00001</td>\n",
" <td>-0.025861</td>\n",
" <td>0.400000</td>\n",
" <td>-0.043176</td>\n",
" <td>0.000800</td>\n",
" <td>0.016516</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-05-05</th>\n",
" <td>1.00001</td>\n",
" <td>-0.064204</td>\n",
" <td>0.400000</td>\n",
" <td>-0.052973</td>\n",
" <td>0.000800</td>\n",
" <td>-0.012030</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-05-19</th>\n",
" <td>1.00002</td>\n",
" <td>-0.017313</td>\n",
" <td>0.400000</td>\n",
" <td>-0.014611</td>\n",
" <td>0.000800</td>\n",
" <td>-0.003502</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-06-06</th>\n",
" <td>1.00002</td>\n",
" <td>0.021499</td>\n",
" <td>0.400000</td>\n",
" <td>0.011175</td>\n",
" <td>0.000800</td>\n",
" <td>0.009524</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-06-20</th>\n",
" <td>1.00001</td>\n",
" <td>0.026088</td>\n",
" <td>0.400000</td>\n",
" <td>0.020476</td>\n",
" <td>0.000800</td>\n",
" <td>0.004812</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-07-04</th>\n",
" <td>1.00001</td>\n",
" <td>0.036755</td>\n",
" <td>0.400000</td>\n",
" <td>0.018864</td>\n",
" <td>0.000800</td>\n",
" <td>0.017091</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-07-18</th>\n",
" <td>1.00009</td>\n",
" <td>-0.002142</td>\n",
" <td>0.497130</td>\n",
" <td>-0.021973</td>\n",
" <td>0.000994</td>\n",
" <td>0.018838</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-08-01</th>\n",
" <td>1.00009</td>\n",
" <td>0.044924</td>\n",
" <td>0.400000</td>\n",
" <td>0.031278</td>\n",
" <td>0.000800</td>\n",
" <td>0.012844</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-08-15</th>\n",
" <td>1.00001</td>\n",
" <td>0.015946</td>\n",
" <td>0.400000</td>\n",
" <td>0.010663</td>\n",
" <td>0.000800</td>\n",
" <td>0.004483</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-08-29</th>\n",
" <td>1.00001</td>\n",
" <td>0.023096</td>\n",
" <td>0.400000</td>\n",
" <td>0.021099</td>\n",
" <td>0.000800</td>\n",
" <td>0.001197</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-09-12</th>\n",
" <td>1.00001</td>\n",
" <td>0.042608</td>\n",
" <td>0.400000</td>\n",
" <td>0.035183</td>\n",
" <td>0.000800</td>\n",
" <td>0.006625</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-09-26</th>\n",
" <td>1.00006</td>\n",
" <td>-0.014834</td>\n",
" <td>0.400000</td>\n",
" <td>-0.015251</td>\n",
" <td>0.000800</td>\n",
" <td>-0.000382</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-10-17</th>\n",
" <td>1.00006</td>\n",
" <td>-0.002885</td>\n",
" <td>0.400000</td>\n",
" <td>-0.002997</td>\n",
" <td>0.000800</td>\n",
" <td>-0.000688</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-10-31</th>\n",
" <td>1.00001</td>\n",
" <td>0.003111</td>\n",
" <td>0.372023</td>\n",
" <td>-0.001593</td>\n",
" <td>0.000744</td>\n",
" <td>0.003961</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-11-14</th>\n",
" <td>1.00002</td>\n",
" <td>-0.002265</td>\n",
" <td>0.400000</td>\n",
" <td>-0.002333</td>\n",
" <td>0.000800</td>\n",
" <td>-0.000731</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-11-28</th>\n",
" <td>1.00002</td>\n",
" <td>-0.026042</td>\n",
" <td>0.400000</td>\n",
" <td>-0.034113</td>\n",
" <td>0.000800</td>\n",
" <td>0.007272</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-12-12</th>\n",
" <td>0.99995</td>\n",
" <td>-0.004642</td>\n",
" <td>0.400000</td>\n",
" <td>-0.006497</td>\n",
" <td>0.000800</td>\n",
" <td>0.001054</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2017-12-26</th>\n",
" <td>0.99995</td>\n",
" <td>-0.014361</td>\n",
" <td>0.515549</td>\n",
" <td>-0.016749</td>\n",
" <td>0.001031</td>\n",
" <td>0.001356</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018-01-10</th>\n",
" <td>0.99999</td>\n",
" <td>0.070323</td>\n",
" <td>0.400000</td>\n",
" <td>0.040133</td>\n",
" <td>0.000800</td>\n",
" <td>0.029391</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018-01-24</th>\n",
" <td>1.00011</td>\n",
" <td>0.004529</td>\n",
" <td>0.400000</td>\n",
" <td>-0.006750</td>\n",
" <td>0.000800</td>\n",
" <td>0.010480</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018-02-07</th>\n",
" <td>1.00004</td>\n",
" <td>-0.080637</td>\n",
" <td>0.400000</td>\n",
" <td>-0.101533</td>\n",
" <td>0.000800</td>\n",
" <td>0.020100</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2018-02-28</th>\n",
" <td>0.99994</td>\n",
" <td>0.031790</td>\n",
" <td>0.400000</td>\n",
" <td>0.044632</td>\n",
" <td>0.000800</td>\n",
" <td>-0.013639</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>199 rows × 6 columns</p>\n",
"</div>"
],
"text/plain": [
" leverage returns turn_over index tc_cost ret_after_tc\n",
"2010-01-04 0.00000 0.000000 0.000000 0.000000 0.000000 0.000000\n",
"2010-01-18 1.00000 0.053401 1.824820 0.058077 0.003650 -0.008325\n",
"2010-02-01 1.00000 -0.119192 0.294016 -0.115825 0.000588 -0.003955\n",
"2010-02-22 0.99839 0.030674 0.416928 0.035925 0.000834 -0.006026\n",
"2010-03-08 0.99537 0.055301 0.400000 0.044084 0.000800 0.010621\n",
"2010-03-22 0.99846 -0.005239 0.400000 -0.000303 0.000800 -0.005736\n",
"2010-04-06 0.99846 0.041815 0.400000 0.043072 0.000800 -0.001990\n",
"2010-04-20 0.99837 0.007571 0.400000 0.006209 0.000800 0.000573\n",
"2010-05-05 0.99837 -0.086619 0.400000 -0.101913 0.000800 0.014328\n",
"2010-05-19 0.99857 -0.120487 0.400000 -0.124804 0.000800 0.003338\n",
"2010-06-02 0.99857 0.038966 0.400000 0.038406 0.000800 -0.000185\n",
"2010-06-21 0.99856 0.027008 0.400000 0.016080 0.000800 0.010152\n",
"2010-07-05 0.99856 -0.101685 0.400000 -0.116871 0.000800 0.014218\n",
"2010-07-19 0.99864 0.090080 0.357899 0.075146 0.000716 0.014320\n",
"2010-08-02 0.99864 0.065057 0.400000 0.059507 0.000800 0.004831\n",
"2010-08-16 0.99857 0.060243 0.442552 0.061077 0.000885 -0.001633\n",
"2010-08-30 0.99857 0.020421 0.400000 0.029092 0.000800 -0.009430\n",
"2010-09-13 0.99857 0.038996 0.400000 0.046005 0.000800 -0.007743\n",
"2010-09-30 0.99870 0.017004 0.400000 -0.007814 0.000800 0.024007\n",
"2010-10-21 0.99882 0.060105 0.400000 0.022792 0.000800 0.036541\n",
"2010-11-04 0.99882 0.091060 0.400000 0.085199 0.000800 0.005161\n",
"2010-11-18 0.99871 -0.033025 0.400000 -0.044425 0.000800 0.010543\n",
"2010-12-02 0.99871 0.011059 0.400000 0.003812 0.000800 0.006452\n",
"2010-12-16 0.99889 0.037539 0.342705 0.024784 0.000685 0.012097\n",
"2010-12-30 0.99889 -0.051308 0.400000 -0.057394 0.000800 0.005223\n",
"2011-01-14 0.99889 -0.067475 0.320836 -0.088210 0.000642 0.019996\n",
"2011-01-28 0.99886 0.032970 0.400000 0.019919 0.000800 0.012274\n",
"2011-02-18 0.99886 0.096319 0.400000 0.089584 0.000800 0.006037\n",
"2011-03-04 0.99669 0.027365 0.445334 0.018727 0.000891 0.007809\n",
"2011-03-18 0.99883 -0.007476 0.400000 -0.010187 0.000800 0.001899\n",
"... ... ... ... ... ... ...\n",
"2016-12-19 0.99999 -0.033943 0.400000 -0.029751 0.000800 -0.004993\n",
"2017-01-03 0.99999 0.017296 0.400000 0.014796 0.000800 0.001700\n",
"2017-01-17 1.00015 -0.052533 0.495767 -0.052350 0.000992 -0.001166\n",
"2017-02-07 1.00015 0.043140 0.400000 0.036071 0.000800 0.006263\n",
"2017-02-21 0.99995 0.037631 0.298597 0.028620 0.000597 0.008415\n",
"2017-03-07 0.99995 0.008797 0.392917 0.003521 0.000786 0.004491\n",
"2017-03-21 0.99992 0.013840 0.400000 0.000433 0.000800 0.012607\n",
"2017-04-06 0.99992 0.018937 0.400000 0.012087 0.000800 0.006052\n",
"2017-04-20 1.00001 -0.025861 0.400000 -0.043176 0.000800 0.016516\n",
"2017-05-05 1.00001 -0.064204 0.400000 -0.052973 0.000800 -0.012030\n",
"2017-05-19 1.00002 -0.017313 0.400000 -0.014611 0.000800 -0.003502\n",
"2017-06-06 1.00002 0.021499 0.400000 0.011175 0.000800 0.009524\n",
"2017-06-20 1.00001 0.026088 0.400000 0.020476 0.000800 0.004812\n",
"2017-07-04 1.00001 0.036755 0.400000 0.018864 0.000800 0.017091\n",
"2017-07-18 1.00009 -0.002142 0.497130 -0.021973 0.000994 0.018838\n",
"2017-08-01 1.00009 0.044924 0.400000 0.031278 0.000800 0.012844\n",
"2017-08-15 1.00001 0.015946 0.400000 0.010663 0.000800 0.004483\n",
"2017-08-29 1.00001 0.023096 0.400000 0.021099 0.000800 0.001197\n",
"2017-09-12 1.00001 0.042608 0.400000 0.035183 0.000800 0.006625\n",
"2017-09-26 1.00006 -0.014834 0.400000 -0.015251 0.000800 -0.000382\n",
"2017-10-17 1.00006 -0.002885 0.400000 -0.002997 0.000800 -0.000688\n",
"2017-10-31 1.00001 0.003111 0.372023 -0.001593 0.000744 0.003961\n",
"2017-11-14 1.00002 -0.002265 0.400000 -0.002333 0.000800 -0.000731\n",
"2017-11-28 1.00002 -0.026042 0.400000 -0.034113 0.000800 0.007272\n",
"2017-12-12 0.99995 -0.004642 0.400000 -0.006497 0.000800 0.001054\n",
"2017-12-26 0.99995 -0.014361 0.515549 -0.016749 0.001031 0.001356\n",
"2018-01-10 0.99999 0.070323 0.400000 0.040133 0.000800 0.029391\n",
"2018-01-24 1.00011 0.004529 0.400000 -0.006750 0.000800 0.010480\n",
"2018-02-07 1.00004 -0.080637 0.400000 -0.101533 0.000800 0.020100\n",
"2018-02-28 0.99994 0.031790 0.400000 0.044632 0.000800 -0.013639\n",
"\n",
"[199 rows x 6 columns]"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ret_df"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'zz800'"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"universe.base_universe[0]"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
...@@ -1738,6 +379,35 @@ ...@@ -1738,6 +379,35 @@
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.6.4" "version": "3.6.4"
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
} }
}, },
"nbformat": 4, "nbformat": 4,
......
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> The methodolegy is similar to The Barra China Equity Model (CNE5)'s documentation"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"\n",
"import numpy as np\n",
"import pandas as pd\n",
"from matplotlib import pyplot as plt\n",
"import statsmodels.api as sm\n",
"from alphamind.api import *\n",
"from PyFin.api import *\n",
"from alphamind.analysis.crosssetctions import cross_section_analysis\n",
"\n",
"plt.style.use('ggplot')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\"\"\"\n",
"Back test parameter settings\n",
"\"\"\"\n",
"\n",
"start_date = '2010-01-01'\n",
"end_date = '2018-02-28'\n",
"category = 'sw_adj'\n",
"level = 1\n",
"freq = '20b'\n",
"universe = Universe('custom', ['ashare_ex'])\n",
"\n",
"\n",
"horizon = map_freq(freq)\n",
"ref_dates = makeSchedule(start_date, end_date, freq, 'china.sse')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def risk_factor_analysis(factor_name):\n",
" data_source = 'postgres+psycopg2://postgres:A12345678!@10.63.6.220/alpha'\n",
" engine = SqlEngine(data_source)\n",
" risk_names = list(set(risk_styles).difference({factor_name}))\n",
" industry_names = list(set(industry_styles).difference({factor_name}))\n",
" constraint_risk = risk_names + industry_names\n",
" \n",
" df = pd.DataFrame(columns=['ret', 'ic', 't.'], dtype=float)\n",
"\n",
" for ref_date in ref_dates:\n",
" df.loc[ref_date, :] = cross_section_analysis(ref_date,\n",
" factor_name,\n",
" universe,\n",
" horizon,\n",
" constraint_risk,\n",
" engine=engine)\n",
" df.index = pd.to_datetime(df.index)\n",
" return df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"candidates_factors = risk_styles + industry_styles"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from dask.distributed import Client\n",
"\n",
"with Client('10.63.6.176:8786') as client:\n",
" tasks = client.map(risk_factor_analysis, candidates_factors, pure=False)\n",
" res = client.gather(tasks)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df = pd.DataFrame()\n",
"\n",
"for f_name, data in zip(candidates_factors, res):\n",
" data['factor'] = f_name\n",
" df = df.append(data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df['abs t.'] = np.abs(df['t.'])\n",
"df[['factor', 'abs t.']].groupby('factor').mean().sort_values('abs t.', ascending=False).head()"
]
}
],
"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.4"
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"position": {
"height": "607px",
"left": "1093px",
"right": "20px",
"top": "94px",
"width": "756px"
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 2
}
# -*- coding: utf-8 -*-
"""
Created on 2018-3-5
@author: cheng.li
"""
import numpy as np
import pandas as pd
import statsmodels.api as sm
from alphamind.api import (
SqlEngine, LinearConstraints, er_portfolio_analysis, alpha_logger
)
def cross_section_analysis(ref_date,
factor_name,
universe,
horizon,
constraint_risk,
linear_bounds,
lbound,
ubound,
engine):
codes = engine.fetch_codes(ref_date, universe)
risk_exposure = engine.fetch_risk_model(ref_date, codes)[1][['code'] + constraint_risk]
factor_data = engine.fetch_factor(ref_date, factor_name, codes)
industry_matrix = engine.fetch_industry_matrix(ref_date, codes, 'sw_adj', 1)
total_data = pd.merge(factor_data, risk_exposure, on='code')
total_data = pd.merge(total_data, industry_matrix, on='code').dropna()
total_risk_exp = total_data[constraint_risk]
constraints = LinearConstraints(linear_bounds, total_risk_exp)
er = total_data[factor_name].values
industry = total_data.industry_name.values
target_pos, _ = er_portfolio_analysis(er,
industry,
None,
constraints,
False,
None,
method='risk_neutral',
lbound=lbound*np.ones(len(er)),
ubound=ubound*np.ones(len(er)))
codes = total_data.code.tolist()
target_pos['code'] = codes
dx_returns = engine.fetch_dx_return(ref_date, codes, horizon=horizon, offset=1)
target_pos = pd.merge(target_pos, dx_returns, on=['code'])
activate_weight = target_pos.weight.values
excess_return = np.exp(target_pos.dx.values) - 1.
port_ret = np.log(activate_weight @ excess_return + 1.)
ic = np.corrcoef(excess_return, activate_weight)[0, 1]
x = sm.add_constant(activate_weight)
results = sm.OLS(excess_return, x).fit()
t_stats = results.tvalues[1]
alpha_logger.info(f"{ref_date} is finished with {len(target_pos)} stocks for {factor_name}")
return port_ret, ic, t_stats
if __name__ == '__main__':
from alphamind.api import (
Universe, map_freq, risk_styles, industry_styles, macro_styles, BoundaryType, create_box_bounds
)
"""
Back test parameter settings
"""
start_date = '2010-01-01'
end_date = '2018-02-28'
category = 'sw_adj'
level = 1
freq = '20b'
universe = Universe('custom', ['zz800'])
data_source = 'postgres+psycopg2://postgres:A12345678!@10.63.6.220/alpha'
engine = SqlEngine(data_source)
horizon = map_freq(freq)
"""
Factor Model
"""
factor_name = 'SIZE'
"""
Constraints
"""
risk_names = list(set(risk_styles).difference({factor_name}))
industry_names = list(set(industry_styles).difference({factor_name}))
constraint_risk = risk_names + industry_names + macro_styles
b_type = []
l_val = []
u_val = []
for name in constraint_risk:
if name in set(risk_styles):
b_type.append(BoundaryType.ABSOLUTE)
l_val.append(0.0)
u_val.append(0.0)
else:
b_type.append(BoundaryType.RELATIVE)
l_val.append(1.0)
u_val.append(1.0)
linear_bounds = create_box_bounds(constraint_risk, b_type, l_val, u_val)
ref_date = '2018-02-08'
df = pd.DataFrame(columns=['ret', 'ic', 't.'])
print(cross_section_analysis(ref_date,
factor_name,
universe,
horizon,
constraint_risk,
linear_bounds,
lbound=-0.01,
ubound=0.01,
engine=engine))
\ No newline at end of file
...@@ -21,7 +21,7 @@ from alphamind.data.dbmodel.models import * ...@@ -21,7 +21,7 @@ from alphamind.data.dbmodel.models import *
uqer.DataAPI.api_base.timeout = 300 uqer.DataAPI.api_base.timeout = 300
start_date = dt.datetime(2017, 8, 22) start_date = dt.datetime(2010, 1, 1)
dag_name = 'update_uqer_data_postgres' dag_name = 'update_uqer_data_postgres'
default_args = { default_args = {
...@@ -71,6 +71,41 @@ def data_info_log(df, table): ...@@ -71,6 +71,41 @@ def data_info_log(df, table):
raise ValueError(msg) raise ValueError(msg)
def update_uqer_index_market(ds, **kwargs):
ref_date, this_date = process_date(ds)
flag = check_holiday(this_date)
if not flag:
return
df = api.MktIdxdGet(tradeDate=ref_date)
df = df[df.exchangeCD.isin(['XSHE', 'XSHG', 'ZICN'])]
df = df[df.ticker <= '999999']
df.rename(columns={'tradeDate': 'trade_date',
'ticker': 'indexCode',
'CHGPct': 'chgPct',
'secShortName': 'indexShortName'}, inplace=True)
df = df[['trade_date',
'indexCode',
'preCloseIndex',
'openIndex',
'highestIndex',
'lowestIndex',
'closeIndex',
'turnoverVol',
'turnoverValue',
'chgPct']]
df['indexCode'] = df.indexCode.astype(int)
query = delete(IndexMarket).where(IndexMarket.trade_date == this_date)
engine.execute(query)
data_info_log(df, Market)
format_data(df, format='%Y-%m-%d')
df.to_sql(IndexMarket.__table__.name, engine, index=False, if_exists='append')
def update_uqer_factors(ds, **kwargs): def update_uqer_factors(ds, **kwargs):
ref_date, this_date = process_date(ds) ref_date, this_date = process_date(ds)
flag = check_holiday(this_date) flag = check_holiday(this_date)
...@@ -265,6 +300,105 @@ def update_uqer_universe_zz800(ds, **kwargs): ...@@ -265,6 +300,105 @@ def update_uqer_universe_zz800(ds, **kwargs):
df.to_sql(Universe.__table__.name, engine, index=False, if_exists='append') df.to_sql(Universe.__table__.name, engine, index=False, if_exists='append')
def update_uqer_universe_zz1000(ds, **kwargs):
ref_date, this_date = process_date(ds)
flag = check_holiday(this_date)
if not flag:
return
query = delete(Universe).where(
and_(
Universe.trade_date == this_date,
Universe.universe == 'zz1000'
)
)
engine.execute(query)
query = select([IndexComponent.trade_date, IndexComponent.code]).where(
and_(
IndexComponent.trade_date == this_date,
IndexComponent.indexCode == 852
)
)
df = pd.read_sql(query, engine)
if df.empty:
return
df['universe'] = 'zz1000'
data_info_log(df, Universe)
format_data(df)
df.to_sql(Universe.__table__.name, engine, index=False, if_exists='append')
def update_uqer_universe_zxb(ds, **kwargs):
ref_date, this_date = process_date(ds)
flag = check_holiday(this_date)
if not flag:
return
query = delete(Universe).where(
and_(
Universe.trade_date == this_date,
Universe.universe == 'zxb'
)
)
engine.execute(query)
query = select([IndexComponent.trade_date, IndexComponent.code]).where(
and_(
IndexComponent.trade_date == this_date,
IndexComponent.indexCode == 399005
)
)
df = pd.read_sql(query, engine)
if df.empty:
return
df['universe'] = 'zxb'
data_info_log(df, Universe)
format_data(df)
df.to_sql(Universe.__table__.name, engine, index=False, if_exists='append')
def update_uqer_universe_cyb(ds, **kwargs):
ref_date, this_date = process_date(ds)
flag = check_holiday(this_date)
if not flag:
return
query = delete(Universe).where(
and_(
Universe.trade_date == this_date,
Universe.universe == 'cyb'
)
)
engine.execute(query)
query = select([IndexComponent.trade_date, IndexComponent.code]).where(
and_(
IndexComponent.trade_date == this_date,
IndexComponent.indexCode == 399006
)
)
df = pd.read_sql(query, engine)
if df.empty:
return
df['universe'] = 'cyb'
data_info_log(df, Universe)
format_data(df)
df.to_sql(Universe.__table__.name, engine, index=False, if_exists='append')
def update_uqer_universe_security_master(ds, **kwargs): def update_uqer_universe_security_master(ds, **kwargs):
ref_date, this_date = process_date(ds) ref_date, this_date = process_date(ds)
flag = check_holiday(this_date) flag = check_holiday(this_date)
...@@ -442,7 +576,20 @@ def update_uqer_index_components(ds, **kwargs): ...@@ -442,7 +576,20 @@ def update_uqer_index_components(ds, **kwargs):
if df.empty: if df.empty:
continue continue
alpha_logger.info('{0} is finished with previous data {1}'.format(index, len(df)))
else: else:
################################
# 2017-10-09, patch for uqer bug
def filter_out_eqy(code: str):
if code[0] in ['0', '3'] and code[-4:] in ['XSHE']:
return True
elif code[0] in ['6'] and code[-4:] in ['XSHG']:
return True
else:
return False
df = df[df.consID.apply(lambda x: filter_out_eqy(x))]
################################
df.rename(columns={'ticker': 'indexCode', df.rename(columns={'ticker': 'indexCode',
'secShortName': 'indexShortName', 'secShortName': 'indexShortName',
'consTickerSymbol': 'code', 'consTickerSymbol': 'code',
...@@ -453,6 +600,7 @@ def update_uqer_index_components(ds, **kwargs): ...@@ -453,6 +600,7 @@ def update_uqer_index_components(ds, **kwargs):
df['trade_date'] = this_date df['trade_date'] = this_date
del df['secID'] del df['secID']
del df['consID'] del df['consID']
alpha_logger.info('{0} is finished with new data {1}'.format(index, len(df)))
total_data = total_data.append(df) total_data = total_data.append(df)
index_codes = total_data.indexCode.unique() index_codes = total_data.indexCode.unique()
...@@ -551,23 +699,6 @@ def update_uqer_risk_model(ds, **kwargs): ...@@ -551,23 +699,6 @@ def update_uqer_risk_model(ds, **kwargs):
df.to_sql(SpecificRiskLong.__table__.name, engine, index=False, if_exists='append') df.to_sql(SpecificRiskLong.__table__.name, engine, index=False, if_exists='append')
def update_uqer_daily_return(ds, **kwargs):
ref_date, this_date = process_date(ds)
flag = check_holiday(this_date)
if not flag:
return
previous_date = advanceDateByCalendar('china.sse', this_date, '-1b').strftime('%Y-%m-%d')
query = select([Market.code, Market.chgPct.label('d1')]).where(Market.trade_date == this_date)
df = pd.read_sql(query, engine)
df['trade_date'] = previous_date
engine.execute(delete(DailyReturn).where(DailyReturn.trade_date == previous_date))
data_info_log(df, DailyReturn)
df.to_sql(DailyReturn.__table__.name, engine, index=False, if_exists='append')
def update_uqer_industry_info(ds, **kwargs): def update_uqer_industry_info(ds, **kwargs):
ref_date, this_date = process_date(ds) ref_date, this_date = process_date(ds)
flag = check_holiday(this_date) flag = check_holiday(this_date)
...@@ -623,11 +754,12 @@ def fetch_date(table, query_date, engine): ...@@ -623,11 +754,12 @@ def fetch_date(table, query_date, engine):
return df return df
def update_materialized_views(ds, **kwargs): index_market_task = PythonOperator(
alpha_logger.info("starting refresh full_factor_view ...") task_id='update_uqer_index_market',
engine.execute("REFRESH MATERIALIZED VIEW CONCURRENTLY full_factor_view;") provide_context=True,
alpha_logger.info("starting cluster full_factor_view ...") python_callable=update_uqer_index_market,
engine.execute("CLUSTER full_factor_view;") dag=dag
)
uqer_task = PythonOperator( uqer_task = PythonOperator(
...@@ -644,13 +776,6 @@ market_task = PythonOperator( ...@@ -644,13 +776,6 @@ market_task = PythonOperator(
dag=dag dag=dag
) )
return_task = PythonOperator(
task_id='update_uqer_daily_return',
provide_context=True,
python_callable=update_uqer_daily_return,
dag=dag
)
industry_task = PythonOperator( industry_task = PythonOperator(
task_id='update_uqer_industry_info', task_id='update_uqer_industry_info',
provide_context=True, provide_context=True,
...@@ -658,7 +783,6 @@ industry_task = PythonOperator( ...@@ -658,7 +783,6 @@ industry_task = PythonOperator(
dag=dag dag=dag
) )
return_task.set_upstream(market_task)
industry_task.set_upstream(market_task) industry_task.set_upstream(market_task)
index_task = PythonOperator( index_task = PythonOperator(
...@@ -689,6 +813,13 @@ universe800_task = PythonOperator( ...@@ -689,6 +813,13 @@ universe800_task = PythonOperator(
dag=dag dag=dag
) )
universe1000_task = PythonOperator(
task_id='update_uqer_universe_zz1000',
provide_context=True,
python_callable=update_uqer_universe_zz1000,
dag=dag
)
universe50_task = PythonOperator( universe50_task = PythonOperator(
task_id='update_uqer_universe_sh50', task_id='update_uqer_universe_sh50',
provide_context=True, provide_context=True,
...@@ -696,16 +827,32 @@ universe50_task = PythonOperator( ...@@ -696,16 +827,32 @@ universe50_task = PythonOperator(
dag=dag dag=dag
) )
universe_zxb_task = PythonOperator(
task_id='update_uqer_universe_zxb',
provide_context=True,
python_callable=update_uqer_universe_zxb,
dag=dag
)
universe_cyb_task = PythonOperator(
task_id='update_uqer_universe_cyb',
provide_context=True,
python_callable=update_uqer_universe_cyb,
dag=dag
)
universe300_task.set_upstream(index_task) universe300_task.set_upstream(index_task)
universe500_task.set_upstream(index_task) universe500_task.set_upstream(index_task)
universe800_task.set_upstream(index_task) universe800_task.set_upstream(index_task)
universe1000_task.set_upstream(index_task)
universe50_task.set_upstream(index_task) universe50_task.set_upstream(index_task)
universe_zxb_task.set_upstream(index_task)
universe_cyb_task.set_upstream(index_task)
security_master_task = PythonOperator( security_master_task = PythonOperator(
task_id='update_uqer_universe_security_master', task_id='update_uqer_universe_security_master',
provide_context=True, provide_context=True,
python_callable=update_uqer_universe_security_master, python_callable=update_uqer_universe_security_master,
dependes_on_past=True,
dag=dag dag=dag
) )
...@@ -742,18 +889,6 @@ _ = PythonOperator( ...@@ -742,18 +889,6 @@ _ = PythonOperator(
dag=dag dag=dag
) )
refresh_materialized_views_task = PythonOperator(
task_id='update_materialized_views',
provide_context=True,
python_callable=update_materialized_views,
dag=dag
)
refresh_materialized_views_task.set_upstream(market_task)
refresh_materialized_views_task.set_upstream(uqer_task)
refresh_materialized_views_task.set_upstream(risk_model_task)
if __name__ == '__main__': if __name__ == '__main__':
update_uqer_index_components(ds='2010-02-01') update_uqer_index_components(ds='2017-11-10')
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