tqsdk.lib - 业务工具库

tqsdk.lib.target_pos_task - 目标持仓工具

class tqsdk.lib.target_pos_task.TargetPosTaskSingleton

TargetPosTask 需要保证在每个账户下每个合约只有一个 TargetPosTask 实例。

当用户多次调用时,应该保证对于同一账户同一合约使用相同的参数构造,否则抛错。

在修改 TargetPosTask 构造参数时,同时应该修改 TargetPosTaskSingleton.__call__ 方法的参数,要确保其个数、名称、默认值和文档描述一致, 这些参数才是实际传给 TargetPosTask.__init__ 方法的参数。

同时应该在 TargetPosTask 实例运行结束时释放相应的资源,_instances 需要情况对应的引用。

class tqsdk.lib.target_pos_task.TargetPosTask(api, symbol, price='ACTIVE', offset_priority='今昨,开', min_volume=None, max_volume=None, trade_chan=None, trade_objs_chan=None, account: Optional[Union[tqsdk.account.TqAccount, tqsdk.account.TqKq, tqsdk.sim.TqSim]] = None, *args, **kwargs)

目标持仓 task, 该 task 可以将指定合约调整到目标头寸

创建目标持仓task实例,负责调整归属于该task的持仓 (默认为整个账户的该合约净持仓).

注意:
  1. TargetPosTask 在 set_target_volume 时并不下单或撤单, 它的下单和撤单动作, 是在之后的每次 wait_update 时执行的. 因此, 需保证 set_target_volume 后还会继续调用wait_update()

  2. 请勿在使用 TargetPosTask 的同时使用 insert_order() 函数, 否则将导致 TargetPosTask 报错或错误下单。

  3. TargetPosTask 如果同时设置 min_volume(每笔最小下单手数),max_volume(每笔最大下单的手数)两个参数,表示采用 大单拆分模式 下单。

    大单拆分模式 下,每次下单的手数为随机生成的正整数,值介于 min_volume、max_volume 之间。

    具体说明:调用 set_target_volume 后,首先会根据目标持仓手数、开平仓顺序计算出,需要平今、平昨、开仓的目标下单手数及顺序。

    • 如果在调整持仓的目标下单手数小于 max_volume,则直接以目标下单手数下单。

    • 如果在调整持仓的目标下单手数大于等于 max_volume,则会以 min_volume、max_volume 之间的随机手数下一笔委托单,手数全部成交后,会接着处理剩余的手数; 继续以随机手数下一笔委托单,全部成交后,继续处理剩余的手数,直至剩余手数小于 max_volume 时,直接以剩余手数下单。

    当使用大单拆分模式下单时,必须同时填写 min_volume、max_volume,且需要满足 max_volume >= min_volume > 0。

Args:

api (TqApi): TqApi实例,该task依托于指定api下单/撤单

symbol (str): 负责调整的合约代码

price (str / Callable): [可选]下单方式, 默认为 "ACTIVE"。
  • "ACTIVE":对价下单,在持仓调整过程中,若下单方向为买,对价为卖一价;若下单方向为卖,对价为买一价。

  • "PASSIVE":排队价下单,在持仓调整过程中,若下单方向为买,对价为买一价;若下单方向为卖,对价为卖一价。

  • Callable[[str], Union[float, int]]: 函数参数为下单方向,函数返回值是下单价格。如果返回 nan,程序会抛错。

offset_priority (str): [可选]开平仓顺序,昨=平昨仓,今=平今仓,开=开仓,逗号=等待之前操作完成

对于下单指令区分平今/昨的交易所(如上期所),按照今/昨仓的数量计算是否能平今/昨仓 对于下单指令不区分平今/昨的交易所(如中金所),按照“先平当日新开仓,再平历史仓”的规则计算是否能平今/昨仓,如果这些交易所设置为"昨开"在有当日新开仓和历史仓仓的情况下,会自动跳过平昨仓进入到下一步

  • "今昨,开" 表示先平今仓,再平昨仓,等待平仓完成后开仓,对于没有单向大边的品种避免了开仓保证金不足

  • "今昨开" 表示先平今仓,再平昨仓,并开仓,所有指令同时发出,适合有单向大边的品种

  • "昨开" 表示先平昨仓,再开仓,禁止平今仓,适合股指这样平今手续费较高的品种

  • "开" 表示只开仓,不平仓,适合需要进行锁仓操作的品种

min_volume (int): [可选] 大单拆分模式下 每笔最小下单的手数,默认不启用 大单拆分模式

max_volume (int): [可选] 大单拆分模式下 每笔最大下单的手数,默认不启用 大单拆分模式

trade_chan (TqChan): [可选]成交通知channel, 当有成交发生时会将成交手数(多头为正数,空头为负数)发到该channel上

trade_objs_chan (TqChan): [可选]成交对象通知channel, 当有成交发生时会将成交对象发送到该channel上

account (TqAccount/TqKq/TqSim): [可选]指定发送下单指令的账户实例, 多账户模式下,该参数必须指定

注意

当 price 参数为函数类型时,该函数应该返回一个有效的价格值,应该避免返回 nan。以下为 price 参数是函数类型时的示例。

Example1:

# ... 用户代码 ...
quote = api.get("SHFE.cu2012")
def get_price(direction):
    # 在 BUY 时使用买一价加一档价格,SELL 时使用卖一价减一档价格
    if direction == "BUY":
        price = quote.bid_price1 + quote.price_tick
    else:
        price = quote.ask_price1 - quote.price_tick
    # 如果 price 价格是 nan,使用最新价报单
    if price != price:
        price = quote.last_price
    return price

target_pos = TargetPosTask(api, "SHFE.cu2012", price=get_price)
# ... 用户代码 ...

Example2:

# ... 用户代码 ...
quote1 = api.get("SHFE.cu2012")
quote2 = api.get("SHFE.au2012")

def get_price(direction, quote):
    # 在 BUY 时使用买一价加一档价格,SELL 时使用卖一价减一档价格
    if direction == "BUY":
        price = quote.bid_price1 + quote.price_tick
    else:
        price = quote.ask_price1 - quote.price_tick
    # 如果 price 价格是 nan,使用最新价报单
    if price != price:
        price = quote.last_price
    return price

target_pos1 = TargetPosTask(api, "SHFE.cu2012", price=lambda direction: get_price(direction, quote1))
target_pos2 = TargetPosTask(api, "SHFE.au2012", price=lambda direction: get_price(direction, quote2))
# ... 用户代码 ...

Example3:

# 大单拆分模式用法示例

from tqsdk import TqApi, TqAuth, TargetPosTask
api = TqApi(auth=TqAuth("信易账户", "账户密码"))
position = api.get_position('SHFE.rb2106')

# 同时设置 min_volume、max_volume 两个参数,表示使用大单拆分模式
t = TargetPosTask(api, 'SHFE.rb2106', min_volume=2, max_volume=10)
t.set_target_volume(50)
while True:
    api.wait_update()
    if position.pos_long == 50:
        break
api.close()

# 说明:
# 以上代码使用 TqSim 交易,开始时用户没有 SHFE.cu2012 合约的任何持仓,那么在 t.set_target_volume(50) 之后应该开多仓 50 手
# 根据用户参数,下单使用大单拆分模式,每次下单手数在 2~10 之间,打印出的成交通知可能是这样的:
# 2021-03-15 11:29:48 -     INFO - 模拟交易成交记录
# 2021-03-15 11:29:48 -     INFO - 时间: 2021-03-15 11:29:47.516138, 合约: SHFE.rb2106, 开平: OPEN, 方向: BUY, 手数: 7, 价格: 4687.000,手续费: 32.94
# 2021-03-15 11:29:48 -     INFO - 时间: 2021-03-15 11:29:47.519699, 合约: SHFE.rb2106, 开平: OPEN, 方向: BUY, 手数: 8, 价格: 4687.000,手续费: 37.64
# 2021-03-15 11:29:48 -     INFO - 时间: 2021-03-15 11:29:47.522848, 合约: SHFE.rb2106, 开平: OPEN, 方向: BUY, 手数: 10, 价格: 4687.000,手续费: 47.05
# 2021-03-15 11:29:48 -     INFO - 时间: 2021-03-15 11:29:47.525617, 合约: SHFE.rb2106, 开平: OPEN, 方向: BUY, 手数: 8, 价格: 4687.000,手续费: 37.64
# 2021-03-15 11:29:48 -     INFO - 时间: 2021-03-15 11:29:47.528151, 合约: SHFE.rb2106, 开平: OPEN, 方向: BUY, 手数: 7, 价格: 4687.000,手续费: 32.94
# 2021-03-15 11:29:48 -     INFO - 时间: 2021-03-15 11:29:47.530930, 合约: SHFE.rb2106, 开平: OPEN, 方向: BUY, 手数: 7, 价格: 4687.000,手续费: 32.94
# 2021-03-15 11:29:48 -     INFO - 时间: 2021-03-15 11:29:47.533515, 合约: SHFE.rb2106, 开平: OPEN, 方向: BUY, 手数: 3, 价格: 4687.000,手续费: 14.12
set_target_volume(volume: int)None

设置目标持仓手数

Args:

volume (int): 目标持仓手数,正数表示多头,负数表示空头,0表示空仓

Example1:

# 设置 rb1810 持仓为多头5手
from tqsdk import TqApi, TqAuth, TargetPosTask

api = TqApi(auth=TqAuth("信易账户", "账户密码"))
target_pos = TargetPosTask(api, "SHFE.rb1810")
target_pos.set_target_volume(5)
while True:
    # 需在 set_target_volume 后调用wait_update()以发出指令
    api.wait_update()

Example2:

# 多账户模式下使用 TargetPosTask
from tqsdk import TqApi, TqMultiAccount, TqAuth, TargetPosTask

account1 = TqAccount("H海通期货", "123456", "123456")
account2 = TqAccount("H宏源期货", "654321", "123456")
api = TqApi(TqMultiAccount([account1, account2]), auth=TqAuth("信易账户", "账户密码"))
symbol1 = "DCE.m2105"
symbol2 = "DCE.i2101"
# 多账户模式下, 调仓工具需要指定账户实例
target_pos1 = TargetPosTask(api, symbol1, account=account1)
target_pos2 = TargetPosTask(api, symbol2, account=account2)
target_pos1.set_target_volume(30)
target_pos2.set_target_volume(80)
while True:
    api.wait_update()

api.close()
cancel()

取消当前 TargetPosTask 实例,会将该实例已经发出但还是未成交的委托单撤单,并且此实例的 set_target_volume 函数不会再生效。

任何时刻,每个账户下一个合约只能有一个 TargetPosTask 实例,并且其构造参数不能修改。

如果对于同一个合约要构造不同参数的 TargetPosTask 实例,需要调用 cancel 方法销毁,才能创建新的 TargetPosTask 实例

Example1:

from datetime import datetime, time
from tqsdk import TqApi, TargetPosTask

api = TqApi(auth=TqAuth("信易账户", "账户密码"))
quote = api.get_quote("SHFE.rb2110")
target_pos_passive = TargetPosTask(api, "SHFE.rb2110", price="PASSIVE")

while datetime.strptime(quote.datetime, "%Y-%m-%d %H:%M:%S.%f").time() < time(14, 50):
    api.wait_update()
    # ... 策略代码 ...

# 取消 TargetPosTask 实例
target_pos_passive.cancel()

while not target_pos_passive.is_finished():  # 此循环等待 target_pos_passive 处理 cancel 结束
    api.wait_update()  # 调用wait_update(),会对已经发出但还是未成交的委托单撤单

# 创建新的 TargetPosTask 实例
target_pos_active = TargetPosTask(api, "SHFE.rb2110", price="ACTIVE")
target_pos_active.set_target_volume(0)  # 平所有仓位

while True:
    api.wait_update()
    # ... 策略代码 ...

api.close()
is_finished()bool

返回当前 TargetPosTask 实例是否已经结束。即此实例的 set_target_volume 函数不会再接受参数,此实例不会再下单或者撤单。

Returns:

bool: 当前 TargetPosTask 实例是否已经结束

class tqsdk.lib.target_pos_task.InsertOrderUntilAllTradedTask(api, symbol, direction, offset, volume, min_volume: Optional[int] = None, max_volume: Optional[int] = None, price: Union[str, Callable[[str], Union[float, int]]] = 'ACTIVE', trade_chan=None, trade_objs_chan=None, account: Optional[Union[tqsdk.account.TqAccount, tqsdk.account.TqKq, tqsdk.sim.TqSim]] = None)

追价下单task, 该task会在行情变化后自动撤单重下,直到全部成交 (注:此类主要在tqsdk内部使用,并非简单用法,不建议用户使用)

创建追价下单task实例

Args:

api (TqApi): TqApi实例,该task依托于指定api下单/撤单

symbol (str): 拟下单的合约symbol, 格式为 交易所代码.合约代码, 例如 "SHFE.cu1801"

direction (str): "BUY" 或 "SELL"

offset (str): "OPEN", "CLOSE" 或 "CLOSETODAY"

volume (int): 需要下单的手数

min_volume (int): [可选] 大单拆分模式下 每笔最小下单的手数,默认不启用 大单拆分模式

max_volume (int): [可选] 大单拆分模式下 每笔最大下单的手数,默认不启用 大单拆分模式

price (str / Callable): [可选]下单方式, 默认为 "ACTIVE"。
  • "ACTIVE":对价下单,在持仓调整过程中,若下单方向为买,对价为卖一价;若下单方向为卖,对价为买一价。

  • "PASSIVE":对价下单,在持仓调整过程中,若下单方向为买,对价为买一价;若下单方向为卖,对价为卖一价。

  • Callable[[str], Union[float, int]]: 函数参数为下单方向,函数返回值是下单价格。如果返回 nan,程序会抛错。

trade_chan (TqChan): [可选]成交通知channel, 当有成交发生时会将成交手数(多头为正数,空头为负数)发到该channel上

trade_objs_chan (TqChan): [可选]成交对象通知channel, 当有成交发生时会将成交对象发送到该channel上

account (TqAccount/TqKq/TqSim): [可选]指定发送下单指令的账户实例, 多账户模式下,该参数必须指定

class tqsdk.lib.target_pos_task.InsertOrderTask(api, symbol, direction, offset, volume, limit_price=None, order_chan=None, trade_chan=None, trade_objs_chan=None, account: Optional[Union[tqsdk.account.TqAccount, tqsdk.account.TqKq, tqsdk.sim.TqSim]] = None)

下单task (注:此类主要在tqsdk内部使用,并非简单用法,不建议用户使用)

创建下单task实例

Args:

api (TqApi): TqApi实例,该task依托于指定api下单/撤单

symbol (str): 拟下单的合约symbol, 格式为 交易所代码.合约代码, 例如 "SHFE.cu1801"

direction (str): "BUY" 或 "SELL"

offset (str): "OPEN", "CLOSE" 或 "CLOSETODAY"

volume (int): 需要下单的手数

limit_price (float): [可选]下单价格, 默认市价单

order_chan (TqChan): [可选]委托单通知channel, 当委托单状态发生时会将委托单信息发到该channel上

trade_chan (TqChan): [可选]成交通知channel, 当有成交发生时会将成交手数(多头为正数,空头为负数)发到该channel上

trade_objs_chan (TqChan): [可选]成交对象通知channel, 当有成交发生时会将成交对象发送到该channel上

account (TqAccount/TqKq/TqSim): [可选]指定发送下单指令的账户实例, 多账户模式下,该参数必须指定

tqsdk.lib.target_pos_scheduler - 基于时间维度的目标持仓工具

class tqsdk.lib.target_pos_scheduler.TargetPosScheduler(api: tqsdk.api.TqApi, symbol: str, time_table: pandas.core.frame.DataFrame, offset_priority: str = '今昨,开', min_volume: Optional[int] = None, max_volume: Optional[int] = None, trade_chan: Optional[tqsdk.channel.TqChan] = None, trade_objs_chan: Optional[tqsdk.channel.TqChan] = None, account: Optional[Union[tqsdk.account.TqAccount, tqsdk.account.TqKq, tqsdk.sim.TqSim]] = None)

算法执行引擎,根据设定的目标持仓任务列表,调整指定合约到目标头寸

创建算法执行引擎实例,根据设定的目标持仓任务列表,调用 TargetPosTask 来调整指定合约到目标头寸。

注意:
  1. TargetPosScheduler 创建后不会立即不下单或撤单, 它的下单和撤单动作, 是在之后的每次 wait_update 时执行的. 因此, 需保证后续还会调用wait_update()

  2. 请勿同时使用 TargetPosScheduler、TargetPosTask、insert_order() 函数, 否则将导致报错或错误下单。

  3. symboloffset_prioritymin_volumemax_volumetrade_chantrade_objs_chanaccount 这几个参数会直接传给 TargetPosTask,请按照 TargetPosTask 的说明设置参数。

Args:

api (TqApi): TqApi实例,该task依托于指定api下单/撤单

symbol (str): 负责调整的合约代码

time_table (DataFrame): 目标持仓任务列表,每一行表示一项目标持仓任务,其应该包含以下几列:
  • interval: 当前这项任务的持续时间长度,单位为秒,经过这么多秒之后,此项任务应该退出,剩余未调整到的目标持仓,会留到下一项任务中
    • 注意1:对于最后一项任务,会按照当前项参数,调整到目标持仓后立即退出(时间参数不对最后一项任务起作用)

    • 注意2:时间长度可以跨非交易时间段(可以跨小节等待),但是不可以跨交易日

  • target_pos: 当前这项任务的目标净持仓手数

  • price: 当前这项任务的下单价格模式,此列中非 None 的项,会作为创建 TargetPosTask 实例的 price 参数,支持以下几种参数:
    • None: 不下单,表示暂停一段时间

    • "PASSIVE" : 排队价下单

    • "ACTIVE": 对价下单

    • Callable (direction: str) -> Union[float, int]: 传入函数作为价格参数,函数参数为下单方向,函数返回值是下单价格。如果返回 nan,程序会抛错。

offset_priority (str): [可选]开平仓顺序,昨=平昨仓,今=平今仓,开=开仓,逗号=等待之前操作完成

对于下单指令区分平今/昨的交易所(如上期所),按照今/昨仓的数量计算是否能平今/昨仓 对于下单指令不区分平今/昨的交易所(如中金所),按照“先平当日新开仓,再平历史仓”的规则计算是否能平今/昨仓,如果这些交易所设置为"昨开"在有当日新开仓和历史仓仓的情况下,会自动跳过平昨仓进入到下一步

  • "今昨,开" 表示先平今仓,再平昨仓,等待平仓完成后开仓,对于没有单向大边的品种避免了开仓保证金不足

  • "今昨开" 表示先平今仓,再平昨仓,并开仓,所有指令同时发出,适合有单向大边的品种

  • "昨开" 表示先平昨仓,再开仓,禁止平今仓,适合股指这样平今手续费较高的品种

  • "开" 表示只开仓,不平仓,适合需要进行锁仓操作的品种

min_volume (int): [可选] 大单拆分模式下 每笔最小下单的手数,默认不启用 大单拆分模式

max_volume (int): [可选] 大单拆分模式下 每笔最大下单的手数,默认不启用 大单拆分模式

trade_chan (TqChan): [可选]成交通知channel, 当有成交发生时会将成交手数(多头为正数,空头为负数)发到该channel上

trade_objs_chan (TqChan): [可选]成交对象通知channel, 当有成交发生时会将成交对象发送到该channel上

account (TqAccount/TqKq/TqSim): [可选]指定发送下单指令的账户实例, 多账户模式下,该参数必须指定

Example:

from pandas import DataFrame
from tqsdk import TqApi, TargetPosScheduler

api = TqApi(auth=TqAuth("信易账户", "账户密码"))
time_table = DataFrame([
    [25, 10, "PASSIVE"],
    [5, 10, "ACTIVE"],
    [25, 20, "PASSIVE"],
    [5, 20, "ACTIVE"],
], columns=['interval', 'target_pos', 'price'])

scheduler = TargetPosScheduler(api, 'SHFE.cu2112', time_table=time_table)
while True:
    api.wait_update()
    if scheduler.is_finished():
        break

print("打印出 scheduler 全部成交以及成交均价")
print(scheduler.trades_df)
average_trade_price = sum(scheduler.trades_df['price'] * scheduler.trades_df['volume']) / sum(scheduler.trades_df['volume'])
print("成交均价:", average_trade_price)
api.close()
cancel()

取消当前 TargetPosScheduler 实例,会将该实例已经发出但还是未成交的委托单撤单。

Example:

from pandas import DataFrame
from tqsdk import TqApi, TargetPosScheduler

api = TqApi(auth=TqAuth("信易账户", "账户密码"))
time_table = DataFrame([
    [25, 10, "PASSIVE"],
    [5, 10, "ACTIVE"],
    [25, 20, "PASSIVE"],
    [5, 20, "ACTIVE"],
], columns=['interval', 'target_pos', 'price'])

scheduler = TargetPosScheduler(api, 'SHFE.cu2112', time_table=time_table)

api.wait_update()
# 运行代码。。。
scheduler.cancel()
while True:
    api.wait_update()
    if scheduler.is_finished():
        break
api.close()
is_finished()

返回当前 TargetPosScheduler 实例是否已经结束。即此实例不会再发出下单或者撤单的任何动作。

Returns:

bool: 当前 TargetPosScheduler 实例是否已经结束