Fund_premium_improvement_fast_001.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. # 克隆自聚宽文章:https://www.joinquant.com/post/33636
  2. # 标题:etf基金溢价-改进版-高收益低回撤-速度已最优
  3. # 作者:发锅
  4. # 本策略网址:https://www.joinquant.com/algorithm/index/edit?algorithmId=2eb4f4246fdf81a56bf43c681de7278b&backtest=37
  5. # 导入函数库
  6. from jqdata import *
  7. from jqlib.technical_analysis import *
  8. import numpy as np
  9. import pandas as pd
  10. import statsmodels.api as sm
  11. import datetime as dt
  12. # 初始化函数,设定基准等等
  13. def initialize(context):
  14. # 设定沪深300作为基准
  15. set_benchmark('000300.XSHG')
  16. # 开启异步报单
  17. set_option('async_order', True)
  18. # 开启动态复权模式(真实价格)
  19. set_option('use_real_price', True)
  20. # 是否未来函数
  21. set_option("avoid_future_data", True)
  22. # 过滤掉order系列API产生的比error级别低的log
  23. # log.set_level('order', 'error')
  24. # 初始化全局变量
  25. g.loss_limit = 0.9 # 单基金止损比例
  26. g.drop_limit_days = 20 # 止损卖出后多少天不重新买入
  27. g.control_days = 0 # 初始化控制全局止损之后暂停的天数
  28. g.total_limit_days = 30 # 检查全局止损比例的天数范围
  29. g.total_limit_rate = 0.15 # 全局止损比例
  30. g.cool_days = 0 # 全局止损后多少天内不持仓,必须小于g.total_limit_days
  31. g.rate_list = []
  32. g.check_loss_list = []
  33. g.just_sell_list = []
  34. g.total_value_list = []
  35. g.hold_list = []
  36. g.holiday = ['2010-02-12','2010-04-30','2010-09-30','2011-02-01','2011-04-29','2011-09-30','2012-01-20','2012-04-27','2012-09-28','2013-02-08',
  37. '2013-04-26','2013-09-30','2014-01-30','2014-04-30','2014-09-30','2015-02-17','2015-04-30','2015-09-30','2016-02-05','2016-04-29','2016-09-30',
  38. '2017-01-26','2017-04-28','2017-09-29','2018-02-14','2018-04-27','2018-09-28','2019-02-01','2019-04-30','2019-09-30','2020-01-23','2020-04-30',
  39. '2020-09-30','2021-02-10','2021-04-30','2021-09-30','2022-01-28','2022-04-29','2022-09-30']
  40. set_order_cost(OrderCost(close_tax=0.000, open_commission=0.00025, close_commission=0.00025, min_commission=0), type='fund')
  41. run_daily(before_market_open, '09:20', reference_security='000300.XSHG')
  42. run_daily(market_open, '14:40', reference_security='000300.XSHG')
  43. run_daily(check_loss_up, time='14:45', reference_security='000300.XSHG')
  44. # run_daily(print_position_info, time='15:10', reference_security='000300.XSHG')
  45. def before_market_open(context):
  46. # 获取基金
  47. fund_list = get_all_securities(['lof', 'etf'], context.previous_date).index.tolist()
  48. g.length1 = len(fund_list)
  49. # 过滤太新的基金
  50. fund_list = filter_new_fund(context,fund_list)
  51. # 嘉实元和事件,所以在2019年5月之后不再买入
  52. if context.current_dt.date() >= np.datetime64('2019-05-01') and ('505888.XSHG' in fund_list):
  53. fund_list.remove('505888.XSHG')
  54. print('remove 505888.XSHG')
  55. # 成交额过滤
  56. df = history(count=5, unit='1d', field="volume", security_list=fund_list).T
  57. df.columns=['volume1','volume2','volume3','volume4','volume5']
  58. df['volume'] = df[['volume1','volume2','volume3','volume4','volume5']].mean(axis=1)
  59. print('after volume check: ', df.iloc[:5,:5])
  60. df.drop(['volume1','volume2','volume3','volume4','volume5'],inplace=True,axis=1)
  61. cur_total_value = context.portfolio.total_value
  62. df = df[df.volume > 1e5] #
  63. # print('after volume check: ', df.head())
  64. # 获取净值
  65. df = get_extras('unit_net_value', df.index.tolist(), end_date=context.previous_date, df=True, count=1).T
  66. df.columns=['unit_net_value']
  67. g.fund_list = df # 基金和净值的df
  68. log.info('开盘前记录净值...')
  69. def market_open(context):
  70. df = g.fund_list
  71. length2 = len(df)
  72. current = get_current_data()
  73. fund_list = df.index.tolist()
  74. ## 获得基金最新价
  75. df['last_price'] = [current[c].last_price for c in fund_list]
  76. ## 计算溢价
  77. df['premium'] = (df.last_price / df.unit_net_value - 1) * 100 #最新价格小于净值的小于0
  78. ## 根据溢价大小排序
  79. if hasattr(df, 'sort'): # 如果有sort方法就用sort,没有用sort_values
  80. df = df.sort(['premium'], ascending = True)
  81. else:
  82. df = df.sort_values(['premium'], ascending = True)
  83. df = df[(df.premium < 0)]
  84. special_rate = len(df)/g.length1
  85. g.rate_list.append(special_rate)
  86. g.rate_list = g.rate_list[-10:]
  87. while len(g.rate_list) < 10:
  88. g.rate_list.append(g.rate_list[0])
  89. if g.cool_days == 0:
  90. if (len(g.rate_list) == 10) and (mean(g.rate_list) > 0.1): # 比例过低就不执行买入卖出的操作
  91. target_fund_list = df[:20].index.tolist()
  92. target_fund_list = [stock for stock in target_fund_list if stock not in g.just_sell_list]
  93. target_fund_list = target_fund_list[:5]
  94. g.max_position = len(target_fund_list)
  95. # 卖出
  96. for fund in context.portfolio.positions.keys():
  97. # 卖出不在股票池或节假日前清仓
  98. if fund not in target_fund_list or str(context.current_dt.date()) in g.holiday:
  99. order_target_value(fund, 0)
  100. # 买入, 节假日前不开仓
  101. if str(context.current_dt.date()) not in g.holiday:
  102. for fund in target_fund_list:
  103. now_position = g.max_position - len(context.portfolio.positions)
  104. if now_position == 0:
  105. continue
  106. if fund not in context.portfolio.positions.keys():
  107. position = context.portfolio.available_cash / now_position
  108. order_target_value(fund, position)
  109. elif (len(g.rate_list) == 10) and (mean(g.rate_list) <= 0.1):
  110. if g.hold_list:
  111. clear_position(context)
  112. else:
  113. g.cool_days -= 1
  114. # 更新持有的基金池
  115. g.hold_list= []
  116. for position in list(context.portfolio.positions.values()):
  117. fund = position.security
  118. g.hold_list.append(fund)
  119. ## 收盘后运行函数
  120. def after_market_close(context):
  121. pass
  122. # 1-6 调整亏损比例过大的股票
  123. def check_loss_up(context):
  124. if g.hold_list:
  125. check_loss_list = []
  126. for stock in g.hold_list:
  127. position = context.portfolio.positions[stock]
  128. price = position.price
  129. avg_cost = position.avg_cost
  130. # print('check %s, price: %2f, avg_cost: %2f' % (stock, price, avg_cost))
  131. if price < g.loss_limit * avg_cost:
  132. log.info("[%s]损失比例过高,卖出" % stock)
  133. close_position(position)
  134. check_loss_list.append(stock)
  135. if check_loss_list:
  136. g.check_loss_list.append(check_loss_list)
  137. else:
  138. g.check_loss_list.append(['nothing'])
  139. if len(g.check_loss_list) > g.drop_limit_days:
  140. g.check_loss_list = g.check_loss_list[-g.drop_limit_days:]
  141. temp_set = set()
  142. for check_loss_list in g.check_loss_list:
  143. temp_set = temp_set.union(set(check_loss_list))
  144. # 不要购买的股票列表,过往20天因为止损而卖出的股票
  145. g.just_sell_list = list(temp_set)
  146. check_total_value(context)
  147. # 1-7 检查整体资金比例
  148. def check_total_value(context):
  149. total_money_today = context.portfolio.total_value
  150. g.total_value_list.append(total_money_today)
  151. print('检查整体资金比例g.total_value_list: ', len(g.total_value_list))
  152. print(g.total_value_list)
  153. if len(g.total_value_list) >= g.total_limit_days:
  154. g.total_value_list = g.total_value_list[-g.total_limit_days:] # 只考虑最近20天的跌幅来判断是否清仓
  155. biggest_pullback = (total_money_today - max(g.total_value_list))/max(g.total_value_list)
  156. print('检查近 %d 的最大损失为 %2f' % (g.total_limit_days, biggest_pullback * 100))
  157. if biggest_pullback < - g.total_limit_rate: # 当跌幅超过最大限制,则清空仓位
  158. clear_position(context)
  159. if g.control_days == 0: # 设定空仓天数
  160. print('清仓后,未修正的g.control_days为: ', g.control_days)
  161. g.control_days = g.cool_days
  162. print('清仓后,修正g.control_days为: ', g.control_days)
  163. print('持仓情况为: ', g.hold_list)
  164. print('判断标准为: ', (not g.hold_list))
  165. if not g.hold_list: # 如果卖光了,那么调整检查全盘资金的数据量,保留10天的数据,因为检查是最近20天,暂停10天
  166. g.total_value_list = g.total_value_list[-(g.total_limit_days-g.cool_days):]
  167. #3-1 交易模块-自定义下单
  168. def order_target_value_(security, value):
  169. if value == 0:
  170. log.debug("Selling out %s" % (security))
  171. else:
  172. log.debug("Order %s to value %f" % (security, value))
  173. return order_target_value(security, value)
  174. #3-3 交易模块-平仓
  175. def close_position(position):
  176. security = position.security
  177. order = order_target_value_(security, 0) # 可能会因停牌失败
  178. if order != None:
  179. if order.status == OrderStatus.held and order.filled == order.amount:
  180. return True
  181. return False
  182. #3-5 交易模块 - 清仓
  183. def clear_position(context):
  184. if context.portfolio.positions:
  185. g.cool_days = 5 # 清仓后5天不进行买入操作
  186. log.info("==> 清仓,卖出所有股票")
  187. for stock in context.portfolio.positions.keys():
  188. position = context.portfolio.positions[stock]
  189. close_position(position)
  190. #2-7 过滤次新股
  191. def filter_new_fund(context,stock_list):
  192. yesterday = context.previous_date
  193. return [stock for stock in stock_list if not yesterday - get_security_info(stock).start_date < datetime.timedelta(days=5)]
  194. # 清理list里nan的模块
  195. def clean_List_nan(List):
  196. Myarray=np.array(List)
  197. x = float('nan')
  198. for elem in Myarray:
  199. if math.isnan(x):
  200. x = 0.0
  201. return Myarray
  202. #4-1 打印每日持仓信息
  203. def print_position_info(context):
  204. #打印当天成交记录
  205. trades = get_trades()
  206. for _trade in trades.values():
  207. print('成交记录:'+str(_trade))
  208. #打印账户信息
  209. for position in list(context.portfolio.positions.values()):
  210. securities=position.security
  211. cost=position.avg_cost
  212. price=position.price
  213. ret=100*(price/cost-1)
  214. value=position.value
  215. amount=position.total_amount
  216. print('代码:{}'.format(securities))
  217. print('成本价:{}'.format(format(cost,'.2f')))
  218. print('现价:{}'.format(price))
  219. print('收益率:{}%'.format(format(ret,'.2f')))
  220. print('持仓(股):{}'.format(amount))
  221. print('市值:{}'.format(format(value,'.2f')))
  222. print('———————————————————————————————————')