def context_setting(self): """ API trading and quote context setting :returns: trade context, quote context """ if self.unlock_password == "": raise Exception("请先配置交易解锁密码! password: {}".format( self.unlock_password)) quote_ctx = ft.OpenQuoteContext(host=self.api_svr_ip, port=self.api_svr_port) if 'HK.' in self.stock: trade_ctx = ft.OpenHKTradeContext(host=self.api_svr_ip, port=self.api_svr_port) if self.trade_env == ft.TrdEnv.REAL: ret_code, ret_data = trade_ctx.unlock_trade( self.unlock_password) if ret_code == ft.RET_OK: print('解锁交易成功!') else: raise Exception("请求交易解锁失败: {}".format(ret_data)) else: print('解锁交易成功!') elif 'US.' in self.stock: if self.trade_env != 0: raise Exception("美股交易接口不支持仿真环境 trade_env: {}".format( self.trade_env)) trade_ctx = ft.OpenUSTradeContext(host=self.api_svr_ip, port=self.api_svr_port) else: raise Exception("stock输入错误 stock: {}".format(self.stock)) return quote_ctx, trade_ctx
def connectTrade(self): """连接交易功能""" # 连接交易接口 if self.market == 'US': self.tradeCtx = ft.OpenUSTradeContext(self.host, self.port) OrderHandlerBase = USTradeOrderHandlerBase DealHandlerBase = USTradeDealHandlerBase else: self.tradeCtx = ft.OpenHKTradeContext(self.host, self.port) OrderHandlerBase = HKTradeOrderHandlerBase DealHandlerBase = HKTradeDealHandlerBase # 继承实现处理器类 class OrderHandler(OrderHandlerBase): """委托处理器""" gateway = self # 缓存Gateway对象 def on_recv_rsp(self, rsp_str): ret_code, content = super(OrderHandler, self).on_recv_rsp(rsp_str) if ret_code != RET_OK: return RET_ERROR, content self.gateway.processOrder(content) return RET_OK, content class DealHandler(DealHandlerBase): """订单簿处理器""" gateway = self def on_recv_rsp(self, rsp_str): ret_code, content = super(DealHandler, self).on_recv_rsp(rsp_str) if ret_code != RET_OK: return RET_ERROR, content self.gateway.processDeal(content) return RET_OK, content # 只有港股实盘交易才需要解锁 if self.market == 'HK' and self.env == 0: self.tradeCtx.unlock_trade(self.password) # 设置回调处理对象 self.tradeCtx.set_handler(OrderHandler()) self.tradeCtx.set_handler(DealHandler()) # 启动交易接口 self.tradeCtx.start() # 订阅委托推送 self.tradeCtx.subscribe_order_deal_push([], order_deal_push=True, envtype=self.env) self.writeLog(u'交易接口连接成功')
def _process_init_api(self): if type(self._quote_ctx) != int or type(self._trade_ctx) != int: return # 创建futu api对象 if self._quote_ctx == 0: self._quote_ctx = ft.OpenQuoteContext(self._api_ip, self._api_port) if self._trade_ctx == 0: if self._market == MARKET_HK: self._trade_ctx = ft.OpenHKTradeContext( self._api_ip, self._api_port) elif self._market == MARKET_US: self._trade_ctx = ft.OpenUSTradeContext( self._api_ip, self._api_port) else: raise Exception("error param!") if self._env_type == ft.TrdEnv.REAL: ret, _ = self._trade_ctx.unlock_trade(self._trade_password) if 0 != ret: raise Exception("error param!") # 开始futu api异步数据推送 self._quote_ctx.start() self._trade_ctx.start() # 市场状态检查 self._check_market_event = FutuMarketEvent(self._market, self._quote_ctx, self._event_engine) #定阅行情数据 self._futu_data_event = FutuDataEvent(self, self._quote_ctx, self._event_engine, self._tiny_strate.symbol_pools) # 启动事件 self._tiny_strate.on_start()
def make_order_and_cancel(api_svr_ip, api_svr_port, unlock_password, test_code, trade_env, acc_id): """ 使用请先配置正确参数: :param api_svr_ip: (string) ip :param api_svr_port: (string) ip :param unlock_password: (string) 交易解锁密码, 必需修改! :param test_code: (string) 股票 :param trade_env: 参见 ft.TrdEnv的定义 :param acc_id: 交易子账号id """ if unlock_password == "": raise Exception("请先配置交易解锁密码!") quote_ctx = ft.OpenQuoteContext(host=api_svr_ip, port=api_svr_port) # 创建行情api quote_ctx.subscribe(test_code, ft.SubType.ORDER_BOOK) # 定阅摆盘 # 创建交易api is_hk_trade = 'HK.' in test_code if is_hk_trade: trade_ctx = ft.OpenHKTradeContext(host=api_svr_ip, port=api_svr_port) else: trade_ctx = ft.OpenUSTradeContext(host=api_svr_ip, port=api_svr_port) # 每手股数 lot_size = 0 is_unlock_trade = False is_fire_trade = False while not is_fire_trade: sleep(2) # 解锁交易 if not is_unlock_trade and trade_env == ft.TrdEnv.REAL: print("unlocking trade...") ret_code, ret_data = trade_ctx.unlock_trade(unlock_password) is_unlock_trade = (ret_code == ft.RET_OK) if not is_unlock_trade: print("请求交易解锁失败:{}".format(ret_data)) break if lot_size == 0: print("get lotsize...") ret, data = quote_ctx.get_market_snapshot(test_code) lot_size = data.iloc[0]['lot_size'] if ret == ft.RET_OK else 0 if ret != ft.RET_OK: print("取不到每手信息,重试中: {}".format(data)) continue elif lot_size <= 0: raise BaseException("该股票每手信息错误,可能不支持交易 code ={}".format(test_code)) print("get order book...") ret, data = quote_ctx.get_order_book(test_code) # 得到第十档数据 if ret != ft.RET_OK: continue # 计算交易价格 bid_order_arr = data['Bid'] if is_hk_trade: if len(bid_order_arr) != 10: continue # 港股下单: 价格定为第十档 price, _, _ = bid_order_arr[9] else: if len(bid_order_arr) == 0: continue # 美股下单: 价格定为一档降10% price, _, _ = bid_order_arr[0] price = round(price * 0.9, 2) qty = lot_size # 价格和数量判断 if qty == 0 or price == 0.0: continue # 下单 order_id = 0 print("place order : price={} qty={} code={}".format(price, qty, test_code)) ret_code, ret_data = trade_ctx.place_order(price=price, qty=qty, code=test_code, trd_side=ft.TrdSide.BUY, order_type=ft.OrderType.NORMAL, trd_env=trade_env, acc_id=acc_id) is_fire_trade = True print('下单ret={} data={}'.format(ret_code, ret_data)) if ret_code == ft.RET_OK: row = ret_data.iloc[0] order_id = row['order_id'] # 循环撤单 sleep(2) if order_id: while True: ret_code, ret_data = trade_ctx.order_list_query(order_id=order_id, status_filter_list=[], code='', start='', end='', trd_env=trade_env, acc_id=acc_id) if ret_code != ft.RET_OK: sleep(2) continue order_status = ret_data.iloc[0]['order_status'] if order_status in [ft.OrderStatus.SUBMIT_FAILED, ft.OrderStatus.TIMEOUT, ft.OrderStatus.FILLED_ALL, ft.OrderStatus.FAILED, ft.OrderStatus.DELETED]: break print("cancel order...") ret_code, ret_data = trade_ctx.modify_order(modify_order_op=ft.ModifyOrderOp.CANCEL, order_id=order_id, price=price, qty=qty, adjust_limit=0, trd_env=trade_env, acc_id=acc_id) print("撤单ret={} data={}".format(ret_code, ret_data)) if ret_code == ft.RET_OK: break else: sleep(2) # destroy object quote_ctx.close() trade_ctx.close()
quote_ctx.get_stock_basicinfo(market, stock_type='STOCK') # 获取股票信息 quote_ctx.get_history_kline(code, start=None, end=None, ktype='K_DAY', autype='qfq') # 获取历史K线 quote_ctx.get_autype_list(code_list) # 获取复权因子 quote_ctx.get_market_snapshot(code_list) # 获取市场快照 quote_ctx.get_plate_list(market, plate_class) # 获取板块集合下的子板块列表 quote_ctx.get_plate_stock(market, stock_code) # 获取板块下的股票列表 # 高频数据接口 quote_ctx.get_stock_quote(code_list) # 获取报价 quote_ctx.get_rt_ticker(code, num) # 获取逐笔 quote_ctx.get_cur_kline(code, num, ktype=' K_DAY', autype='qfq') # 获取当前K线 quote_ctx.get_order_book(code) # 获取摆盘 quote_ctx.get_rt_data(code) # 获取分时数据 quote_ctx.get_broker_queue(code) # 获取经纪队列 # 实例化港股交易上下文对象 trade_hk_ctx = ft.OpenHKTradeContext(host="127.0.0.1", port=11111) # 实例化美股交易上下文对象 trade_us_ctx = ft.OpenUSTradeContext(host="127.0.0.1", port=11111) # 交易接口列表 ret_code, ret_data = trade_hk_ctx.unlock_trade(password='******') # 解锁接口 ret_code, ret_data = trade_hk_ctx.place_order(price, qty, strcode, orderside, ordertype=0, envtype=0) # 下单接口 ret_code, ret_data = trade_hk_ctx.set_order_status(status, orderid=0, envtype=0) # 设置订单状态 ret_code, ret_data = trade_hk_ctx.change_order(price, qty, orderid=0, envtype=0) # 修改订单 ret_code, ret_data = trade_hk_ctx.accinfo_query(envtype=0) # 查询账户信息 ret_code, ret_data = trade_hk_ctx.order_list_query(statusfilter="", envtype=0) # 查询订单列表 ret_code, ret_data = trade_hk_ctx.position_list_query(envtype=0) # 查询持仓列表 ret_code, ret_data = trade_hk_ctx.deal_list_query(envtype=0) # 查询成交列表
def trailing_stop(api_svr_ip='127.0.0.1', api_svr_port=11111, unlock_password="", code='HK.00700', trade_env=ft.TrdEnv.SIMULATE, method=TrailingMethod.DROP_ABS, drop=1.0, volume=100, how_to_sell=SellMethod.SMART_SELL, diff=0, rest_time=2, enable_email_notification=False, receiver=''): """ 止损策略函数 :param api_svr_ip: (string)ip :param api_svr_port: (int)port :param unlock_password: (string)交易解锁密码, 必需修改! 模拟交易设为一个非空字符串即可 :param code: (string)股票 :param trade_env: ft.TrdEnv.REAL: 真实交易 ft.TrdEnv.SIMULATE: 模拟交易 :param method: method == TrailingMethod.DROP_ABS: 股票下跌drop价格就会止损 railingMethod.DROP_PER: 股票下跌drop的百分比就会止损 :param drop: method == TrailingMethod.DROP_ABS, 股票下跌的价格 method == TrailingMethod.DROP_PER,股票下跌的百分比,0.01表示下跌1%则止损 :param volume: 需要卖掉的股票数量 :param how_to_sell: 以何种方式卖出股票, SellMethod 类型 :param diff: 默认为0,当how_to_sell为SellMethod.DROP_ABS时,以(市价-diff)的价格卖出 :param rest_time: 每隔REST_TIME秒,会检查订单状态, 需要>=2 :param enable_email_notification: 激活email功能 :param receiver: 邮件接收者 """ EmailNotification.set_enable(enable_email_notification) if how_to_sell not in [SellMethod.SIMPLE_SELL, SellMethod.SMART_SELL]: raise Exception('how_to_sell value error') if method not in [TrailingMethod.DROP_ABS, TrailingMethod.DROP_PER]: raise Exception('method value error') quote_ctx = ft.OpenQuoteContext(host=api_svr_ip, port=api_svr_port) is_hk_trade = 'HK.' in code if is_hk_trade: trade_ctx = ft.OpenHKTradeContext(host=api_svr_ip, port=api_svr_port) else: trade_ctx = ft.OpenUSTradeContext(host=api_svr_ip, port=api_svr_port) if unlock_password == "": raise Exception('请先配置交易密码') ret, data = trade_ctx.unlock_trade(unlock_password) if ret != ft.RET_OK: raise Exception('解锁交易失败') ret, data = trade_ctx.position_list_query(trd_env=trd_env) if ret != ft.RET_OK: raise Exception("无法获取持仓列表") try: qty = data[data['code'] == code].iloc[0]['qty'] except: raise Exception('你没有持仓!无法买卖') qty = int(qty) if volume == 0: volume = qty if volume < 0: raise Exception('volume lower than 0') elif qty < volume: raise Exception('持仓不足') ret, data = quote_ctx.get_market_snapshot(code) if ret != ft.RET_OK: raise Exception('获取lot size失败') lot_size = data.iloc[0]['lot_size'] if volume % lot_size != 0: raise Exception('volume 必须是{}的整数倍'.format(lot_size)) ret, data = quote_ctx.subscribe(code, ft.SubType.QUOTE) if ret != ft.RET_OK: raise Exception('订阅QUOTE错误: error {}:{}'.format(ret, data)) ret, data = quote_ctx.subscribe(code, ft.SubType.ORDER_BOOK) if ret != ft.RET_OK: print('error {}:{}'.format(ret, data)) raise Exception('订阅order book失败: error {}:{}'.format(ret, data)) if diff: if is_hk_trade: ret, data = quote_ctx.get_order_book(code) if ret != ft.RET_OK: raise Exception( '获取order book失败: cannot get order book'.format(data)) min_diff = round(abs(data['Bid'][0][0] - data['Bid'][1][0]), 3) if floor(diff / min_diff) * min_diff != diff: raise Exception('diff 应是{}的整数倍'.format(min_diff)) else: if round(diff, 2) != diff: raise Exception('美股价差保留2位小数{}->{}'.format( diff, round(diff, 2))) if method == TrailingMethod.DROP_ABS: if is_hk_trade: if floor(drop / min_diff) * min_diff != drop: raise Exception('drop必须是{}的整数倍'.format(min_diff)) else: if round(drop, 2) != drop: raise Exception('drop必须保留2位小数{}->{}'.format( drop, round(drop, 2))) elif method == TrailingMethod.DROP_PER: if drop < 0 or drop > 1: raise Exception('drop must in [0, 1] if method is DROP_PER') trailing_stop_handler = TrailingStopHandler(quote_ctx, is_hk_trade, method, drop) quote_ctx.set_handler(trailing_stop_handler) quote_ctx.start() while True: if trailing_stop_handler.finished: # sell the stock qty = volume sell_price = trailing_stop_handler.stop while qty > 0: if how_to_sell == SellMethod.SIMPLE_SELL: data = simple_sell(quote_ctx, trade_ctx, code, sell_price - diff, qty, trade_env, ft.OrderType.SPECIAL_LIMIT) else: data = smart_sell(quote_ctx, trade_ctx, code, qty, trade_env, ft.OrderType.SPECIAL_LIMIT) if data is None: print('下单失败') EmailNotification.send_email( receiver, '下单失败', '股票代码{},数量{}'.format(code, volume)) sleep(rest_time) continue order_id = data.iloc[0]['order_id'] sleep(rest_time) while True: ret, data = trade_ctx.order_list_query(order_id=order_id, trd_env=trade_env) if ret != ft.RET_OK: sleep(rest_time) continue status = data.iloc[0]['order_status'] dealt_qty = int(data.iloc[0]['dealt_qty']) order_price = data.iloc[0]['price'] qty -= dealt_qty if status == ft.OrderStatus.FILLED_ALL: print('全部成交:股票代码{}, 成交总数{},价格{}'.format( code, dealt_qty, order_price)) EmailNotification.send_email( receiver, '全部成交', '股票代码{},成交总数{},价格{}'.format( code, dealt_qty, order_price)) break elif status == ft.OrderStatus.FILLED_PART: print('部分成交:股票代码{},成交总数{},价格{}'.format( code, dealt_qty, order_price)) EmailNotification.send_email( receiver, '部分成交', '股票代码{},成交总数{},价格{}'.format( code, dealt_qty, order_price)) break elif status == ft.OrderStatus.FAILED or status == ft.OrderStatus.SUBMIT_FAILED or \ status == ft.OrderStatus.CANCELLED_ALL or status == ft.OrderStatus.DELETED: break else: trade_ctx.modify_order(ft.ModifyOrderOp.CANCEL, order_id, 0, 0) sleep(rest_time) continue if how_to_sell == SellMethod.SIMPLE_SELL: ret, data = quote_ctx.get_order_book(code) if ret != ft.RET_OK: raise Exception('获取order_book失败') sell_price = data['Bid'][0][0] # draw price and stop price_lst = trailing_stop_handler.price_lst plt.plot(np.arange(len(price_lst)), price_lst) stop_list = trailing_stop_handler.stop_lst plt.plot(np.arange(len(stop_list)), stop_list) break quote_ctx.close() trade_ctx.close()