def aberration(self): ''' Aberration策略 (难度:初级) 参考: https://www.shinnytech.com/blog/aberration/ 注: 该示例策略仅用于功能示范, 实盘时请根据自己的策略/经验进行修改 ''' # 获取合约信息 api = self.api symbol = self.symbol quote = api.get_quote(symbol) klines = api.get_kline_serial(symbol, 60 * 60 * 24) position = api.get_position(symbol) target_pos = TargetPosTask(api, symbol) # 使用BOLL指标计算中轨、上轨和下轨,其中26为周期N ,2为参数p def boll_line(klines): boll = BOLL(klines, 26, 2) midline = boll["mid"].iloc[-1] topline = boll["top"].iloc[-1] bottomline = boll["bottom"].iloc[-1] print("策略运行,中轨:%.2f,上轨为:%.2f,下轨为:%.2f" % (midline, topline, bottomline)) return midline, topline, bottomline midline, topline, bottomline = boll_line(klines) while True: api.wait_update() # 每次生成新的K线时重新计算BOLL指标 if api.is_changing(klines.iloc[-1], "datetime"): midline, topline, bottomline = boll_line(klines) # 每次最新价发生变化时进行判断 if api.is_changing(quote, "last_price"): # 判断开仓条件 if position.pos_long == 0 and position.pos_short == 0: # 如果最新价大于上轨,K线上穿上轨,开多仓 if quote.last_price > topline: print("K线上穿上轨,开多仓") target_pos.set_target_volume(20) # 如果最新价小于轨,K线下穿下轨,开空仓 elif quote.last_price < bottomline: print("K线下穿下轨,开空仓") target_pos.set_target_volume(-20) else: print("当前最新价%.2f,未穿上轨或下轨,不开仓" % quote.last_price) # 在多头情况下,空仓条件 elif position.pos_long > 0: # 如果最新价低于中线,多头清仓离场 if quote.last_price < midline: print("最新价低于中线,多头清仓离场") target_pos.set_target_volume(0) else: print("当前多仓,未穿越中线,仓位无变化") # 在空头情况下,空仓条件 elif position.pos_short > 0: # 如果最新价高于中线,空头清仓离场 if quote.last_price > midline: print("最新价高于中线,空头清仓离场") target_pos.set_target_volume(0) else: print("当前空仓,未穿越中线,仓位无变化")
def __init__(self, symbol, account=None, donchian_channel_open_position=20, donchian_channel_stop_profit=10, atr_day_length=20, max_risk_ratio=0.5): self.account = account # 交易账号 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.max_risk_ratio = max_risk_ratio # 最高风险度 self.state = { "position": 0, # 本策略净持仓数(正数表示多头,负数表示空头,0表示空仓) "last_price": float("nan"), # 上次调仓价 } self.n = 0 # 平均真实波幅(N值) self.unit = 0 # 买卖单位 self.donchian_channel_high = 0 # 唐奇安通道上轨 self.donchian_channel_low = 0 # 唐奇安通道下轨 self.api = TqApi(self.account) 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)
def __init__(self, symbol): Process.__init__(self) self.symbol = symbol self.rsi_long = 50 + self.rsi_signal self.rsi_short = 50 - self.rsi_signal self.api = TqApi(TqSim(init_balance=50000)) self.now = datetime.now() self.target_pos = TargetPosTask(self.api, self.symbol) self.ticks = self.api.get_tick_serial(self.symbol) self.klines5 = self.api.get_kline_serial(self.symbol, 60 * 5) self.klines15 = self.api.get_kline_serial(self.symbol, 60 * 15) self.position = self.api.get_position(self.symbol)
def __init__(self, api, symbol): self.api = api self.symbol = symbol self.account_position = self.api.get_position(self.symbol) self.target_pos = TargetPosTask(self.api, self.symbol) self.TAG = "PositionManager" self.bid_price = 0 self.ask_price = 0 self.MAX_POS = 2 self.init_data()
async def price_watcher(SYMBOL): """该task在价格触发开仓价时开仓,触发平仓价时平仓""" klines = api.get_kline_serial(SYMBOL, duration_seconds=5 * 60) target_pos = TargetPosTask(api, SYMBOL) async with api.register_update_notify( klines) as update_price_chan: # 当 quote 有更新时会发送通知到 update_chan 上 while True: async for _ in update_price_chan: # 当从 update_chan 上收到行情更新通知时判断是否触发开仓条件 ys = pd.Series(data=klines.close[-100:-1], index=[ str(dt.datetime.fromtimestamp(i / 1e9)) for i in klines.datetime[-100:-1] ]) target_pos.set_target_volume(1) print(SYMBOL, '价格')
async def signal_generator(SYMBOL, strategy): """该task应用策略在价格触发时开仓,出发平仓条件时平仓""" klines = api.get_kline_serial(SYMBOL, duration_seconds=15 * 60) position = api.get_position(SYMBOL) target_pos = TargetPosTask(api, SYMBOL) async with api.register_update_notify(klines) as update_klines_chan: while True: async for _ in update_klines_chan: k15 = str(dt.datetime.fromtimestamp(klines.datetime[-1] / 1e9)) print(SYMBOL, '信号时间', k15) ys = pd.Series(data=klines.close[-100:-1], index=[ str(dt.datetime.fromtimestamp(i / 1e9)) for i in klines.datetime[-100:-1] ]) macd, macdsignal, macdhist = strategy(ys) target_pos_value = position['volume_long'] - position[ 'volume_short'] if target_pos_value == 0: if (macd[-2] < macdsignal[-2]) and (macd[-1] > macdsignal[-1]): target_pos_value = 5 target_pos.set_target_volume(target_pos_value) print(SYMBOL, "上涨做多", '时间:', k15) if (macd[-2] > macdsignal[-2]) and (macd[-1] < macdsignal[-1]): target_pos_value = -5 target_pos.set_target_volume(target_pos_value) print(SYMBOL, "下跌做空", '时间:', k15) break if (target_pos_value > 0 and macdhist[-2] > 0 and (macdhist[-1] < macdhist[-2])) or \ (target_pos_value < 0 and macdhist[-2] < 0 and (macdhist[-1] > macdhist[-2])): target_pos.set_target_volume(0) print(SYMBOL, '止损平仓', '时间:', k15) break
async def close_win(SYMBOL): """该task应用策略在价格触发止盈条件时平仓""" quote = api.get_quote(SYMBOL) position = api.get_position(SYMBOL) target_pos = TargetPosTask(api, SYMBOL) async with api.register_update_notify(quote) as update_quote_chan: while True: async for _ in update_quote_chan: target_pos = TargetPosTask(api, SYMBOL) target_pos_value = position['volume_long'] - position[ 'volume_short'] print(SYMBOL, target_pos_value, "时间:", quote["datetime"]) if (target_pos_value > 0 and quote["last_price"] - position["open_price_long"] >= 2) or \ (target_pos_value < 0 and (position["open_price_short"] - quote["last_price"]) >= 2): target_pos.set_target_volume(0) print(SYMBOL, '止盈平仓')
def huanYue(): global lastmonth, quote, now, klinesH1, position, target_pos if True: target_pos.set_target_volume(0) #自定义换月规则 yue = int(now.month / 3 + 1) * 3 #根据需要修改代码,此示例为03,06,09,12季月 if yue == 15: huanyue = str(now.year + 1)[2:] + "03" else: huanyue = str(now.year)[2:] + "0" + str(yue) if yue < 10 else str( now.year)[2:] + str(yue) SYMBOL = SYMBOL_ZhuLian[5:] + huanyue print("当前月", now.month, ", 换月", huanyue, ", 合约", SYMBOL, "\n") quote = api.get_quote(SYMBOL) now = datetime.strptime(quote.datetime, "%Y-%m-%d %H:%M:%S.%f") lastmonth = now.month klinesH1 = api.get_kline_serial(SYMBOL, 3600, data_length=4) # position = api.get_position(SYMBOL) lot = position.pos_long - position.pos_short # 净目标净持仓数 target_pos = TargetPosTask(api, SYMBOL)
def trend_line(self, kline_level="day", atr_n=14, MA_n=40): ''' 趋势线策略 ''' # profit_space_switch = { # "day":lambda n: ATR(self.api.get_kline_serial(self.symbol, 60*60*24), n), # "hour":lambda n: ATR(self.api.get_kline_serial(self.symbol, 60*60), n) # } # loss_space_switch = { # "day":lambda n: ATR(self.api.get_kline_serial(self.symbol, 60*60), n), # "hour":lambda n: ATR(self.api.get_kline_serial(self.symbol, 5*60), n) # } # loss_space = loss_space_switch[self.kline_level](atr_n) # profit_space = profit_space_switch[self.kline_level](atr_n) trend = 0 # 1 上升趋势,0 震荡, -1 下降趋势 quote = self.api.get_quote(self.symbol) target = TargetPosTask(self.api, self.symbol) if kline_level == "day": loss_atr_period = 60 * 60 profit_atr_period = 24 * 60 * 60 trend_period = 24 * 60 * 60 trend_kline = self.api.get_kline_serial(self.symbol, trend_period) elif kline_level == "week": loss_atr_period = 60 * 60 profit_atr_period = 7 * 24 * 60 * 60 trend_period = 24 * 60 * 60 loss_space = ATR( self.api.get_kline_serial(self.symbol, loss_atr_period), atr_n) profit_space = ATR( self.api.get_kline_serial(self.symbol, profit_atr_period), atr_n) if trend_kline.iloc[-1].close > MA( trend_kline, 40).iloc[-1] + 2 * ATR(trend_kline, atr_n): trend = 1 while True: pass
def macd_trade(self): while True: self.api.wait_update() klines = self.kwargs['klines'] quote = self.kwargs['quote'] if self.api.is_changing(quote, "last_price"): macd = MACD(klines, 12, 26, 9) diff = list(macd["diff"])[-1] dea = list(macd["dea"])[-1] key = tools.createKey(self.instrumentId) position = self.checkPosition(self.instrumentId) crossup = tafunc.crossup(macd["diff"], macd["dea"]) crossdown = tafunc.crossdown(macd["diff"], macd["dea"]) target_pos = TargetPosTask(self.api, self.instrumentId) # 创建一个自动调仓工具 # 会有问题,再该分钟,和上一分钟,一直都会是下穿,或上穿状态 # 随着行情推送,会一直有交易指示 if list(crossup)[-1] == 1 and self.crossupflag: logger.info("上穿") self.crossupflag = False self.crossdownflag = True # 如果有仓位,先平仓,再开仓 if position.pos < 0: # 平仓 print(" SELL close %d" % position.pos) target_pos.set_target_volume(0) if not position.pos < 0: trade_volume = self.controlPosition(quote, 0.06) order = self.insertOrder(self.instrumentId, "BUY", "OPEN", trade_volume, quote.ask_price1) conn.lpush(key, order.__dict__) logger.info(order) elif list(crossdown)[-1] == 1 and self.crossdownflag: logger.info("下穿") self.crossupflag = True self.crossdownflag = False if position.pos > 0: # 平仓 logger.info(" BUY close %d" % abs(position.pos)) target_pos.set_target_volume(0) if not position.pos > 0: trade_volume = self.controlPosition(quote, 0.06) self.insertOrder(self.instrumentId, "SELL", "OPEN", trade_volume, quote.bid_price1)
def run_tianqin_code(port): try: api = TqApi(backtest=TqBacktest(start_dt=date(2018, 5, 5), end_dt=date(2018, 5, 10)), web_gui="127.0.0.1:" + port) klines = api.get_kline_serial("DCE.m1901", 5 * 60, data_length=15) target_pos = TargetPosTask(api, "DCE.m1901") while True: api.wait_update() if api.is_changing(klines): ma = sum(klines.close.iloc[-15:]) / 15 if klines.close.iloc[-1] > ma: target_pos.set_target_volume(5) elif klines.close.iloc[-1] < ma: target_pos.set_target_volume(0) except BacktestFinished as e: while True: api.wait_update() except Exception as e: api.close()
def test_lib_insert_order_time_check_6(self): ''' lib下单时间判断测试6 测试: 设置目标持仓后在TargetPosTask未下单前调整目标持仓, lib等到10:30有行情之后调整到的是最新目标持仓 ''' # 预设服务器端响应 dir_path = os.path.dirname(os.path.realpath(__file__)) self.mock.run( os.path.join(dir_path, "log_file", "test_lib_insert_order_time_check_6.script.lzma")) utils.RD = random.Random(4) api = TqApi(backtest=TqBacktest(start_dt=datetime.datetime( 2019, 7, 11, 10, 15), end_dt=datetime.date(2019, 7, 12)), _ins_url=self.ins_url_2019_12_04, _td_url=self.td_url, _md_url=self.md_url) symbol1 = "SHFE.cu1908" symbol2 = "CFFEX.IF1908" # 用于行情推进,到10:20 quote2 = api.get_quote(symbol2) target_pos = TargetPosTask(api, symbol1) orders = api.get_order() position = api.get_position(symbol1) try: target_pos.set_target_volume(5) while quote2.datetime < "2019-07-11 10:20:00.000000": api.wait_update() self.assertEqual(len(api.get_order()), 0) target_pos.set_target_volume(2) while quote2.datetime < "2019-07-11 10:25:00.000000": api.wait_update() self.assertEqual(len(api.get_order()), 0) while True: api.wait_update() except BacktestFinished: self.assertEqual(len(orders), 1) self.assertEqual(position.pos, 2) api.close()
def run_tianqin_code(port, queue): try: ins_url = "http://127.0.0.1:5000/t/md/symbols/2019-07-03.json" api = TqApi(backtest=TqBacktest(start_dt=date(2019, 7, 10), end_dt=date(2019, 7, 20)), _ins_url=ins_url, web_gui="127.0.0.1:" + port) queue.put("webready") klines = api.get_kline_serial("DCE.m1912", 5 * 60, data_length=15) target_pos = TargetPosTask(api, "DCE.m1912") while True: api.wait_update() if api.is_changing(klines): ma = sum(klines.close.iloc[-15:]) / 15 if klines.close.iloc[-1] > ma: target_pos.set_target_volume(5) elif klines.close.iloc[-1] < ma: target_pos.set_target_volume(0) except BacktestFinished as e: while True: api.wait_update() except Exception as e: api.close()
ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # 输出到console的log等级的开关 # 第三步,定义handler的输出格式 formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s") fh.setFormatter(formatter) ch.setFormatter(formatter) # 第四步,将logger添加到handler里面 logger.addHandler(fh) logger.addHandler(ch) api = TqApi(TqSim()) #time_slot_start = datetime.time(START_HOUR, START_MINUTE) # 计划交易时段起始时间点 #time_slot_end = datetime.time(END_HOUR, END_MINUTE) # 计划交易时段终点时间点 klines = api.get_kline_serial(SYMBOL, TIME_CELL, data_length=int(10 * 60 * 60 / TIME_CELL)) target_pos = TargetPosTask(api, SYMBOL) position = api.get_position(SYMBOL) # 持仓信息 quote = api.get_quote(SYMBOL) logger.info("start %s daily strategy for %s!"%(c, SYMBOL)) current_volume = 0 # 记录持仓量 traded_volume = 0 cur_trading_date = '' # 交易预警参数 k_count = 0 signal_interval = 10 short_price = 0.0 sum_profit = 0.0
class Turtle: def __init__(self, account, symbol, donchian_channel_open_position=20, donchian_channel_stop_profit=10, atr_day_length=20, max_risk_ratio=0.5): self.account = account # 交易账号 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.max_risk_ratio = max_risk_ratio # 最高风险度 self.state = { "position": 0, # 本策略净持仓数(正数表示多头,负数表示空头,0表示空仓) "last_price": float("nan"), # 上次调仓价 } self.n = 0 # 平均真实波幅(N值) self.unit = 0 # 买卖单位 self.donchian_channel_high = 0 # 唐奇安通道上轨 self.donchian_channel_low = 0 # 唐奇安通道下轨 self.api = TqApi(self.account) 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): # 平均真实波幅(N值) self.n = ATR(self.klines, self.atr_day_length)["atr"].iloc[-1] # 买卖单位 self.unit = int((self.account.balance * 0.01) / (self.quote.volume_multiple * self.n)) # 唐奇安通道上轨:前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("唐其安通道上下轨: %f, %f" % (self.donchian_channel_high, self.donchian_channel_low)) 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.iloc[-1], "datetime"): # 如果产生新k线,则重新计算唐奇安通道及买卖单位 self.recalc_paramter() if self.api.is_changing(self.quote, "last_price"): print("最新价: %f" % self.quote.last_price) if self.quote.last_price > self.donchian_channel_high: # 当前价>唐奇安通道上轨,买入1个Unit;(持多仓) print("当前价>唐奇安通道上轨,买入1个Unit(持多仓): %d 手" % self.unit) self.set_position(self.state["position"] + self.unit) elif self.quote.last_price < self.donchian_channel_low: # 当前价<唐奇安通道下轨,卖出1个Unit;(持空仓) print("当前价<唐奇安通道下轨,卖出1个Unit(持空仓): %d 手" % 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 and self.account.risk_ratio <= self.max_risk_ratio: 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 and self.account.risk_ratio <= self.max_risk_ratio: 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()
from tqsdk import TqReplay from tqsdk.tafunc import ma data_length = 200 # k线数据长度 SYMBOL = "SHFE.rb2005" # 合约代码 api = TqApi(TqAccount("simnow", "090828", "jimc1230",front_broker='9999',front_url='tcp://180.168.146.187:10100'),web_gui="0.0.0.0:9876")#web_gui="0.0.0.0:9876" # api = TqApi(backtest=TqBacktest(start_dt=date(2020, 3,10), end_dt=date(2020, 3, 19)),web_gui="0.0.0.0:9876")#"0.0.0.0:9876"#回测模式 # api = TqApi(backtest = TqReplay(date(2020,3,18)),web_gui="0.0.0.0:9876")#复盘模式 print("策略开始运行") # "duration_seconds=60"为一分钟线, 日线的duration_seconds参数为: 24*60*60 klines = api.get_kline_serial(SYMBOL, duration_seconds=1*60, data_length=data_length) #收盘后不可查 target_pos = TargetPosTask(api, SYMBOL) position = api.get_position(SYMBOL)#指定一个品种查看持仓相关信息 account = api.get_account()#获取用户账户资金信息 while True: api.wait_update() if api.is_changing(klines.iloc[-1], "datetime"): # 产生新k线:重新计算SMA klines["ma5"]=ma(klines["close"], 5) klines["ma10"]=ma(klines["close"], 10) klines["ma89"]=ma(klines["close"], 89) klines["ma144"]=ma(klines["close"], 144) sar=SAR(klines, 4, 0.02, 0.2) if api.is_changing(klines.iloc[-1], "datetime"): # 产生新k线:重新计算SMA
pivot = (high + low + close) / 3 # 枢轴点 bBreak = high + 2 * (pivot - low) # 突破买入价 sSetup = pivot + (high - low) # 观察卖出价 sEnter = 2 * pivot - low # 反转卖出价 bEnter = 2 * pivot - high # 反转买入价 bSetup = pivot - (high - low) # 观察买入价 sBreak = low - 2 * (high - pivot) # 突破卖出价 print("已计算新标志线, 枢轴点: %f, 突破买入价: %f, 观察卖出价: %f, 反转卖出价: %f, 反转买入价: %f, 观察买入价: %f, 突破卖出价: %f" % (pivot, bBreak, sSetup, sEnter, bEnter, bSetup, sBreak)) return pivot, bBreak, sSetup, sEnter, bEnter, bSetup, sBreak 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.pos_long - position.pos_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.iloc[-1], "datetime"): # 产生新k线,则重新计算7条指标线 pivot, bBreak, sSetup, sEnter, bEnter, bSetup, sBreak = get_index_line(klines) if api.is_changing(quote, "datetime"): now = datetime.strptime(quote.datetime, "%Y-%m-%d %H:%M:%S.%f") if now.hour == CLOSE_HOUR and now.minute >= CLOSE_MINUTE: # 到达平仓时间: 平仓 print("临近本交易日收盘: 平仓") target_pos_value = 0 # 平仓
class CuatroStrategy(Process): '''''' author = 'XIAO LI' boll_window = 20 boll_dev = 1.8 rsi_window = 14 rsi_signal = 20 fast_window = 4 slow_window = 26 trailing_long = 0.5 trailing_short = 0.3 vol = 1 boll_up = float('nan') boll_down = float('nan') rsi_value = float('nan') rsi_long = float('nan') rsi_short = float('nan') fast_ma = float('nan') slow_ma = float('nan') ma_trend = float('nan') intra_trade_high = float('nan') intra_trade_low = float('nan') long_stop = float('nan') short_stop = float('nan') parameters = [ 'boll_window' 'boll_dev' 'rsi_window' 'rsi_signal' 'fast_window' 'slow_window' 'trailing_long' 'trailing_short' 'vol' ] variables = [ 'boll_up' 'boll_down' 'rsi_value' 'rsi_long' 'rsi_short' 'fast_ma' 'slow_ma' 'ma_trend' 'intra_trade_high' 'intra_trade_low ' 'long_stop' 'short_stop' ] def __init__(self, symbol): Process.__init__(self) self.symbol = symbol self.rsi_long = 50 + self.rsi_signal self.rsi_short = 50 - self.rsi_signal self.api = TqApi(TqSim(init_balance=50000)) self.now = datetime.now() self.target_pos = TargetPosTask(self.api, self.symbol) self.ticks = self.api.get_tick_serial(self.symbol) self.klines5 = self.api.get_kline_serial(self.symbol, 60 * 5) self.klines15 = self.api.get_kline_serial(self.symbol, 60 * 15) self.position = self.api.get_position(self.symbol) def on_init(self): print(self.now, '策略初始化') def on_start(self): print(self.now, '策略启动') def on_stop(self): print(self.now, '策略停止') def on_tick(self, ticks): if self.api.is_changing(ticks, 'datetime'): if self.position.pos_long == 0 and self.position.pos_short == 0: if self.ma_trend > 0 and self.rsi_value >= self.rsi_long and ticks.iloc[-1].last_price > self.boll_up: self.target_pos.set_target_volume(self.vol) self.intra_trade_high = ticks.iloc[-1].last_price if self.ma_trend < 0 and self.rsi_value <= self.rsi_short and ticks.iloc[ -1].last_price < self.boll_down: self.target_pos.set_target_volume(-self.vol) self.intra_trade_low = ticks.iloc[-1].last_price elif self.position.pos_long > 0: self.intra_trade_high = max(self.intra_trade_high, ticks.iloc[-1].last_price) self.long_stop = (self.intra_trade_high - self.trailing_long * (self.boll_up - self.boll_down)) if ticks.iloc[-1].last_price < self.long_stop: self.target_pos.set_target_volume(0) self.intra_trade_high = float('nan') else: self.intra_trade_low = min(self.intra_trade_low, ticks.iloc[-1].last_price) self.short_stop = (self.intra_trade_low + self.trailing_short * (self.boll_up - self.boll_down)) if ticks.iloc[-1].last_price > self.short_stop: self.target_pos.set_target_volume(0) self.intra_trade_low = float('nan') def on_5minbar(self, klines5): if self.api.is_changing(klines5, 'datetime'): boll = ta.BOLL(klines5.iloc[:-1], self.boll_window, self.boll_dev).iloc[-1] self.boll_up = boll['top'] self.boll_down = boll['bottom'] self.rsi_value = ta.RSI(klines5.iloc[:-1], self.rsi_window).iloc[-1]['rsi'] def on_15minbar(self, klines15): if self.api.is_changing(klines15, 'datetime'): self.fast_ma = ta.SMA(klines15.iloc[:-1], self.fast_window, 2) self.slow_ma = ta.SMA(klines15.iloc[:-1], self.slow_window, 2) if self.fast_ma > self.slow_ma: self.ma_trend = 1 elif self.fast_ma < self.slow_ma: self.ma_trend = -1 else: self.ma_trend = 0 def on_order(self): if self.api.is_changing(self.api.get_order()): pass def on_trade(self): if self.api.is_changing(self.api.get_trade()): pass def run(self): self.on_init() self.on_start() while True: self.api.wait_update() self.on_tick(self.ticks) self.on_5minbar(self.klines5) self.on_15minbar(self.klines15) self.on_order() self.on_trade() self.on_stop()
class PositionManger(object): def __init__(self, api, symbol): self.api = api self.symbol = symbol self.account_position = self.api.get_position(self.symbol) self.target_pos = TargetPosTask(self.api, self.symbol) self.TAG = "PositionManager" self.bid_price = 0 self.ask_price = 0 self.MAX_POS = 2 self.init_data() def init_data(self): self.runing_pos = 0 # self.account_hold_pos = self.target_pos.pos_long + self.target_pos.pos_short self.account_hold_pos = self.account_position.pos_long_today + self.account_position.pos_short_today self.run_flag = True self.entry_time = 0 self.exit_time = 0 self.strategy_pos = 0 self.keep_pos = 0 self.count_limit_flag = False #不确定set_position 会不会取消order self.close_setup = 0 # 0 撤掉order;平仓 def init(self, pos): self.strategy_pos = pos has_pos = self.get_hold_position() if (has_pos != self.strategy_pos): self.target_pos.set_target_volume(self.strategy_pos) def run(self): if self.api.is_changing(self.account_position): print("今多头: %d 手" % (self.account_position.pos_long_today)) print("今空头: %d 手" % (self.account_position.pos_short_today)) def new_day(self): self.init_data() def get_hold_position(self): position = self.api.get_position(self.symbol) pos_1 = position.pos_long - position.pos_short pos_2 = position.pos if (pos_1 != pos_2): print("[%s] pos_long=%d pos_short=%d pos=%d" % (self.TAG, position.pos_long, position.pos_short, pos_2)) return pos_2 ''' def get_runing_position(self): position = self.api.get_position(self.symbol) return position.pos_long + position.pos_short ''' def refresh(self): #position = self.api.get_position("SHFE.rb2005") if (self.run_flag == False): return def buy(self): if (self.run_flag == False): return self.entry_time = get_current_minute_bar() def buy_to_cover(self): if (self.run_flag == False): return def sell_to_short(self): if (self.run_flag == False): return self.entry_time = get_current_minute_bar() def sell(self): if (self.run_flag == False): return def order_buy(self): if (self.run_flag == False): return def order_buy_to_cover(self): if (self.run_flag == False): return def order_sell_to_short(self): if (self.run_flag == False): return def order_sell(self): if (self.run_flag == False): return def get_entry_time(self): return get_current_minute_bar - self.entry_time def get_trade_done_time(self): #trade = self.api.get_trade() pass #position = api.get_position("DCE.m1809") def get_profit(self): self.account_position = self.api.get_position(self.symbol) profit = self.account_position.float_profit_long + self.account_position.float_profit_short #print(profit) if (profit != profit): return 0 else: return int(profit) def get_balance(self): account = self.api.get_account() balance = account.balance return int(balance) #持仓盈亏 def get_position_profit(self): #self.account_position = self.api.get_position(self.symbol) #return self.account_position.position_profit account = self.api.get_account() return int(account.position_profit) #本交易日内平仓盈亏 def get_close_profit(self): #self.account_position = self.api.get_position(self.symbol) #return self.account_position.close_profit account = self.api.get_account() return int(account.close_profit) def get_last_trade_type(self): pass def get_exit_time(self): pass def cancel_all_orders(self): orders = self.api.get_order() print(orders) if (orders): for oid, order in orders.items(): if (order.status == "ALIVE"): self.api.cancel_order(order) def start(self): self.run_flag = True th1 = threading.Thread(target=PositionManger.monitor_thread, args=(self, )) th1.start() def stop(self): self.run_flag = False def close(self): if (self.run_flag == True): if (self.close_setup == 0): print("[%s] cancel all_orders", self.TAG) self.cancel_all_orders() self.close_setup = 1 return else: print("[%s] close.....", self.TAG) self.target_pos.set_target_volume(self.keep_pos) self.run_flag = False self.strategy_pos = 0 self.logger.info(self.TAG, "close....self.keep_pos=%d" % (self.keep_pos)) print("[%s] close self.keep_pos=%d" % (self.TAG, self.keep_pos)) else: return def close2(self): if (self.run_flag == True): print("[%s] cancel all_orders", self.TAG) self.cancel_all_orders() self.target_pos.set_target_volume(0) self.run_flag = False self.strategy_pos = 0 self.logger.info(self.TAG, "close2....") print("[%s] close2", self.TAG) else: return def set_position(self, pos): lt = time.localtime(time.time()) if (((lt.tm_hour == 14 and lt.tm_min >= 56) or self.run_flag == False) and pos != 0): return self.strategy_pos = pos if (self.count_limit_flag == False): self.target_pos.set_target_volume(self.strategy_pos) else: if (self.strategy_pos >= self.MAX_POS): self.target_pos.set_target_volume(self.MAX_POS) elif (self.strategy_pos <= -self.MAX_POS): self.target_pos.set_target_volume(-self.MAX_POS) else: self.target_pos.set_target_volume(0) def check(self): localtime = time.localtime(time.time()) if (self.close_setup >= 1): return if (localtime.tm_min % 5 == 0): account_position = self.api.get_position() if (account_position.pos_long + account_position.pos_short != self.strategy_pos): self.target_pos.set_target_volume(self.strategy_pos) if (localtime.tm_hour == 14 and localtime.tm_min >= 58): self.shutdown_flag = True account_position = self.api.get_position() if (account_position.pos_long != 0): self.target_pos.set_target_volume(0) self.strategy_pos = 0 def set_keep_position(self, pos): self.keep_pos = pos def set_logger(self, logger): self.logger = logger def init_trade_orders(self, direction, pos, trade_date): pass def monitor_thread(self): pass
''' 连续3根阴线就做空,连续3根阳线就做多,否则空仓 ''' from tqsdk import TqApi, TargetPosTask api = TqApi() # 设定连续多少根阳线/阴线 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) elif all(down): print("连续阴线: 目标持仓 空头1手") target_pos.set_target_volume(-1)
#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = 'chengzhi' from tqsdk import TqApi, TqAuth, TargetPosTask ''' 价差回归 当近月-远月的价差大于250时做空近月,做多远月 当价差小于200时平仓 ''' api = TqApi(auth=TqAuth("信易账户", "账户密码")) quote_near = api.get_quote("SHFE.rb2104") quote_deferred = api.get_quote("SHFE.rb2105") # 创建 rb2104 的目标持仓 task,该 task 负责调整 rb2104 的仓位到指定的目标仓位 target_pos_near = TargetPosTask(api, "SHFE.rb2104") # 创建 rb2105 的目标持仓 task,该 task 负责调整 rb2105 的仓位到指定的目标仓位 target_pos_deferred = TargetPosTask(api, "SHFE.rb2105") 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 > 250: print("目标持仓: 空近月,多远月") # 设置目标持仓为正数表示多头,负数表示空头,0表示空仓 target_pos_near.set_target_volume(-1) target_pos_deferred.set_target_volume(1) elif spread < 200: print("目标持仓: 空仓") target_pos_near.set_target_volume(0)
from datetime import date from contextlib import closing from tqsdk import TqApi, TqSim, TqBacktest, TargetPosTask ''' 如果当前价格大于5分钟K线的MA15则开多仓 如果小于则平仓 回测从 2018-05-01 到 2018-10-01 ''' # 在创建 api 实例时传入 TqBacktest 就会进入回测模式 api = TqApi(TqSim(), backtest=TqBacktest(start_dt=date(2018, 5, 1), end_dt=date(2018, 10, 1))) # 获得 m1901 5分钟K线的引用 klines = api.get_kline_serial("DCE.m1901", 5 * 60, data_length=15) # 创建 m1901 的目标持仓 task,该 task 负责调整 m1901 的仓位到指定的目标仓位 target_pos = TargetPosTask(api, "DCE.m1901") # 使用with closing机制确保回测完成后释放对应的资源 with closing(api): while True: api.wait_update() if api.is_changing(klines): ma = sum(klines.close.iloc[-15:]) / 15 print("最新价", klines.close.iloc[-1], "MA", ma) if klines.close.iloc[-1] > ma: print("最新价大于MA: 目标多头5手") # 设置目标持仓为多头5手 target_pos.set_target_volume(5) elif klines.close.iloc[-1] < ma: print("最新价小于MA: 目标空仓") # 设置目标持仓为空仓
#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = 'chengzhi' from datetime import date from tqsdk import TqApi, TqReplay, TargetPosTask ''' 复盘 2019-11-12 如果当前价格大于5分钟K线的MA15则开多仓,如果小于则平仓 ''' # 在创建 api 实例时传入 TqReplay 就会进入复盘模式, 同时打开 web_gui api = TqApi(backtest=TqReplay(date(2019, 11, 12)), web_gui=True) # 获得 m1901 5分钟K线的引用 klines = api.get_kline_serial("SHFE.cu2001", 5 * 60, data_length=15) # 创建 m1901 的目标持仓 task,该 task 负责调整 m1901 的仓位到指定的目标仓位 target_pos = TargetPosTask(api, "SHFE.cu2001") while True: api.wait_update() if api.is_changing(klines): ma = sum(klines.close.iloc[-15:]) / 15 print("最新价", klines.close.iloc[-1], "MA", ma) if klines.close.iloc[-1] > ma: print("最新价大于MA: 目标多头5手") # 设置目标持仓为多头5手 target_pos.set_target_volume(5) elif klines.close.iloc[-1] < ma: print("最新价小于MA: 目标空仓") # 设置目标持仓为空仓 target_pos.set_target_volume(0)
# 在创建 api 实例时传入 TqBacktest 就会进入回测模式 api = TqApi(acc, backtest=TqBacktest(start_dt=date(2018, 2, 1), end_dt=date(2018, 5, 1)), web_gui='http://127.0.0.1:8889') # kind1 = 'SHFE.cu1901' kind2 = 'SHFE.cu1902' # 获得 m1901 5分钟K线的引用 klines1 = api.get_kline_serial([kind1], 1 * 30, data_length=8000) klines2 = api.get_kline_serial([kind2], 1 * 30, data_length=8000) quote1 = api.get_quote(kind1) quote2 = api.get_quote(kind2) # # 创建 m1901 的目标持仓 task,该 task 负责调整 m1901 的仓位到指定的目标仓位 target_pos1 = TargetPosTask(api, kind1) target_pos2 = TargetPosTask(api, kind2) now1 = datetime.datetime.strptime(quote1.datetime, "%Y-%m-%d %H:%M:%S.%f") # 当前quote的时间 now2 = datetime.datetime.strptime(quote2.datetime, "%Y-%m-%d %H:%M:%S.%f") # 当前quote的时间 # api.wait_update() # time1 = time.ctime(klines2.iloc[-1].datetime/(10**9)) while True: api.wait_update() if not pd.isna(klines2['close'][0]): break flag = cointegration_test(np.array(klines1['close']),
# ls_symbols = ['SHFE.fu1909', 'SHFE.bu1912', 'DCE.jd1909'] api = TqApi('SIM') # api = TqApi(TqSim(), backtest=TqBacktest(start_dt=dt.date(2019,1,2), end_dt=dt.date(2019,1,10))) dict_quotes = {} dict_klines = {} dict_positions = {} dict_target_pos = {} dict_update_kline_chan = {} dict_update_quote_chan = {} for SYMBOL in ls_symbols: dict_quotes[SYMBOL] = api.get_quote(SYMBOL) # 行情数据 dict_klines[SYMBOL] = api.get_kline_serial(SYMBOL, duration_seconds=15 * 60) dict_positions[SYMBOL] = api.get_position(SYMBOL) dict_target_pos[SYMBOL] = TargetPosTask(api, SYMBOL) dict_update_kline_chan[SYMBOL] = api.register_update_notify(dict_klines[SYMBOL]) dict_update_quote_chan[SYMBOL] = api.register_update_notify(dict_quotes[SYMBOL]) async def signal_generator(SYMBOL, strategy): """该task应用策略在价格触发时开仓,出发平仓条件时平仓""" klines = dict_klines[SYMBOL] position = dict_positions[SYMBOL] target_pos = dict_target_pos[SYMBOL] update_kline_chan = dict_update_kline_chan[SYMBOL] while True: target_pos_value = 0 async for _ in update_kline_chan: if api.is_changing(klines[-1], 'datetime'):
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手")
import logging from tqsdk import TqApi, TqSim, TargetPosTask SYMBOL = "DCE.jd1905" # 合约代码 NDAY = 5 # 天数 K1 = 0.2 # 上轨K值 K2 = 0.2 # 下轨K值 api = TqApi(TqSim()) logger = logging.getLogger("TQ") logger.info("策略开始运行") quote = api.get_quote(SYMBOL) klines = api.get_kline_serial(SYMBOL, 24 * 60 * 60) # 86400使用日线 target_pos = TargetPosTask(api, SYMBOL) def dual_thrust(quote, klines): current_open = klines[-1]["open"] 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 # 下轨 logger.info("当前开盘价: %f, 上轨: %f, 下轨: %f" % (current_open, buy_line, sell_line)) return buy_line, sell_line
TARGET_VOLUME = 300 # 目标交易手数 (>0: 多头, <0: 空头) SYMBOL = "DCE.jd2001" # 交易合约代码 HISTORY_DAY_LENGTH = 20 # 使用多少天的历史数据用来计算每个时间单元的下单手数 START_HOUR, START_MINUTE = 9, 35 # 计划交易时段起始时间点 END_HOUR, END_MINUTE = 10, 50 # 计划交易时段终点时间点 api = TqApi(auth=TqAuth("信易账户", "账户密码")) print("策略开始运行") # 根据 HISTORY_DAY_LENGTH 推算出需要订阅的历史数据长度, 需要注意history_day_length与time_cell的比例关系以避免超过订阅限制 time_slot_start = datetime.time(START_HOUR, START_MINUTE) # 计划交易时段起始时间点 time_slot_end = datetime.time(END_HOUR, END_MINUTE) # 计划交易时段终点时间点 klines = api.get_kline_serial(SYMBOL, TIME_CELL, data_length=int(10 * 60 * 60 / TIME_CELL * HISTORY_DAY_LENGTH)) target_pos = TargetPosTask(api, SYMBOL) position = api.get_position(SYMBOL) # 持仓信息 def get_kline_time(kline_datetime): """获取k线的时间(不包含日期)""" kline_time = datetime.datetime.fromtimestamp(kline_datetime // 1000000000).time() # 每根k线的时间 return kline_time def get_market_day(kline_datetime): """获取k线所对应的交易日""" kline_dt = datetime.datetime.fromtimestamp(kline_datetime // 1000000000) # 每根k线的日期和时间 if kline_dt.hour >= 18: # 当天18点以后: 移到下一个交易日
参考: https://www.shinnytech.com/blog/fairy-four-price/ 注: 该示例策略仅用于功能示范, 实盘时请根据自己的策略/经验进行修改 ''' from tqsdk import TqApi, TargetPosTask from datetime import datetime import time symbol = "SHFE.cu2002" # 合约代码 close_hour, close_minute = 14, 50 # 平仓时间 api = TqApi() # 使用模拟帐号直连行情和交易服务器 quote = api.get_quote(symbol) # 获取指定合约的盘口行情 klines = api.get_kline_serial(symbol, 24 * 60 * 60) # 获取日线 position = api.get_position(symbol) # 持仓信息 target_pos = TargetPosTask(api, symbol) # 目标持仓 top_rail = klines.high.iloc[-2] # 上轨: 昨日高点 bottom_rail = klines.low.iloc[-2] # 下轨: 昨日低点 print("上轨:", top_rail, ",下轨:", bottom_rail, ",昨日收盘价:", klines.close.iloc[-2], ",今日开盘价:", klines.open.iloc[-1]) while True: api.wait_update() if api.is_changing(klines.iloc[-1], "datetime"): # 如果产生一根新日线 (即到达下一个交易日): 重新获取上下轨 top_rail = klines.high.iloc[-2] bottom_rail = klines.low.iloc[-2] print("上轨:", top_rail, ",下轨:", bottom_rail, ",昨日收盘价:", klines.close.iloc[-2], ",今日开盘价:", klines.open.iloc[-1]) if api.is_changing(quote, "last_price"): # 如果行情最新价发生变化 print("当前最新价", quote.last_price) # 开仓突破
''' 价差回归 当近月-远月的价差大于200时做空近月,做多远月 当价差小于150时平仓 ''' from tqsdk import TqApi, TargetPosTask api = TqApi() 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("目标持仓: 空仓") target_pos_near.set_target_volume(0) target_pos_deferred.set_target_volume(0)