| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- # 克隆自聚宽文章:https://www.joinquant.com/post/61157
- # 标题:致敬市场(13)——K-means 指数增强
- # 作者:Gyro^.^
- import numpy as np
- import pandas as pd
- import datetime as dt
- from sklearn.cluster import KMeans
- def initialize(context):
- # setting system
- log.set_level('order', 'error')
- set_option('use_real_price', True)
- set_option('avoid_future_data', True)
- run_monthly(iUpdate, 1, 'before_open')
- run_daily(iTrader, 'every_bar')
- run_daily(iReport, 'after_close')
- def iUpdate(context):
- # paramers
- nday = 243
- n_cluster = 24
- n_class = 5
- # all funds
- dt_now = context.current_dt.date()
- all_fund = get_all_securities('fund', dt_now)
- # onlist 1-year
- dt_1y = dt_now - dt.timedelta(days=365)
- funds = all_fund[all_fund.start_date < dt_1y].index.tolist()
- # filter, liquity
- hm = history(nday, '1d', 'money', funds).mean()
- funds = hm[hm > 1e6].index.tolist()
- # history return
- h = history(nday, '1d', 'close', funds).dropna(axis=1)
- r = np.log(h).diff()[1:]
- # annual return
- ar = 100*nday*r.mean()
- # K-means
- cluster = KMeans(n_clusters=n_cluster).fit(r.T)
- # labels
- c = pd.Series(cluster.labels_, r.columns)
- # choice
- df = pd.DataFrame(columns=['Cluster', 'Return', 'Name'])
- for k in range(n_cluster):
- # class
- choice = []
- for s in c.index:
- if c[s] == k:
- choice.append(s)
- log.info(k, len(choice))
- # high return
- cr = ar[choice].sort_values(ascending=False).head(n_class)
- for s in cr.index:
- if s in context.portfolio.positions:
- df.loc[s] = [k, cr[s], get_security_info(s).display_name]
- break
- else:
- s = cr.index[0]
- df.loc[s] = [k, cr[s], get_security_info(s).display_name]
- # report
- pd.set_option('display.max_rows', None)
- log.info('Funds', len(df), '\n', df)
- # results
- g.funds = df.index.tolist()
- g.position_size = 0.95/n_cluster * context.portfolio.total_value
- def iTrader(context):
- # load data
- choice = g.funds
- position_size = g.position_size
- lm_value = 0.7*position_size
- hm_value = 1.5*position_size
- cash_size = 0.05*context.portfolio.total_value
- cdata = get_current_data()
- # sell
- for s in context.portfolio.positions:
- if cdata[s].paused or \
- cdata[s].last_price >= cdata[s].high_limit or cdata[s].last_price <= cdata[s].low_limit:
- continue # 过滤三停
- if s not in choice:
- log.info('sell', s, cdata[s].name)
- order_target(s, 0, MarketOrderStyle(0.99*cdata[s].last_price))
- # buy
- for s in choice:
- if cdata[s].paused or \
- cdata[s].last_price >= cdata[s].high_limit or cdata[s].last_price <= cdata[s].low_limit:
- continue # 过滤三停
- if context.portfolio.available_cash < cash_size:
- break # 现金耗尽
- if s not in context.portfolio.positions:
- log.info('buy', s, cdata[s].name)
- order_target_value(s, position_size, MarketOrderStyle(1.01*cdata[s].last_price))
- def iReport(context):
- # table of positions
- ptable = pd.DataFrame(columns=['amount', 'value', 'weight', 'name'])
- tvalue = context.portfolio.total_value
- cdata = get_current_data()
- for s in context.portfolio.positions:
- ps = context.portfolio.positions[s]
- ptable.loc[s] = [ps.total_amount, int(ps.value), 100*ps.value/tvalue, cdata[s].name]
- ptable = ptable.sort_values(by='weight', ascending=False)
- # report portfolio
- pd.set_option('display.max_rows', None)
- log.info(' positions', len(ptable), '\n', ptable.head())
- log.info(' total value %.2f, cash %.2f', \
- context.portfolio.total_value/10000, context.portfolio.available_cash/10000)
- # end
|