# https://www.joinquant.com/algorithm/index/edit?algorithmId=4519676386ce59c6457da930b52d0e7c&startTime=2015-07-01&endTime=2016-04-06&baseCapital=100000&frequency=day&pyVersion=2 # 克隆自聚宽文章:https://www.joinquant.com/post/1079 # 标题:多策略组合利器——分仓管控技术【非交易策略】 # 作者:莫邪的救赎 def initialize(context): # 获取股票 df = get_fundamentals(query( valuation.code,valuation.market_cap ).order_by( valuation.market_cap.asc() ).limit( 200 )).dropna() g.security = list(df['code']) g.buylist = g.security[-100:] set_universe(g.security) log.set_level('order', 'error')#屏蔽order warning提示 g.stocknum = 5 #仓位数 g.per_sell_stock = [] #欲卖出股票列表 g.proportion_initial_buy_cash = 0.6 #建仓资金占总资金的比例 g.proportion_cash = 0.4 #预留补仓现金比例占总资金的比例 #(0.6代表用仓位总资金的六成用于建仓,其余四成用于加仓) initialize_position_cash(context) #初始化仓位个数及资金 # 执行函数 run_daily(buy_stocks, time='open') run_daily(overweight_and_stop_profit, time='open') run_daily(stop_loss, time='open') run_daily(change_position_cash, time='after_close') def before_trading_start(context): #获取前日持仓股票列表 g.already_hold_stock = context.portfolio.positions.keys() log.info('buylist长度:%s',len(g.buylist)) def initialize_position_cash(context): ''' 初始化每个仓位的信息 ''' start_cash = context.portfolio.starting_cash every_position = start_cash/g.stocknum g.hold_temp = {'total':every_position, 'initial_buy_cash':every_position*g.proportion_initial_buy_cash, 'cash':every_position*g.proportion_cash} g.hold = {} # 仓位信息 g.hold_stock_name = {} # 仓位对于的股票名称 for i in range(g.stocknum): g.hold[i] = g.hold_temp.copy() g.hold_stock_name[i] = None #仓位为空,则对应的value值为None # log.info('init hold:',g.hold) # log.info('hold_stock_name:',g.hold_stock_name) def buy_stocks(context): current_data = get_current_data(g.buylist) for stock in g.buylist: if current_data[stock].paused == 0:#跳过停牌 for n in g.hold_stock_name.items(): if n[1] == None: g.hold_stock_name[n[0]] = stock #标记仓位对应的股票 Cash = g.hold[n[0]]['initial_buy_cash'] #获取建仓资金 order_value(stock, Cash) #建仓 # log.info('buy: %s',stock) g.buylist.remove(stock) break else: pass else: g.buylist.remove(stock) # log.info('hold_stock_name:',g.hold_stock_name) def overweight_and_stop_profit(context): ''' 加仓以及止盈 ''' hold_stock = context.portfolio.positions.keys() if len(hold_stock)>0: current_data = get_current_data(hold_stock) for stock in hold_stock: #跳过停牌,因为T+1。所以sellable_amount>0即跳过当日建仓的股票 if current_data[stock].paused == 0 and context.portfolio.positions[stock].sellable_amount>0: avg_cost = context.portfolio.positions[stock].avg_cost #持仓成本 price = context.portfolio.positions[stock].price #持仓股票当前价 # 根据触发条件,对不在“欲卖出”列表的股票进行加仓,降低持仓成本 if ((price/avg_cost) <= 0.9) and (stock not in g.per_sell_stock): log.info("overweight: %s", stock) #打印加仓股票代码 i = get_key(stock) #获取加仓股票对应的key值,get_key函数见下 Cash = get_buy_cash(i) #获取用于加仓资金,get_buy_cash函数见下 order_value(stock, Cash) #买入 # log.info('buy: %s',stock) # 止盈 elif (price/avg_cost) >= 1.2: order_target(stock, 0) log.info('sell: %s', stock) # 如果股票在“欲卖出”列表中,则将其删除 if stock in g.per_sell_stock: g.per_sell_stock.remove(stock) else: pass def get_key(stock): ''' 获取stock在g.hold_stock_name对应的key值 ''' for n in g.hold_stock_name.items(): if n[1] == stock: return n[0] def get_buy_cash(i): ''' 获取g.hold中i键对应仓位的可用购买现金, 这里设定:将剩余四成仓位分两次购买 (如需更改购买现金占比,请自行修改) 并在现金用尽之后,将股票加入“欲卖出”股票列表,如下跌找过一定比例,则进行止损 ''' all_cash = g.hold[i]['cash'] #仓位中可用现金 total = g.hold[i]['total'] #仓位初始总资金 if all_cash/total > 0.3: cash = all_cash*0.5 return cash elif all_cash/total < 0.3: will_sell_stock = g.hold_stock_name[i] if will_sell_stock not in g.per_sell_stock: g.per_sell_stock.append(will_sell_stock) cash = all_cash return cash def stop_loss(context): ''' “欲卖出”股票列表,如下跌找过一定比例,则进行止损 ''' if len(g.per_sell_stock)>0: current_data = get_current_data(g.per_sell_stock) for stock in g.per_sell_stock: if current_data[stock].paused == 0: #跳过停牌 avg_cost = context.portfolio.positions[stock].avg_cost price = context.portfolio.positions[stock].price if (price/avg_cost) <= 0.9: order_target(stock, 0) g.per_sell_stock.remove(stock) log.info('sell: %s', stock) else: pass else: pass def change_position_cash(context): ''' I. 更新每个仓位的可用现金 II. 将已卖掉的股票现金仓位进行资金再平衡。 (这里只是将当前空余的仓位进行了资金再平衡 如,有N支仓位空余,及重新平均分配N支仓位的资金) ''' g.selled_list_keys = [] #已卖掉的股票keys g.rebalance_total_money = 0 #再分仓的总金额 trades=get_orders() #过去当天订单 hold_stock = context.portfolio.positions for t in trades.values(): # 更新每个仓位的可用现金 if t.is_buy and t.filled > 0: #买入有效订单 if (t.security not in g.already_hold_stock) and (hold_stock[t.security].sellable_amount > 0): i = get_key(t.security) # g.hold[i]['initial_buy_cash'] = t.cash g.hold[i]['cash'] = g.hold[i]['total'] - t.cash #更新可用现金 elif (t.security in g.already_hold_stock) and (hold_stock[t.security].sellable_amount > 0): i = get_key(t.security) # g.hold[i]['initial_buy_cash'] = g.hold[i]['cash'] + t.cash g.hold[i]['cash'] = g.hold[i]['cash'] - t.cash #更新可用现金 # 将已卖掉的股票现金仓位进行资金再平衡。 elif not t.is_buy and t.filled > 0:#卖出有效订单 if t.security not in context.portfolio.positions.keys(): i = get_key(t.security) g.selled_list_keys.append(i) g.rebalance_total_money += t.cash # 资金重分配函数,如需隔离仓位,则不用执行该函数 if len(g.selled_list_keys) > 0: rebalance_money(g.selled_list_keys, g.rebalance_total_money) # 打印结果(用于调试) # log.info('hold:',g.hold) log.info('hold_stock_name:',g.hold_stock_name) log.info('per_sell_stock:',g.per_sell_stock) def rebalance_money(selled_list_keys, rebalance_total_money): ''' 资金重分配函数 如需隔离仓位,则不用执行该函数 ''' # 获取再分仓的总金额 for i in selled_list_keys: rebalance_total_money += g.hold[i]['cash'] # 确定每个仓位的总金额 every_position = rebalance_total_money/len(selled_list_keys) # 资金重分配 for i in selled_list_keys: g.hold_stock_name[i] = None g.hold[i]['total'] = every_position g.hold[i]['initial_buy_cash'] = every_position*g.proportion_initial_buy_cash g.hold[i]['cash'] = every_position*g.proportion_cash