def __init__(self, account_id, symbol, donchian_channel_open_position=20, donchian_channel_stop_profit=10, atr_day_length=20): self.account_id = account_id self.symbol = symbol # 合约代码 self.donchian_channel_open_position = donchian_channel_open_position # 唐奇安通道的天数周期(开仓) self.donchian_channel_stop_profit = donchian_channel_stop_profit # 唐奇安通道的天数周期(止盈) self.atr_day_length = atr_day_length # ATR计算所用天数 self.state = { "position": 0, # 本策略净持仓数(正数表示多头,负数表示空头,0表示空仓) "last_price": float("nan"), # 上次调仓价 } self.n = 0 self.unit = 0 self.donchian_channel_high = 0 self.donchian_channel_low = 0 self.api = TqApi(self.account_id) self.quote = self.api.get_quote(self.symbol) # 由于ATR是路径依赖函数,因此使用更长的数据序列进行计算以便使其值稳定下来 kline_length = max(donchian_channel_open_position + 1, donchian_channel_stop_profit + 1, atr_day_length * 5) self.klines = self.api.get_kline_serial(self.symbol, 24 * 60 * 60, data_length=kline_length) self.account = self.api.get_account() self.target_pos = TargetPosTask(self.api, self.symbol, init_pos=self.state["position"])
def parity_quote_task(self, opt: dict, future_id: str, future_target_pos): call_quote = self.api.get_quote(opt['c']) put_quote = self.api.get_quote(opt['p']) future_quote = self.api.get_quote(future_id) #future_target_pos = TargetPosTask(self.api, future_id) call_target_pos = TargetPosTask(self.api, opt['c']) put_target_pos = TargetPosTask(self.api, opt['p']) put_position = self.api.get_position(opt['p']) self._put_vols[opt['K']] = put_position.pos self.api.create_task( self.quote_watcher(future_quote, opt['K'], call_quote, put_quote, future_target_pos, call_target_pos, put_target_pos)) print('Quote subscribed ' + str(opt))
def subscribe_main_parity( api, future_symbol, future_product_id, option_product_id, long_call, short_call, min_strike, max_strike, return_threshold=0.1, max_margin=4000, ): """ 对某个基础资产进行put-call parity异步套利。 """ # 取主力合约的信息 kq_m = api.get_quote("KQ.m@" + future_symbol) # 用主力合约的symbol订阅报价 future = api.get_quote(kq_m.underlying_symbol) # 初始化期货-期权信息 opt_api = TqOption( api, underlying_future_id=kq_m.underlying_symbol, option_product_id=option_product_id, ) # 按照行权价范围,选择需要的期权合约组合 _, opts = opt_api.get_future_opt_symbols( strike_year=future.delivery_year, #行权年 strike_month=future.delivery_month, #行权月 min_strike=min_strike, #最小行权价 max_strike=max_strike, #最大行权价 ) # 设定开仓信号:put-call parity residual的开仓临界值 trade = OptionTrade( api, opt_api, kq_m.underlying_symbol, #主力合约的symbol opts, can_trade=True, #是否支持交易 save_data=False, #是否存储数据 long_call_threshold=long_call, #开仓方案一:买call、卖put、空期货的开仓临界值,可None long_put_threshold=short_call, #开仓方案一:买put、卖call、多期货的开仓临界值,可None return_threshold=return_threshold, #开仓方案二:按照理论收益率(=残差/保证金占用)来开仓,可None max_margin=max_margin, #最大保证金占用,可None ) global trade_dict # 使用future_symbol可以查询这组put-call parity arbitrage对象 trade_dict[future_product_id] = trade print("期货代码:" + kq_m.underlying_symbol) future_target_pos = TargetPosTask(api, kq_m.underlying_symbol) # 订阅所有的期权合约套利组 for opt in opts.values(): trade.parity_quote_task(opt, kq_m.underlying_symbol, future_target_pos)
def trade_group(self, strike_price: float, future_vol: int, future: TargetPosTask, call: TargetPosTask, put: TargetPosTask): """ 交易期权、期货组合套利 Args: future_vol > 0, 对应long call + short put + short期货 """ call.set_target_volume(future_vol * self._option_multiplier) put.set_target_volume(-future_vol * self._option_multiplier) self._put_vols[strike_price] = -future_vol * self._option_multiplier future_target_vol = int( sum(self._put_vols.values()) / self._option_multiplier) future.set_target_volume(future_target_vol)
__author__ = 'yanqiong' from tqsdk.api import TqApi from tqsdk.lib import TargetPosTask ''' 连续3根阴线就做空,连续3根阳线就做多,否则空仓 ''' api = TqApi("SIM") # 设定连续多少根阳线/阴线 length = 3 # 获得 rb1901 10秒K线的引用, 长度为 length+1 klines = api.get_kline_serial("SHFE.rb1901", 10, data_length = length + 1) # 创建 rb1901 的目标持仓 task,该 task 负责调整 rb1901 的仓位到指定的目标仓位, offset_priority的用法详见文档 target_pos = TargetPosTask(api, "SHFE.rb1901", offset_priority="今昨开") while True: api.wait_update() # 只有在新创建出K线时才判断开平仓条件 if api.is_changing(klines[-1], "datetime"): # 将K线转为pandas.DataFrame, 跳过最后一根刚生成的K线 df = klines.to_dataframe()[:-1] # 比较收盘价和开盘价,判断是阳线还是阴线 # df["close"] 为收盘价序列, df["open"] 为开盘价序列, ">"(pandas.Series.gt) 返回收盘价是否大于开盘价的一个新序列 up = df["close"] > df["open"] down = df["close"] < df["open"] if all(up): print("连续阳线: 目标持仓 多头1手") # 设置目标持仓为正数表示多头,负数表示空头,0表示空仓 target_pos.set_target_volume(1)
class Turtle: def __init__(self, account_id, symbol, donchian_channel_open_position=20, donchian_channel_stop_profit=10, atr_day_length=20): self.account_id = account_id self.symbol = symbol # 合约代码 self.donchian_channel_open_position = donchian_channel_open_position # 唐奇安通道的天数周期(开仓) self.donchian_channel_stop_profit = donchian_channel_stop_profit # 唐奇安通道的天数周期(止盈) self.atr_day_length = atr_day_length # ATR计算所用天数 self.state = { "position": 0, # 本策略净持仓数(正数表示多头,负数表示空头,0表示空仓) "last_price": float("nan"), # 上次调仓价 } self.n = 0 self.unit = 0 self.donchian_channel_high = 0 self.donchian_channel_low = 0 self.api = TqApi(self.account_id) self.quote = self.api.get_quote(self.symbol) # 由于ATR是路径依赖函数,因此使用更长的数据序列进行计算以便使其值稳定下来 kline_length = max(donchian_channel_open_position + 1, donchian_channel_stop_profit + 1, atr_day_length * 5) self.klines = self.api.get_kline_serial(self.symbol, 24 * 60 * 60, data_length=kline_length) self.account = self.api.get_account() self.target_pos = TargetPosTask(self.api, self.symbol, init_pos=self.state["position"]) def recalc_paramter(self): try: df = self.klines.to_dataframe() # 本交易日的平均真实波幅(N值) self.n = talib.ATR(df["high"], df["low"], df["close"], timeperiod=self.atr_day_length).iloc[-1] # 买卖单位 self.unit = int((self.account["balance"] * 0.01) / (self.quote["volume_multiple"] * self.n)) print('atr:', self.n, " unit:", self.unit) # 唐奇安通道上轨:前N个交易日的最高价 self.donchian_channel_high = max( self.klines.high[-self.donchian_channel_open_position - 1:-1]) # 唐奇安通道下轨:前N个交易日的最低价 self.donchian_channel_low = min( self.klines.low[-self.donchian_channel_open_position - 1:-1]) print("唐其安通道上下轨:", self.donchian_channel_high, self.donchian_channel_low) except Exception: # 若尚未接收到数据, 即数据为NaN, 则在ATR或unit计算时会报错 return False return True def set_position(self, pos): self.state["position"] = pos self.state["last_price"] = self.quote["last_price"] self.target_pos.set_target_volume(self.state["position"]) def try_open(self): """开仓策略""" while self.state["position"] == 0: self.api.wait_update() if self.api.is_changing(self.klines[-1], "datetime"): # 如果产生新k线,则重新计算唐奇安通道及买卖单位 self.recalc_paramter() if self.api.is_changing(self.quote, "last_price"): print("最新价: ", self.quote["last_price"]) if self.quote[ "last_price"] > self.donchian_channel_high: # 当前价>唐奇安通道上轨,买入1个Unit;(持多仓) print("当前价>唐奇安通道上轨,买入1个Unit(持多仓):", self.unit, "手") self.set_position(self.state["position"] + self.unit) elif self.quote[ "last_price"] < self.donchian_channel_low: # 当前价<唐奇安通道下轨,卖出1个Unit;(持空仓) print("当前价<唐奇安通道下轨,卖出1个Unit(持空仓):", self.unit, "手") self.set_position(self.state["position"] - self.unit) def try_close(self): """交易策略""" while self.state["position"] != 0: self.api.wait_update() if self.api.is_changing(self.quote, "last_price"): print("最新价: ", self.quote["last_price"]) if self.state["position"] > 0: # 持多单 # 加仓策略: 如果是多仓且资产的价格在上一次建仓(或者加仓)的基础上又上涨了0.5N,就再加一个Unit的多仓 if self.quote["last_price"] >= self.state[ "last_price"] + 0.5 * self.n: print("加仓:加1个Unit的多仓") self.set_position(self.state["position"] + self.unit) # 止损策略: 如果是多仓且资产的价格在上一次建仓(或者加仓)的基础上又下跌了2N,就卖出全部头寸止损 elif self.quote["last_price"] <= self.state[ "last_price"] - 2 * self.n: print("止损:卖出全部头寸") self.set_position(0) # 止盈策略: 如果是多仓且当前资产价格跌破了10日唐奇安通道的下轨,就清空所有头寸结束策略,离场 if self.quote["last_price"] <= min( self.klines. low[-self.donchian_channel_stop_profit - 1:-1]): print("止盈:清空所有头寸结束策略,离场") self.set_position(0) elif self.state["position"] < 0: # 持空单 # 加仓策略: 如果是空仓且资产的价格在上一次建仓(或者加仓)的基础上又下跌了0.5N,就再加一个Unit的空仓 if self.quote["last_price"] <= self.state[ "last_price"] - 0.5 * self.n: print("加仓:加1个Unit的空仓") self.set_position(self.state["position"] - self.unit) # 止损策略: 如果是空仓且资产的价格在上一次建仓(或者加仓)的基础上又上涨了2N,就平仓止损 elif self.quote["last_price"] >= self.state[ "last_price"] + 2 * self.n: print("止损:卖出全部头寸") self.set_position(0) # 止盈策略: 如果是空仓且当前资产价格升破了10日唐奇安通道的上轨,就清空所有头寸结束策略,离场 if self.quote["last_price"] >= max( self.klines. high[-self.donchian_channel_stop_profit - 1:-1]): print("止盈:清空所有头寸结束策略,离场") self.set_position(0) def strategy(self): """海龟策略""" print("等待K线及账户数据...") deadline = time.time() + 5 while not self.recalc_paramter(): if not self.api.wait_update(deadline=deadline): raise Exception("获取数据失败,请确认行情连接正常并已经登录交易账户") while True: self.try_open() self.try_close()
sSetup = pivot + (high - low) # 观察卖出价 sEnter = 2 * pivot - low # 反转卖出价 bEnter = 2 * pivot - high # 反转买入价 bSetup = pivot - (high - low) # 观察买入价 sBreak = low - 2 * (high - pivot) # 突破卖出价 print("已计算新标志线: ", "\n枢轴点", pivot, "\n突破买入价", bBreak, "\n观察卖出价", sSetup, "\n反转卖出价", sEnter, "\n反转买入价", bEnter, "\n观察买入价", bSetup, "\n突破卖出价", sBreak) return pivot, bBreak, sSetup, sEnter, bEnter, bSetup, sBreak api = TqApi("SIM") quote = api.get_quote(symbol) klines = api.get_kline_serial(symbol, 24*60*60) # 86400: 使用日线 position = api.get_position(symbol) target_pos = TargetPosTask(api, symbol) target_pos_value = position["volume_long"] - position["volume_short"] # 净目标净持仓数 open_position_price = position["open_price_long"] if target_pos_value > 0 else position["open_price_short"] # 开仓价 pivot, bBreak, sSetup, sEnter, bEnter, bSetup, sBreak = get_index_line(klines) # 七条标准线 while True: target_pos.set_target_volume(target_pos_value) api.wait_update() if api.is_changing(klines[-1], "datetime"): # 产生新k线,则重新计算7条指标线 pivot, bBreak, sSetup, sEnter, bEnter, bSetup, sBreak = get_index_line(klines) nowTime = quote["datetime"].split()[1].split(":") # 当前行情时间: [时,分,秒] if int(nowTime[0]) == close_hour and int(nowTime[1]) >= close_minute: # 到达平仓时间: 平仓 break '''交易规则'''
#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = 'chengzhi' from tqsdk.api import TqApi from tqsdk.lib import TargetPosTask ''' 价差回归 当近月-远月的价差大于200时做空近月,做多远月 当价差小于150时平仓 ''' api = TqApi("SIM") quote_near = api.get_quote("SHFE.rb1810") quote_deferred = api.get_quote("SHFE.rb1901") # 创建 rb1810 的目标持仓 task,该 task 负责调整 rb1810 的仓位到指定的目标仓位 target_pos_near = TargetPosTask(api, "SHFE.rb1810") # 创建 rb1901 的目标持仓 task,该 task 负责调整 rb1901 的仓位到指定的目标仓位 target_pos_deferred = TargetPosTask(api, "SHFE.rb1901") while True: api.wait_update() if api.is_changing(quote_near) or api.is_changing(quote_deferred): spread = quote_near["last_price"] - quote_deferred["last_price"] print("当前价差:", spread) if spread > 200: print("目标持仓: 空近月,多远月") # 设置目标持仓为正数表示多头,负数表示空头,0表示空仓 target_pos_near.set_target_volume(-1) target_pos_deferred.set_target_volume(1) elif spread < 150: print("目标持仓: 空仓")
· MA120 之下: - MA10 下穿 MA20 ,死叉,做空 - MA10 上穿 MA20 ,金叉,平空 """ # 实盘交易 # api = TqApi(TqAccount("G光大期货","[账号]","[密码]"), web_gui=True) # 回测模式 from datetime import date api = TqApi(backtest=TqBacktest(date(2019, 7, 1), date(2019, 12, 1)), web_gui=True) # 策略初始化 symbol = "CFFEX.IF1912" klines = api.get_kline_serial(symbol, 60 * 15) # 订阅15分钟K线序列 target_pos = TargetPosTask(api, symbol) try: while True: api.wait_update() if api.is_changing(klines): # MA120的最新值 ma120 = ma(klines.close, 120).iloc[-1] # MA10上穿MA20的bool序列的最新值 up_cross = crossup(ma(klines.close, 10), ma(klines.close, 20)).iloc[-1] # MA10下穿MA20的bool序列的最新值 down_cross = crossdown(ma(klines.close, 10), ma(klines.close, 20)).iloc[-1] # 如果最新K线收盘价在MA120上方 if klines.close.iloc[-1] > ma120: # 如果MA10上穿MA20,开一手
#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = 'chengzhi' from tqsdk.api import TqApi from tqsdk.lib import TargetPosTask ''' 如果当前价格大于10秒K线的MA15则开多仓 如果小于则平仓 ''' api = TqApi("SIM") # 获得 m1901 10秒K线的引用 klines = api.get_kline_serial("DCE.m1901", 10) # 创建 m1901 的目标持仓 task,该 task 负责调整 m1901 的仓位到指定的目标仓位 target_pos = TargetPosTask(api, "DCE.m1901") while True: api.wait_update() if api.is_changing(klines): ma = sum(klines.close[-15:]) / 15 print("最新价", klines.close[-1], "MA", ma) if klines.close[-1] > ma: print("最新价大于MA: 目标多头5手") # 设置目标持仓为多头5手 target_pos.set_target_volume(5) elif klines.close[-1] < ma: print("最新价小于MA: 目标空仓") # 设置目标持仓为空仓 target_pos.set_target_volume(0)
HH = max(klines.high[-Nday - 1:-1]) # N日最高价的最高价 HC = max(klines.close[-Nday - 1:-1]) # N日收盘价的最高价 LC = min(klines.close[-Nday - 1:-1]) # N日收盘价的最低价 LL = min(klines.low[-Nday - 1:-1]) # N日最低价的最低价 range = max(HH - LC, HC - LL) buy_line = current_open + range * K1 # 上轨 sell_line = current_open - range * K2 # 下轨 print("当前开盘价:", current_open, "\n上轨:", buy_line, "\n下轨:", sell_line) return buy_line, sell_line api = TqApi("SIM") quote = api.get_quote(symbol) klines = api.get_kline_serial(symbol, 24 * 60 * 60) # 86400使用日线 target_pos = TargetPosTask(api, symbol) buy_line, sell_line = dual_thrust(quote, klines) # 获取上下轨 while True: api.wait_update() if api.is_changing(klines[-1], "datetime") or api.is_changing( quote, "open"): # 新产生一根日线或开盘价发生变化: 重新计算上下轨 buy_line, sell_line = dual_thrust(quote, klines) if api.is_changing(quote, "last_price"): print("最新价变化", quote["last_price"], end=':') if quote["last_price"] > buy_line: # 高于上轨 print("高于上轨,目标持仓 多头3手") target_pos.set_target_volume(3) # 交易 elif quote["last_price"] < sell_line: # 低于下轨 print("低于下轨,目标持仓 空头3手")