def order_target_value(context, security, value): if value < 0: logger.info("目标价值不能为负,已调整为0") value = 0 today_data = baoStockUtil.get_today_data(context, security) hold_value = context.positions.get(security, 0) * today_data['open'] delta_value = value - hold_value order_value(context, security, delta_value)
def order_target(context, security, amount): if amount < 0: logger.info("目标股数不能为负,已调整为0") amount = 0 today_data = baoStockUtil.get_today_data(context, security) hold_amount = context.positions.get(security, 0) # TODO 卖出没有考虑 T+1 delta_amount = amount - hold_amount _order(context, today_data, security, delta_amount)
def sendMsg(msg, friendName='quantMsg'): # 给好友发送消息 if len(msg.strip()) == 0: return logger.info("发送qq消息: " + msg) setText(msg) hwndQQ = win32gui.FindWindow(None, friendName) # 找到名字为'friendName'的窗口 if hwndQQ == 0: # logger.error('未找到qq对话框') raise Exception('未找到qq对话框 ' + friendName) # return win32gui.SendMessage(hwndQQ, win32con.WM_PASTE, 0, 0) win32gui.SendMessage(hwndQQ, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
def send_data(self, message): if len(message.strip()) == 0: return logger.info("发送微信消息: " + message) send_url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' + self.get_access_token( ) send_values = { "touser": self.TOUSER, "msgtype": "text", "agentid": self.AGENTID, "text": { "content": message }, "safe": "0" } send_msg = (bytes(json.dumps(send_values), 'utf-8')) response = requests.post(send_url, send_msg) response = response.json() # 当返回的数据是json串的时候直接用.json即可将respone转换成字典 if 'ok' != response["errmsg"]: raise Exception('发送微信消息失败:' + response["errmsg"])
def get_stocks_info_from_h5(key='stocks_info'): """ 从h5文件中读取股票信息 :return: df """ result = get_h5_data(key) # 清空h5文件,重新构造 clear_flag = False try: if not timeUtil.is_today( result.iloc[-1]['最新时间']) and timeUtil.is_trade_day(): # 如果从文件里读出的信息不是当日的最新信息,则清空文件内容,这样做是为了每天初始化消息信号的标志位 clear_flag = True except KeyError: # 读取不到最新时间 clear_flag = True if clear_flag: logger.info("清空h5文件,重新构造") result = init_stocks_info() put_h5_data(key, result) return result
def _order(context, today_data, security, amount): operate_flag = "买入" if amount < 0: operate_flag = "卖出" if len(today_data) == 0: logger.info("今日停牌") return # 当前股票价格 p = today_data['open'] if context.cash < amount * p: amount = int(context.cash / p) logger.info("现金不足,已调整为%d股" % amount) # 买卖的数量为100的倍数 if amount % 100 != 0: if amount != -context.positions.get(security, 0): err_amount = amount # 负号为卖出,卖出时不等于当前持有这只股票的总数 amount = int(amount / 100) * 100 # logger.info("%s的数量%s不是100的倍数,已调整为%d股" % (operate_flag, err_amount, amount)) # 卖出的数量大于已持有的数量时 if context.positions.get(security, 0) < -amount: amount = -context.positions.get(security, 0) logger.info("卖出股票不能超过持仓数,已调整为%d股" % amount) logger.info("%s%s股" % (operate_flag, abs(amount))) # 更新持仓信息 context.positions[security] = context.positions.get(security, 0) + amount if context.positions[security] == 0: del context.positions[security] # 更新现金信息 context.cash = context.cash - amount * p # 保留两位小数 context.cash = round(context.cash, 2) logger.info("剩余可用金额:%s" % context.cash)
def strategy_w_shape(code, current_data, history_data, round_=0): logger.info("第%d轮解析%s" % (round_, code)) # 最后一天为买点,要是阳线 if current_data['开盘价'] >= current_data['最新价']: # logger.info(code+"当前是阴线,不符合条件") return code + "当前是阴线,不符合条件" # 当前价格要上穿8日均线 ma8 = dataUtil.get_cur_ma_line(history_data, current_data, 8) if current_data['最新价'] < ma8[-1]: return code + "当前价格在8日均线以下,不符合条件" # 合并k线 history_data = shapeUtil.merge_all_k_line(history_data) # 将今日最新价 追加到history_data后 history_data = dataUtil.fill_today_data(current_data, history_data) # 当天应是底分型的形态 if not shapeUtil.is_bottom_shape(history_data, history_data.iloc[-2].name): return code + "当前不是底分型,不符合条件" # data_range 确定游标范围长度,默认从15开始,因为有三个趋势类型,每个趋势类型至少有5条k线 for data_range in range(15, len(history_data) + 1): range_df = history_data[-data_range:] # 从后往前逐步扩大范围 # logger.info('游标天数=====:' + str(data_range) + " 时间范围: " + range_df.index[0].strftime('%Y-%m-%d') + " - " + # range_df.index[-1].strftime('%Y-%m-%d')) for split in range(4, data_range - 4): # 分割索引从4开始,保证区域内至少有3个值 region1 = range_df[0:split] region2 = range_df[split:] high1_index = region1['high'].idxmax() high1_data = region1.loc[high1_index] min1_index = region1['low'].idxmin() min1_data = region1.loc[min1_index] min2_index = region2['low'].idxmin() min2_data = region2.loc[min2_index] # 倒数第二天应是min2应该在的位置;倒数第一天完成成底分型; min2_temp = history_data.iloc[-2] if min2_temp.name != min2_index: continue # return '未找到符合条件的时间范围' region3 = range_df.loc[min1_index:min2_index] if len(region3) < 3: # 区域3需要掐头去尾掉区域一二的最小值,这里的判断保证区域三内至少有值 continue region3 = region3[1:-1] # 去掉 区域三 头尾的最小值 high2_index = region3['high'].idxmax() high2_data = region3.loc[high2_index] high1 = high1_data['high'] min1 = min1_data['low'] high2 = high2_data['high'] min2 = min2_data['low'] # TODO debug找日期 # if high1_index.strftime('%Y-%m-%d') == '2020-10-16' and min1_index.strftime('%Y-%m-%d') == '2020-12-25' \ # and high2_index.strftime('%Y-%m-%d') == '2021-01-06': # pass # else: # continue # 开始校验==================================================================================================== # 如果极值在起始边界,则为无效数据,跳过下面分型的校验。 if high1_index.strftime('%Y-%m-%d') == range_df.index[0].strftime('%Y-%m-%d') \ or min2_index.strftime('%Y-%m-%d') == range_df.index[-1].strftime('%Y-%m-%d'): continue # 四个极点的顺序 if high1_index < min1_index < high2_index < min2_index: pass else: continue # 确认极点形态 if min1 <= min2 and high1 > high2: # w 形态的四个主要点,高一低一;高二低二 pass else: continue # high1要是整个范围内的最大值 if high1_data['high'] < range_df['high'].max(): continue # min1要是整个范围内的最小值 if min1_data['low'] > range_df['low'].min(): continue # min2要是high2到结束间的最小值 region_high2_end = range_df.iloc[range_df.index.get_loc(high2_index ) + 1:] if min2_data['low'] > region_high2_end['low'].min(): continue # 一顶和一底之间至少有 3 条k线。如果包括两个极点,就是5条k线 if range_df.index.get_loc(min1_index) - range_df.index.get_loc( high1_index) > 3: pass else: continue # 一底和二顶之间至少有 3 条k线。如果包括两个极点,就是5条k线 if range_df.index.get_loc(high2_index) - range_df.index.get_loc( min1_index) > 3: pass else: continue # 二顶和二底之间至少有 3 条k线。如果包括两个极点,就是5条k线 if range_df.index.get_loc(min2_index) - range_df.index.get_loc( high2_index) > 3: pass else: continue # 识别一顶分型 if shapeUtil.is_top_shape(range_df, high1_index): pass else: continue # 识别一底分型 if shapeUtil.is_bottom_shape(range_df, min1_index): pass else: continue # 识别二顶分型 if shapeUtil.is_top_shape(range_df, high2_index): pass else: continue # 识别二底分型 if shapeUtil.is_bottom_shape(range_df, min2_index): pass else: continue # 如果通过前面的校验,则加到结果集 info = code + ' 符合底分型 ' + str(high1_index).replace( ' 00:00:00', ' ') + str(min1_index).replace( ' 00:00:00', ' ') + str(high2_index).replace( ' 00:00:00', ' ') + str(min2_index).replace( ' 00:00:00', ' ') # mplfinance.plot(range_df, type='candle') # result.add(info) logger.info(info) stocks_info = h5Util.get_stocks_info() # 这里单独读取是为了防止其他进程已经改了已有数据 c_data = current_data.copy() c_data['w_shape_flag'] = 'True' stocks_info.loc[code] = c_data # stocks_info.loc[code, 'w_shape_flag'] = 'True' h5Util.put_h5_data("stocks_info", stocks_info) return info return '未找到符合条件的时间范围'
lock = multiprocessing.Manager().Lock() # 创建进程锁 context = get_context() stocks_info = h5Util.get_stocks_info() # 将全部code按每组chunk_len个进行分组,代码分组后,将每组放入进程池分批处理 for code_list in np.array_split(all_code_list, len(all_code_list) / chunk_len + 1): # handle_list(context, code_list, stocks_info) pool.apply_async(generate_signal, (context, code_list, stocks_info, lock, round_)) pool.close() pool.join() # 根据信号发送消息 msgUtil.send_msg_by_signal() if __name__ == '__main__': # handle_data() round_ = 0 # 轮次 while 1: if timeUtil.in_trade_time(time1=vs.STRATEGY_START_TIME, time2=vs.STRATEGY_END_TIME): start_time = datetime.datetime.now() round_ = round_ + 1 handle_data(round_) end_time = datetime.datetime.now() logger.info("完成第" + str(round_) + "轮解析,耗时" + str(round((end_time - start_time).seconds / 60, 2)) + "分钟")