separate_warehouse.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. # https://www.joinquant.com/algorithm/index/edit?algorithmId=4519676386ce59c6457da930b52d0e7c&startTime=2015-07-01&endTime=2016-04-06&baseCapital=100000&frequency=day&pyVersion=2
  2. # 克隆自聚宽文章:https://www.joinquant.com/post/1079
  3. # 标题:多策略组合利器——分仓管控技术【非交易策略】
  4. # 作者:莫邪的救赎
  5. def initialize(context):
  6. # 获取股票
  7. df = get_fundamentals(query(
  8. valuation.code,valuation.market_cap
  9. ).order_by(
  10. valuation.market_cap.asc()
  11. ).limit(
  12. 200
  13. )).dropna()
  14. g.security = list(df['code'])
  15. g.buylist = g.security[-100:]
  16. set_universe(g.security)
  17. log.set_level('order', 'error')#屏蔽order warning提示
  18. g.stocknum = 5 #仓位数
  19. g.per_sell_stock = [] #欲卖出股票列表
  20. g.proportion_initial_buy_cash = 0.6 #建仓资金占总资金的比例
  21. g.proportion_cash = 0.4 #预留补仓现金比例占总资金的比例
  22. #(0.6代表用仓位总资金的六成用于建仓,其余四成用于加仓)
  23. initialize_position_cash(context) #初始化仓位个数及资金
  24. # 执行函数
  25. run_daily(buy_stocks, time='open')
  26. run_daily(overweight_and_stop_profit, time='open')
  27. run_daily(stop_loss, time='open')
  28. run_daily(change_position_cash, time='after_close')
  29. def before_trading_start(context):
  30. #获取前日持仓股票列表
  31. g.already_hold_stock = context.portfolio.positions.keys()
  32. log.info('buylist长度:%s',len(g.buylist))
  33. def initialize_position_cash(context):
  34. '''
  35. 初始化每个仓位的信息
  36. '''
  37. start_cash = context.portfolio.starting_cash
  38. every_position = start_cash/g.stocknum
  39. g.hold_temp = {'total':every_position, 'initial_buy_cash':every_position*g.proportion_initial_buy_cash, 'cash':every_position*g.proportion_cash}
  40. g.hold = {} # 仓位信息
  41. g.hold_stock_name = {} # 仓位对于的股票名称
  42. for i in range(g.stocknum):
  43. g.hold[i] = g.hold_temp.copy()
  44. g.hold_stock_name[i] = None #仓位为空,则对应的value值为None
  45. # log.info('init hold:',g.hold)
  46. # log.info('hold_stock_name:',g.hold_stock_name)
  47. def buy_stocks(context):
  48. current_data = get_current_data(g.buylist)
  49. for stock in g.buylist:
  50. if current_data[stock].paused == 0:#跳过停牌
  51. for n in g.hold_stock_name.items():
  52. if n[1] == None:
  53. g.hold_stock_name[n[0]] = stock #标记仓位对应的股票
  54. Cash = g.hold[n[0]]['initial_buy_cash'] #获取建仓资金
  55. order_value(stock, Cash) #建仓
  56. # log.info('buy: %s',stock)
  57. g.buylist.remove(stock)
  58. break
  59. else:
  60. pass
  61. else:
  62. g.buylist.remove(stock)
  63. # log.info('hold_stock_name:',g.hold_stock_name)
  64. def overweight_and_stop_profit(context):
  65. '''
  66. 加仓以及止盈
  67. '''
  68. hold_stock = context.portfolio.positions.keys()
  69. if len(hold_stock)>0:
  70. current_data = get_current_data(hold_stock)
  71. for stock in hold_stock:
  72. #跳过停牌,因为T+1。所以sellable_amount>0即跳过当日建仓的股票
  73. if current_data[stock].paused == 0 and context.portfolio.positions[stock].sellable_amount>0:
  74. avg_cost = context.portfolio.positions[stock].avg_cost #持仓成本
  75. price = context.portfolio.positions[stock].price #持仓股票当前价
  76. # 根据触发条件,对不在“欲卖出”列表的股票进行加仓,降低持仓成本
  77. if ((price/avg_cost) <= 0.9) and (stock not in g.per_sell_stock):
  78. log.info("overweight: %s", stock) #打印加仓股票代码
  79. i = get_key(stock) #获取加仓股票对应的key值,get_key函数见下
  80. Cash = get_buy_cash(i) #获取用于加仓资金,get_buy_cash函数见下
  81. order_value(stock, Cash) #买入
  82. # log.info('buy: %s',stock)
  83. # 止盈
  84. elif (price/avg_cost) >= 1.2:
  85. order_target(stock, 0)
  86. log.info('sell: %s', stock)
  87. # 如果股票在“欲卖出”列表中,则将其删除
  88. if stock in g.per_sell_stock:
  89. g.per_sell_stock.remove(stock)
  90. else:
  91. pass
  92. def get_key(stock):
  93. '''
  94. 获取stock在g.hold_stock_name对应的key值
  95. '''
  96. for n in g.hold_stock_name.items():
  97. if n[1] == stock:
  98. return n[0]
  99. def get_buy_cash(i):
  100. '''
  101. 获取g.hold中i键对应仓位的可用购买现金,
  102. 这里设定:将剩余四成仓位分两次购买
  103. (如需更改购买现金占比,请自行修改)
  104. 并在现金用尽之后,将股票加入“欲卖出”股票列表,如下跌找过一定比例,则进行止损
  105. '''
  106. all_cash = g.hold[i]['cash'] #仓位中可用现金
  107. total = g.hold[i]['total'] #仓位初始总资金
  108. if all_cash/total > 0.3:
  109. cash = all_cash*0.5
  110. return cash
  111. elif all_cash/total < 0.3:
  112. will_sell_stock = g.hold_stock_name[i]
  113. if will_sell_stock not in g.per_sell_stock:
  114. g.per_sell_stock.append(will_sell_stock)
  115. cash = all_cash
  116. return cash
  117. def stop_loss(context):
  118. '''
  119. “欲卖出”股票列表,如下跌找过一定比例,则进行止损
  120. '''
  121. if len(g.per_sell_stock)>0:
  122. current_data = get_current_data(g.per_sell_stock)
  123. for stock in g.per_sell_stock:
  124. if current_data[stock].paused == 0: #跳过停牌
  125. avg_cost = context.portfolio.positions[stock].avg_cost
  126. price = context.portfolio.positions[stock].price
  127. if (price/avg_cost) <= 0.9:
  128. order_target(stock, 0)
  129. g.per_sell_stock.remove(stock)
  130. log.info('sell: %s', stock)
  131. else:
  132. pass
  133. else:
  134. pass
  135. def change_position_cash(context):
  136. '''
  137. I. 更新每个仓位的可用现金
  138. II. 将已卖掉的股票现金仓位进行资金再平衡。
  139. (这里只是将当前空余的仓位进行了资金再平衡
  140. 如,有N支仓位空余,及重新平均分配N支仓位的资金)
  141. '''
  142. g.selled_list_keys = [] #已卖掉的股票keys
  143. g.rebalance_total_money = 0 #再分仓的总金额
  144. trades=get_orders() #过去当天订单
  145. hold_stock = context.portfolio.positions
  146. for t in trades.values():
  147. # 更新每个仓位的可用现金
  148. if t.is_buy and t.filled > 0: #买入有效订单
  149. if (t.security not in g.already_hold_stock) and (hold_stock[t.security].sellable_amount > 0):
  150. i = get_key(t.security)
  151. # g.hold[i]['initial_buy_cash'] = t.cash
  152. g.hold[i]['cash'] = g.hold[i]['total'] - t.cash #更新可用现金
  153. elif (t.security in g.already_hold_stock) and (hold_stock[t.security].sellable_amount > 0):
  154. i = get_key(t.security)
  155. # g.hold[i]['initial_buy_cash'] = g.hold[i]['cash'] + t.cash
  156. g.hold[i]['cash'] = g.hold[i]['cash'] - t.cash #更新可用现金
  157. # 将已卖掉的股票现金仓位进行资金再平衡。
  158. elif not t.is_buy and t.filled > 0:#卖出有效订单
  159. if t.security not in context.portfolio.positions.keys():
  160. i = get_key(t.security)
  161. g.selled_list_keys.append(i)
  162. g.rebalance_total_money += t.cash
  163. # 资金重分配函数,如需隔离仓位,则不用执行该函数
  164. if len(g.selled_list_keys) > 0:
  165. rebalance_money(g.selled_list_keys, g.rebalance_total_money)
  166. # 打印结果(用于调试)
  167. # log.info('hold:',g.hold)
  168. log.info('hold_stock_name:',g.hold_stock_name)
  169. log.info('per_sell_stock:',g.per_sell_stock)
  170. def rebalance_money(selled_list_keys, rebalance_total_money):
  171. '''
  172. 资金重分配函数
  173. 如需隔离仓位,则不用执行该函数
  174. '''
  175. # 获取再分仓的总金额
  176. for i in selled_list_keys:
  177. rebalance_total_money += g.hold[i]['cash']
  178. # 确定每个仓位的总金额
  179. every_position = rebalance_total_money/len(selled_list_keys)
  180. # 资金重分配
  181. for i in selled_list_keys:
  182. g.hold_stock_name[i] = None
  183. g.hold[i]['total'] = every_position
  184. g.hold[i]['initial_buy_cash'] = every_position*g.proportion_initial_buy_cash
  185. g.hold[i]['cash'] = every_position*g.proportion_cash