乐于分享
好东西不私藏

PeopleQuant帮助文档:最小开仓数量规定下如何实现自动调仓

PeopleQuant帮助文档:最小开仓数量规定下如何实现自动调仓

许多人在交易中希望方便的把仓位开到或平到需要的数量,而在达到目标数量的过程中,不想对撤单、追单等过程亲自处理,一方面可能觉得处理起来较复杂,需要写一串代码使得代码结构臃肿,一方面怕处理不好而存在交易逻辑错误。
一些量化平台都提供了自动调仓工具,帮用户自动把仓位调整到目标数量,但交易所的撮合机制存在部分成交的情况,当合约最低开仓手数大于1手时,剩余未成交的数量是继续按最低开仓数量补足,还是放弃追单,或者追单后超出的数量再平仓?不同用户的需求可能不一样,调仓工具不能为用户做决断就会停止运行,例如天勤量化的调仓工具无法设置相关合约。
当前,交易所设定最小开仓手数的合约已经有二十多个,用户不得不为调仓功能做出自己的配置了。好在实现目标调仓的过程并不复杂,我写过一个应用于天勤量化的OpenClose函数,它是对下单函数insert_order非常实用的封装。
OpenClose实现调仓很简单,例如想把目标多头持仓数量设置为lots,当前的持仓数量为pos_long,那么当pos_long<lots时需要补仓lots-pos_long,当pos_long>lots时需要减仓仓pos_long-lots,实现的代码结构为:
#大单拆分lots = 100 #总仓100手lot = 5 #单笔5手if position.pos_long < lots:    if lots - position.pos_long >= lot: L = lot    else : L = lots - position.pos_long    task = api.create_task(OpenClose(api,quote,position,"kaiduo",lot=L,price='对手价',n_price_tick=5))    while not task.done(): api.wait_update() #等待成交完成if position.pos_long > lots:    if position.pos_long - lots >= lot: L = lot    else : L = position.pos_long - lots    task = api.create_task(OpenClose(api,quote,position,"pingduo",lot=L,price='对手价',n_price_tick=5))    while not task.done(): api.wait_update() #等待成交完成
这个代码结构还包括了把大单拆分成小单的逻辑,把它放在循环语句(一般是while,或协程的循环中)中,就可以自动把持仓调整到目标lots。
当合约规定的最小开仓数量open_min_volume大于1时,就要面临放弃补仓、继续按最小数量补仓、补仓后超出部分再平仓的选择。
这三种处理也很简单,放弃补仓时,只需要在下单语句前加一个条件:
if L >= quote.open_min_limit_order_volume:    task = api.create_task(OpenClose(api,quote,position,"kaiduo",lot=L,price='对手价',n_price_tick=5))    while not task.done(): api.wait_update() #等待成交完成
继续按最小数量补仓时在下单前加一句:
L = max(L,quote.open_min_limit_order_volume)
补仓后超出部分平仓时,保持平仓结构即可,因为平仓的最小数量默认都为1。

OpenClose还有两个实用的参数,n_price_tick表示排队价偏离报单价格多少跳不成交就撤单,che_time表示等待多少时间不成交就撤单,一般行情没有变动的情况下,继续等待即可无需撤单,所以che_time应用的场景更少,但在对最小开仓数量大于1的合约调仓时就会比较有用,例如当行情波动不大,多等几秒钟说不定又全部成交了,不用立即撤单,相比于立即撤单补仓和对超出持仓减仓,可能会减少不必要的损失。

所以,使用OpenClose,用户可以实现四种灵活的调仓,1、等待che_time秒并且排队价偏离报单价格n_price_tick跳后撤单,2、撤单后放弃补仓,3、撤单后按最小数量补仓,4、补仓后超出部分再平仓。

天勤中实现的OpenClose代码放在文末,下面再推荐下PeopleQuant中的调仓实现。

PeopleQuant中首先也是推荐使用类似功能的open_close函数,open_close是比OpenClose功能更全的下单函数,调仓过程和使用OpenClose结构类似,最低开仓数量通过参数open_min_volume传入,支持多线程或协程中用法,具体可参考安装包中的示例。

其次,在PeopleQuant中可使用对open_close进一步封装的调仓工具auto_pos_thread(多线程版)和auto_pos_async(异步协程版),并使用函数set_pos_volume设置目标持仓,例如:

#设置多头目标仓位100手 set_pos_volume(rb2601, pos_long=100 )#设置多头目标仓位100手,以排队价下单 set_pos_volume(rb2601, pos_long=100, price = "排队价" )#设置空头目标仓位10手set_pos_volume(rb2601, pos_short=10)#同时设置多头目标仓位100手、空头目标仓位10手,auto_pos会先完成多头调仓再完成空头调仓set_pos_volume(rb2601, pos_long=100, pos_short=10)#设置多头目标仓位10手, 最低开仓数量4手(如菜粕交易所规定最低开仓4手)set_pos_volume(RM601, pos_long=10, open_min_volume=4)#设置多头目标仓位10手, 最低开仓数量4手,价格偏离3跳撤单set_pos_volume(RM601, pos_long=10, open_min_volume=4,n_price_tick=3)#设置多头目标仓位10手, 最低开仓数量4手,价格偏离3跳同时等待了5秒撤单set_pos_volume(RM601, pos_long=10, open_min_volume=4,n_price_tick=3,che_time=5)

所以,使用set_pos_volume可以灵活的对多头或空头单独调仓或同时调仓,传入price设置下单价格,传入open_min_volume设置最小开仓数量,传入n_price_tick和che_time设置撤单偏离跳数和等待时间。

撤单后需要补仓时,当前pos_long,目标持仓lots,则传入持仓设置为pos_long+max(lots-pos_long,open_min_volume),即目标数量是当前持仓+剩余数量与最小开仓数量的最大值。

if position.pos_long < lots:    L = pos_long + max(lots-pos_long, 4)    set_pos_volume(RM601, pos_long=L, open_min_volume=4,n_price_tick=3,che_time=5)

对多出数量减仓时,在开完仓后执行调仓:

if position.pos_long > lots:    set_pos_volume(RM601, pos_long=lots)

具体用法可查阅安装包中的智能调仓示例和相关函数的源代码。欢迎安装PeopleQuant使用,支持Python3.12以上版本安装:

pip install -U PeopleQuant

github仓库地址,欢迎收藏:

https://github.com/zhuxueli-cta/PeopleQuant

欢迎关注公众号,一起学习量化交易

最后,附上天勤中实现的OpenClose代码:

async def _OpenClose(api,quote={},position={},kaiping='',lot=0,price=None,advanced=None,order_close_chan=True,trade_chan=None,                    block=True,n_price_tick=1,che_time=0,order_info='无',signal_price=float('nan'),close_minutes=3,**kw):    '''    Args:    kaiping: str,交易方向,开多"kaiduo",开空"kaikong",平多"pingduo",平空"pingkong"    lot: int,下单手数    price: float or str,下单价格,默认对手价,可选"停板价","对手价","排队价",或float类型的价格    advanced: str,高级委托指令,默认None,可选"FOK","FAK",推荐None    order_close_chan:TqChan,发送平仓结果到外部(最新用法建议从返回值获取交易结果)    trade_chan:TqChan,发送成交结果到外部(最新用法建议从返回值获取交易结果)    block: bool,是否阻塞等待成交完成,默认True,即等待成交完成    n_price_tick: int,盘口排队价偏离委托价多少个最小变动价位不成交主动撤单,默认1    che_time: int,多少秒不成交主动撤单,默认0,即不按时间撤单    order_info: str,报单备注信息,默认为"无"    signal_price: float,信号位价格,默认为nan    close_minutes:3,夜盘离尾盘多少分钟撤单,默认3分钟    kw: dict,其他可选参数,如"OrderDict","Order_Log","name","close_minutes"等    return shoushu, junjia, che_count, day_order,symbol, kaiping,lot,price, last_msg, quote_volume, profit_count,profit_money, order_id,order_wrong,signal_price,order_info,quote,position    '''    kw.update({"kaiping":kaiping,"lot":lot,"price":price,"advanced":advanced,"order_close_chan":True,"trade_chan":True,                "block":block,"n_price_tick":n_price_tick,"che_time":che_time,"order_info":order_info,"signal_price":signal_price,"close_minutes":close_minutes})    if "name" not in kw: kw["name"] = "name" #策略名    quote_volume = quote.ask_volume1 if kaiping in ["kaiduo","pingkong"else quote.bid_volume1 #盘口挂单量    order_wrong = False #是否错单    n_price_tick = int(n_price_tick)    che_time = int(che_time)    symbol = quote.instrument_id #品种代码    product_id = quote.product_id    no_backtest = "backtest" not in kw or not kw["backtest"#是否回测    quote_datetime = time_to_datetime(quote.datetime)    equal_time = no_backtest and quote_datetime.hour == datetime.now().hour  #行情时间是否等于本机时间    if close_minutes:        quote_time = (quote_datetime+timedelta(minutes=close_minutes)).time()        trading_time = quote.trading_time        if trading_time['night'] : #是否有夜盘            night_end = trading_time['night'][-1][-1]            time_list = [int(i) for i in night_end.split(':') ]            #夜盘收盘时间            night_end_time = time(time_list[0if time_list[0] < 24 else time_list[0]-24,time_list[1],time_list[2])            #close_minutes分钟后夜盘收盘,停止交易            if abs(datetime.now().timestamp() - quote_datetime.timestamp())<=close_minutes*2*60 and (time_list[0] >= 24 and time(8) > quote_time >= night_end_time or time_list[0] < 24 and quote_time >= night_end_time):                return {"shoushu":0"junjia":float("nan"), "che_count":0"day_order":0"symbol":symbol, "kaiping":kaiping,"lot":lot,"price":price,                        "last_msg":f"临近夜盘收盘停止交易" ,"quote_volume":quote_volume,                        "profit_count":0,"profit_money":0"order_id":[],"order_wrong":True if equal_time else False,"signal_price":signal_price,"order_info":order_info,                        "quote":quote,"position":position}    #print(quote.datetime,'已经触发下单,品种:',symbol,'方向:',kaiping,'手数:',lot,'信号位:',signal_price)    if not price or  price == '超价':        if quote.bid_price1 == quote.bid_price1:            price_buy = quote.bid_price1 + quote.price_tick         else: price_buy = quote.last_price #停板时以最新价报单        if quote.ask_price1 == quote.ask_price1:            price_sell = quote.ask_price1 - quote.price_tick        else: price_sell = quote.last_price    elif price == '对手价':        if quote.ask_price1 == quote.ask_price1:            price_buy = quote.ask_price1        else: price_buy = quote.last_price #停板时以最新价报单        if quote.bid_price1 == quote.bid_price1:            price_sell = quote.bid_price1        else: price_sell = quote.last_price    elif price == '停板价':        price_buy = quote.upper_limit        price_sell = quote.lower_limit    elif price == '排队价':        if quote.bid_price1 == quote.bid_price1:            price_buy = quote.bid_price1        else: price_buy = quote.last_price #停板时以最新价报单        if quote.ask_price1 == quote.ask_price1:            price_sell = quote.ask_price1        else: price_sell = quote.last_price        advanced = None    elif price == price :        if price >= quote.upper_limit :            price_buy = price_sell = quote.upper_limit #超出停板时以停板价报单        elif price <= quote.lower_limit:            price_buy = price_sell = quote.lower_limit #超出停板时以停板价报单        else: price_buy = price_sell = price #其他限定价        if price_buy < quote.ask_price1 and kaiping in ["kaiduo","pingkong"]:advanced = None        if price_sell > quote.bid_price1 and kaiping in ["pingduo","kaikong"]:advanced = None    elif price != price:         if quote.last_price == quote.upper_limit and kaiping in ["kaiduo","pingkong"]:            price_buy = price_sell = quote.last_price        elif quote.last_price == quote.lower_limit and kaiping in ["pingduo","kaikong"]:            price_buy = price_sell = quote.last_price        else: price_buy = price_sell = price    price_buy = min(price_buy, quote.upper_limit) #买入价格不能高于涨停价    price_sell = max(price_sell, quote.lower_limit) #卖出价格不能低于跌停价    if (price_buy == quote.upper_limit and kaiping in ["kaiduo","pingkong"or         price_sell == quote.lower_limit and kaiping in ["pingduo","kaikong"]):        advanced = None #无对手价时不能用高级委托指令    if not no_backtest: #回测模式限价手数有值则取值        if quote.max_limit_order_volume > 0: lot = min(lot, quote.max_limit_order_volume)        if 'kai' in kaiping and quote.open_max_limit_order_volume > 0: lot = min(lot,quote.open_max_limit_order_volume)    else#非回测模式取值        lot = min(lot, quote.max_limit_order_volume)        if 'kai' in kaiping : lot = min(lot,quote.open_max_limit_order_volume)    lot = int(lot)    if price_buy != price_buy or price_sell != price_sell or lot <= 0 or 'kai' in kaiping and lot < quote.open_min_limit_order_volume:         #以持仓数量平仓时平仓数量为0可能是服务器故障持仓未更新(也可能其他程序超额平仓,或清仓代码正常所需非本策略bug),等待更新后可继续下单,其他情况下的报价和手数错误应退出交易        if not (lot == position.pos_long == 0 and kaiping == 'pingduo' or lot == position.pos_short == 0 and kaiping=='pingkong'): order_wrong = True         return {"shoushu":0"junjia":float("nan"), "che_count":0"day_order":0"symbol":symbol, "kaiping":kaiping,"lot":lot,"price":price_buy if kaiping in ["kaiduo","pingkong"else price_sell,                "last_msg":f"报单错误,下单价格{price},下单手数{lot},最大限价单{quote.max_limit_order_volume},最大限价开仓单{quote.open_max_limit_order_volume},最小限价开仓单{quote.open_min_limit_order_volume}" ,                "quote_volume":quote_volume,"profit_count":0,"profit_money":0"order_id":[],"order_wrong":order_wrong,"signal_price":signal_price,"order_info":order_info,"quote":quote,"position":position}    #liquid = quote.ask_price1 - quote.bid_price1 <= quote.price_tick*10     shoushu = 0 #已成交手数      junjia = 0.0 #成交均价     che_count, day_order = 00 #报撤单次数    profit_count, profit_money = 00 #平仓盈亏点数和盈亏金额    ping_jin,ping_zuo,order = None,None,None  #委托单对象    last_msg, order_id = "", []  #委托单状态信息和单号    #while position.pos_long != position.volume_long or position.pos_short != position.volume_short: await update_chan.recv() #新单之前已检查过    if kaiping== 'pingduo'#交易方向为平多        #可能服务器故障持仓未更新,等待更新        if not position.pos_long or position.open_price_long != position.open_price_long:            return {"shoushu":0"junjia":float("nan"), "che_count":0"day_order":0"symbol":symbol, "kaiping":kaiping,"lot":lot,"price":price_buy if kaiping in ["kaiduo","pingkong"else price_sell,                    "last_msg":f"多头持仓错误,多头持仓手数{position.pos_long},多头持仓价格{position.open_price_long}" ,                    "quote_volume":quote_volume,"profit_count":0,"profit_money":0"order_id":[],"order_wrong":order_wrong,"signal_price":signal_price,"order_info":order_info,"quote":quote,"position":position}        pos_direction = "多" #原持仓方向        pos0 = position.pos_long #原多头持仓手数        price0 = position.open_price_long #原多头开仓均价        #margin0 = position.margin_long #原多头占用保证金        if 0 < lot <= position.pos_long_today : #小于等于今仓,平今            ping_jin=api.insert_order(symbol=symbol, direction='SELL', offset='CLOSETODAY', volume=lot, limit_price=price_sell,advanced=advanced)        elif 0 < position.pos_long_today < lot <= position.pos_long:  #优先平今,剩余仓位平昨               ping_zuo=api.insert_order(symbol=symbol, direction='SELL', offset='CLOSE', volume=lot-position.pos_long_today, limit_price=price_sell,advanced=advanced) #先平昨再平今             ping_jin=api.insert_order(symbol=symbol, direction='SELL', offset='CLOSETODAY', volume=position.pos_long_today, limit_price=price_sell,advanced=advanced)        elif 0 == position.pos_long_today < lot <= position.pos_long: #只有昨仓            ping_zuo=api.insert_order(symbol=symbol, direction='SELL', offset='CLOSE', volume=lot, limit_price=price_sell,advanced=advanced)     elif kaiping=='pingkong'#交易方向为平空        if not position.pos_short or position.open_price_short != position.open_price_short:            return {"shoushu":0"junjia":float("nan"), "che_count":0"day_order":0"symbol":symbol, "kaiping":kaiping,"lot":lot,"price":price_buy if kaiping in ["kaiduo","pingkong"else price_sell,                    "last_msg":f"空头持仓错误,空头持仓手数{position.pos_short},空头持仓价格{position.open_price_short}" ,                    "quote_volume":quote_volume,"profit_count":0,"profit_money":0"order_id":[],"order_wrong":order_wrong,"signal_price":signal_price,"order_info":order_info,"quote":quote,"position":position}        pos_direction = "空" #原持仓方向        pos0 = position.pos_short #原空头持仓手数        price0 = position.open_price_short #原空头开仓均价        #margin0 = position.margin_short #原空头占用保证金        if 0 < lot <= position.pos_short_today : #小于等于今仓,平今            ping_jin=api.insert_order(symbol=symbol, direction='BUY', offset='CLOSETODAY', volume=lot, limit_price=price_buy,advanced=advanced)            elif 0 < position.pos_short_today < lot <= position.pos_short:                  ping_zuo=api.insert_order(symbol=symbol, direction='BUY', offset='CLOSE', volume=lot-position.pos_short_today, limit_price=price_buy,advanced=advanced)                  ping_jin=api.insert_order(symbol=symbol, direction='BUY', offset='CLOSETODAY', volume=position.pos_short_today, limit_price=price_buy,advanced=advanced)        elif 0 == position.pos_short_today < lot <= position.pos_short:                  ping_zuo=api.insert_order(symbol=symbol, direction='BUY', offset='CLOSE', volume=lot, limit_price=price_buy,advanced=advanced)    elif kaiping== 'kaiduo'#交易方向为开多        order = api.insert_order(symbol=symbol, direction='BUY', offset='OPEN', volume=lot, limit_price=price_buy,advanced=advanced)       elif kaiping=='kaikong'#交易方向为开空        order = api.insert_order(symbol=symbol, direction="SELL", offset="OPEN", volume=lot, limit_price=price_sell,advanced=advanced)    all_trades_id = set()  # 记录所有的 trade_id    async with api.register_update_notify() as update_chan:        t = datetime.now().timestamp() #时间起点        if ping_zuo is not None:            order_id.append(ping_zuo.order_id)            last_price = quote.last_price            while ping_zuo.status != "FINISHED" or (ping_zuo.volume_orign - ping_zuo.volume_left) != sum(                [trade.volume for trade in ping_zuo.trade_records.values()]): #平昨单是否完成                if not advanced and not block :break #当日有效单,且无需等待是否完成                quote_volume = quote.ask_volume1 if kaiping in ["kaiduo","pingkong"else quote.bid_volume1                quote_time = (time_to_datetime(quote.datetime)+timedelta(minutes=close_minutes)).time()                #等待che_time秒还不成交撤单,或者价格偏离委托价n_price_tick不成交撤单,适用于advanced=None的情况                if ping_zuo.status != "FINISHED" and not advanced and (che_time>0 and n_price_tick<=0 and datetime.now().timestamp() - t >= che_time or n_price_tick>0 and che_time<=0 and                    ((ping_zuo.direction=="BUY" and quote.bid_price1 >= ping_zuo.limit_price+quote.price_tick*n_price_tick) or                   (ping_zuo.direction=="SELL" and quote.ask_price1 <= ping_zuo.limit_price-quote.price_tick*n_price_tick))                    and last_price != quote.last_price == quote.last_price                    or che_time>0 and n_price_tick>0 and datetime.now().timestamp() - t >= che_time and                    ((ping_zuo.direction=="BUY" and quote.bid_price1 >= ping_zuo.limit_price+quote.price_tick*n_price_tick) or                    (ping_zuo.direction=="SELL" and quote.ask_price1 <= ping_zuo.limit_price-quote.price_tick*n_price_tick))                     and last_price != quote.last_price == quote.last_price                    or (close_minutes and trading_time['night'and abs(t - quote_datetime.timestamp())<=close_minutes*2*60 and ( time_list[0] >= 24 and time(8) > quote_time >= night_end_time or time_list[0] < 24 and quote_time >= night_end_time))                   ):                    api.cancel_order(ping_zuo)                     while ping_zuo.status != "FINISHED":                        await update_chan.recv() #等待撤单完成,防止重复撤单                await update_chan.recv() #更新委托单                if isinstance(trade_chan,TqChan):                    rest_trades_id = set(ping_zuo.trade_records) - all_trades_id                    for trade_id in rest_trades_id:                        # 新收到的 trade 提取成字典发送到 chan                        d = {k: v for k, v in ping_zuo.trade_records[trade_id].items() if not k.startswith("_")}                        d.update({"signal_price":signal_price,"order_info":order_info})                        await trade_chan.send(d)                        all_trades_id.add(trade_id)            else:                day_order += 1 #报单数加1                volume = ping_zuo.volume_orign-ping_zuo.volume_left #成交手数                while volume > 0 and ping_zuo.trade_price != ping_zuo.trade_price: await update_chan.recv() #更新成交均价                if abs(ping_zuo.volume_left)>0 or ping_zuo.volume_orign<=0 or volume<=0 or not isinstance(volume,int) :che_count += 1 #撤单次数增加                last_msg += ping_zuo.last_msg                if volume > 0#有成交                    shoushu += volume #计算已成交手数                    junjia += ping_zuo.trade_price*volume        if ping_jin is not None:            order_id.append(ping_jin.order_id)            last_price = quote.last_price            while ping_jin.status != "FINISHED" or (ping_jin.volume_orign - ping_jin.volume_left) != sum(                [trade.volume for trade in ping_jin.trade_records.values()]): #平今单是否完成                if not advanced and not block :break                quote_volume = quote.ask_volume1 if kaiping in ["kaiduo","pingkong"else quote.bid_volume1                quote_time = (time_to_datetime(quote.datetime)+timedelta(minutes=close_minutes)).time()                if ping_jin.status != "FINISHED" and not advanced and (che_time>0 and n_price_tick<=0 and datetime.now().timestamp() - t >= che_time or n_price_tick>0 and che_time<=0 and                    ((ping_jin.direction=='BUY' and quote.bid_price1 >= ping_jin.limit_price+quote.price_tick*n_price_tick) or                   (ping_jin.direction=='SELL' and quote.ask_price1 <= ping_jin.limit_price-quote.price_tick*n_price_tick))                   and last_price != quote.last_price == quote.last_price                     or che_time>0 and n_price_tick>0 and datetime.now().timestamp() - t >= che_time and                   ((ping_jin.direction=='BUY' and quote.bid_price1 >= ping_jin.limit_price+quote.price_tick*n_price_tick) or                    (ping_jin.direction=='SELL' and quote.ask_price1 <= ping_jin.limit_price-quote.price_tick*n_price_tick))                   and last_price != quote.last_price == quote.last_price                        or (close_minutes and trading_time['night'and abs(t - quote_datetime.timestamp())<=close_minutes*2*60 and ( time_list[0] >= 24 and time(8) > quote_time >= night_end_time or time_list[0] < 24 and quote_time >= night_end_time))                   ):                    api.cancel_order(ping_jin)                     while ping_jin.status != "FINISHED":                        await update_chan.recv() #等待撤单完成                await update_chan.recv() #                if isinstance(trade_chan,TqChan):                    rest_trades_id = set(ping_jin.trade_records) - all_trades_id                    for trade_id in rest_trades_id:                        # 新收到的 trade 提取成字典发送到 chan                        d = {k: v for k, v in ping_jin.trade_records[trade_id].items() if not k.startswith("_")}                        d.update({"signal_price":signal_price,"order_info":order_info})                        await trade_chan.send(d)                        all_trades_id.add(trade_id)            else:                day_order += 1                volume = ping_jin.volume_orign-ping_jin.volume_left                while volume > 0 and ping_jin.trade_price != ping_jin.trade_price: await update_chan.recv()                if abs(ping_jin.volume_left)>0 or ping_jin.volume_orign<=0 or volume<=0 or not isinstance(volume,int) :che_count += 1 #撤单次数增加                last_msg += ping_jin.last_msg                if volume > 0:                    shoushu += volume                    junjia += ping_jin.trade_price*volume        if order is not None:            order_id.append(order.order_id)            last_price = quote.last_price            while order.status != "FINISHED" or (order.volume_orign - order.volume_left) != sum(                [trade.volume for trade in order.trade_records.values()]): #开仓是否完成                if not advanced and not block :break                quote_volume = quote.ask_volume1 if kaiping in ["kaiduo","pingkong"else quote.bid_volume1                quote_time = (time_to_datetime(quote.datetime)+timedelta(minutes=close_minutes)).time()                if order.status != "FINISHED" and not advanced and (che_time>0 and n_price_tick<=0 and datetime.now().timestamp() - t >= che_time or n_price_tick>0 and che_time<=0 and                    ((order.direction=='BUY' and quote.bid_price1 >= order.limit_price+quote.price_tick*n_price_tick) or                   (order.direction=='SELL' and quote.ask_price1 <= order.limit_price-quote.price_tick*n_price_tick))                   and last_price != quote.last_price == quote.last_price                     or che_time>0 and n_price_tick>0 and datetime.now().timestamp() - t >= che_time and                     ((order.direction=='BUY' and quote.bid_price1 >= order.limit_price+quote.price_tick*n_price_tick) or                      (order.direction=='SELL' and quote.ask_price1 <= order.limit_price-quote.price_tick*n_price_tick))                   and last_price != quote.last_price == quote.last_price                         or (close_minutes and trading_time['night'and abs(t - quote_datetime.timestamp())<=close_minutes*2*60 and ( time_list[0] >= 24 and time(8) > quote_time >= night_end_time or time_list[0] < 24 and quote_time >= night_end_time))                   ):                    api.cancel_order(order)                     while order.status != "FINISHED":                        await update_chan.recv() #等待撤单完成                await update_chan.recv() #                if isinstance(trade_chan,TqChan):                    rest_trades_id = set(order.trade_records) - all_trades_id                    for trade_id in rest_trades_id:                        # 新收到的 trade 提取成字典发送到 chan                        d = {k: v for k, v in order.trade_records[trade_id].items() if not k.startswith("_")}                        d.update({"signal_price":signal_price,"order_info":order_info})                        await trade_chan.send(d)                        all_trades_id.add(trade_id)            else:                day_order += 1                volume = order.volume_orign-order.volume_left                while volume > 0 and order.trade_price != order.trade_price: await update_chan.recv()                if abs(order.volume_left)>0 or order.volume_orign<=0 or volume<=0 or not isinstance(volume,int) :che_count += 1 #撤单次数增加                last_msg += order.last_msg                if volume > 0:                    shoushu += volume #计算已成交手数                    junjia = order.trade_price*volume        if shoushu:             if no_backtest: await asyncio.sleep(1)  #等待1秒,确保持仓更新,降低下单频率防范异常错误下频繁报撤单            #position的价格、仓位非同步更新,此处通过ctp查询以便进一步同步            while (position.pos_long != position.volume_long or position.pos_short != position.volume_short                 or position.pos_long and position.open_price_long != position.open_price_long                 or position.pos_short and position.open_price_short != position.open_price_short                or not position.pos_long and position.open_price_long == position.open_price_long                 or not position.pos_short and position.open_price_short == position.open_price_short): await update_chan.recv()            junjia = junjia/shoushu #计算成交均价            #junjia = round(junjia/shoushu,quote.price_decs) #保留和报价同样小数位            if kaiping in ['pingduo','pingkong'and order_close_chan:                if kaiping== 'pingduo':                    pos_c = position.pos_long #平仓后剩余多头手数                    price_c = position.open_price_long #平仓后剩余多头开仓均价                    #margin_c = position.margin_long #平仓后剩余多头保证金                elif kaiping=='pingkong':                    pos_c = position.pos_short #平仓后剩余空头手数                    price_c =position.open_price_short #平仓后剩余空头开仓均价                    #margin_c = position.margin_short #平仓后剩余空头保证金                if pos_c > 0:                    price_o = (pos0*price0 - pos_c*price_c)/shoushu #被平仓的理论开仓均价                else :price_o = price0                if kaiping=='pingduo':                    profit_count = junjia - price_o #盈利点数                elif kaiping=='pingkong'                    profit_count = price_o - junjia #盈利点数                count_percent = round(profit_count/price_o,5if price_o else 0 #盈利点率                #margin_release = (margin0 if margin0==margin0 else 0) - (margin_c if margin_c==margin_c else 0)#释放保证金,平仓释放的保证金为理论投入资金                #margin_release = (margin0 if margin0==margin0 else 0) * shoushu/pos0  #释放保证金,按仓位比计算,避免外部平仓产生影响                profit_money = shoushu * profit_count * quote.volume_multiple #盈利金额                if isinstance(order_close_chan,TqChan):                     #成交记录                    order_list = [symbol,pos_direction,price_o,junjia,shoushu,profit_count,profit_money,count_percent,                    signal_price,order_info,quote.datetime]                    await order_close_chan.send(order_list) #把计算后的结果发送出去        else#错单原因            junjia = float('nan')            #集合竞价,未到开盘时间等待60秒            if "拒绝" in last_msg and ("竞价" in last_msg or "竟价" in last_msg or "交易时间" in last_msg): await asyncio.sleep(60)            elif ("拒绝" in last_msg or "限制" in last_msg or "不足" in last_msg or "超过" in last_msg or "不支持" in last_msg or "低于" in last_msg                  or "权限" in last_msg or "平仓" in last_msg or "开仓" in last_msg):                order_wrong = True    #print(quote.datetime,'已经成交手数:',shoushu,'成交均价:',junjia,'品种:',symbol,'方向:',kaiping,'信号位:',signal_price,"order_info",order_info)    return {"shoushu":shoushu, "junjia":junjia, "che_count":che_count, "day_order":day_order, "symbol":symbol, "kaiping":kaiping, "lot":lot, "price":price_buy if kaiping in ["kaiduo","pingkong"else price_sell, "last_msg":last_msg,             "quote_volume":quote_volume,"profit_count":profit_count, "profit_money":profit_money, "order_id":order_id,"order_wrong":order_wrong,"signal_price":signal_price,"order_info":order_info,"quote":quote,"position":position} #返回成交手数、成交均价,和主动撤单次数,若无成交则均价为nan值
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » PeopleQuant帮助文档:最小开仓数量规定下如何实现自动调仓

猜你喜欢

  • 暂无文章