ГлавнаяПостроение системы › Построение системы. Алгоритм исполнения ордеров-2

Построение системы. Алгоритм исполнения ордеров-2

book2

Продолжение. Первая часть см. здесь.

Действия по тику

Тик получаем из API, когда какая-то часть стакана обновляется ( лучший бид или аск, либо их объемы). Внутри кода взаимодействия с API (tws) есть подпрограмма, которая следит за приходом такого обновления и вызывает соответсвующую функцию.

Что делает такая функция? Если мы выставляем пассивный ордер ( помните, это состояние по умолчанию):

  • ... и прошло более 5 минут - меняем ордер на агрессивный.
  • если мы покупаем и текущий лучший бид уходит вверх от стартового состояния, меняем ордер на агрессивный
  • если продаем, и текущий лучший аск уходит вниз - также меняем на агрессивный
  • если мы видим нежелательный дисбаланс ордеров ( например, если количество ордеров на продажу в 5 раз превышает количество ордеров на покупку, в случае, когда мы также продаем) - меняем на агрессивный
     

Если мы выставляем агрессивный ордер

  • ... и более 10 минут проходит, отменяем ордер
  • если покупаем и текущий лучший аск движется вверх от стартового состояния, обновляем цену нашего лимитного ордера на равную новому лучшему аску ( движемся за рынком)
  • если продаем и текущий лучший бид движется вниз, обновляем цену нашего ордера на равную лучшему биду.
passivetimelimit=5*60 ## макс 5 мин
totaltimelimit=10*60 ## макс другие 5 мин для агрессивного состояния
maximbalance=5.0 ## величина дисбаланса ордеров
 
def EasyAlgo_on_tick(dbtype, orderid, marketdata, tws, contract):
    """
    Эта функция вызывается на каждый тик

    Args: 
    dbtype, tws: обозначают базу данных и API
    orderid: идентификационный номер, ассоциированный с тиком
    marketdata: текущая маркет дата (стакан)
    contract: контракт, который мы торгуем

    """
    ## Диагностический код
    log=logger()
    diag=diagnostic(dbtype, system="algo",  system3=str(int(orderid)))
    s=state_from_sdict(orderid, diag, log)   

    ## Достаем информацию о нашем ордере из базы данных

    am=algo_memory_table(dbtype)
    trade=am.read_value(orderid, "Trade")
    current_limit=am.read_value(orderid, "Limit")
    Started=am.read_value(orderid, "Started")
    Mode=am.read_value(orderid, "Mode")
    lastsideprice=am.read_value(orderid, "ValidSidePrice")
    lastoffsideprice=am.read_value(orderid, "ValidOffSidePrice")
    LastNotice=am.read_value(orderid, "LastNotice")
 
    ## если не находим ордер в базе данных

    if Mode is None or Started is None or current_limit is None or trade is None or LastNotice is None:
        log.critical("Can't get algo memory values for orderid %d CANCELLING" % orderid)
        FinishOrder(dbtype, orderid, marketdata, tws, contract)
       
    Started=float_as_date(Started)
    LastNotice=float_as_date(LastNotice)
    timenow=datetime.datetime.now()

    ## если покупка, получаем лучший аск (sideprice) и лучший бид (offsideprice)
    ## если продажа, получаем лучший бид (sideprice) и лучший аск (offsideprice)
    (sideprice, offsideprice)=get_price_sides(marketdata, trade)

    s.update(dict(limit_price=current_limit, offside_price=offsideprice, side_price=sideprice,
                  Mode=Mode))

    ## получаем время, сколько мы торгуем и время с последнего уведомления 'LastNotice'


    time_trading=(timenow - Started).total_seconds()
    time_since_last=(timenow - LastNotice).seconds

    ## если прошла минута


    if time_since_last>60:
        s.update(dict(message="One minute since last noticed now %s, total time %d seconds - waiting %d %s %s" % (str(timenow), time_trading, orderid, contract.code, contract.contractid)))
        am.update_value(orderid, "LastNotice", date_as_float(timenow))

    ## если мы вышли за максимальное время - сбрасываем ордера

    if time_trading>totaltimelimit:
        s.update(dict(message="Out of time cancelling for %d %s %s" % (orderid, contract.code, contract.contractid)))
        FinishOrder(dbtype, orderid, marketdata, tws, contract)
        return -1

    if not np.isnan(sideprice) and sideprice<>lastsideprice:
        am.update_value(orderid, "ValidSidePrice", sideprice)
   
    if not np.isnan(offsideprice) and offsideprice<>lastoffsideprice:
        am.update_value(orderid, "ValidOffSidePrice", offsideprice)

    am.close()


    if Mode=="Passive":

        ## время вышло для пассивного ордера (5 минут)

        if time_trading>passivetimelimit:
            s.update(dict(message="Out of time moving to aggressive for %d %s %s" % (orderid, contract.code, contract.contractid)))

            SwitchToAggresive(dbtype, orderid, marketdata, tws, contract, trade)
            return -1

        if np.isnan(offsideprice):
            s.update(dict(message="NAN offside price in passive mode - waiting %d %s %s" % (orderid, contract.code, contract.contractid)))
            return -5

        if trade>0:
            ## Покупка
            if offsideprice > current_limit:
                ## с момента постановки ордера цена ушла вверх
               
                s.update(dict(message="Adverse price move moving to aggressive for %d %s %s" % (orderid, contract.code, contract.contractid)))

                SwitchToAggresive(dbtype, orderid, marketdata, tws, contract, trade)
               
                return -1
        elif trade<0:
            ## Selling
            if offsideprice < current_limit: ## с момента постановки ордера цена ушла вниз

                s.update(dict(message="Adverse price move moving to aggressive for %d %s %s" % (orderid, contract.code, contract.contractid)))

                SwitchToAggresive(dbtype, orderid, marketdata, tws, contract, trade)
                return -1

        ## Обнаружен дисбаланс (объем бид/объем аск если покупаем, наоборот - если продаем)

        balancestat=order_imbalance(marketdata, trade)
       
        if balancestat > maximbalance:
                s.update(dict(message="Order book imbalance of %f developed compared to %f, switching to aggressive for %d %s %s" %(balancestat , maximbalance, orderid, contract.code, contract.contractid)))

                SwitchToAggresive(dbtype, orderid, marketdata, tws, contract, trade)
                return -1
           
    elif Mode=="Aggressive":

    ## агрессивный ордер
        if np.isnan(sideprice):
            s.update(dict(message="NAN side price in aggressive mode - waiting %d %s %s" % (orderid, contract.code, contract.contractid)))
            return -5

        if trade>0:
            ## Покупка
            if sideprice > current_limit:
                ## с момента постановки ордера цена ушла вверх. Переставляем ордер

                s.update(dict(message="Adverse price move in aggressive mode for %d %s %s" % (orderid, contract.code, contract.contractid)))
                SwitchToAggresive(dbtype, orderid, marketdata, tws, contract, trade)
               
                return -1
        elif trade<0:
            ## Selling
            if sideprice < current_limit ## с момента постановки ордера цена ушла вниз. Переставляем ордер

                s.update(dict(message="Adverse price move in aggressive mode for %d %s %s" % (orderid, contract.code, contract.contractid)))

                SwitchToAggresive(dbtype, orderid, marketdata, tws, contract, trade)
                return -1
           
    elif Mode=="Finished":
        ## ничего не делаем
        pass

    else:
        msg="Mode %s not known for order %d" % (Mode, orderid)
        s.update(dict(message=msg))

        log=logger()
        log.critical(msg)
        raise Exception(msg)

    s.update(dict(message="tick no action %d %s %s" % (orderid, contract.code, contract.contractid)))

    diag.close()

    return 0

def SwitchToAggresive(dbtype, orderid, marketdata, tws, contract, trade):
    """
    действия при смене текущего ордера на агрессивный или передвижение агрессивного ордера
    """
    ## diagnostics...
    log=logger()
    diag=diagnostic(dbtype, system="algo",  system3=str(int(orderid)))
    s=state_from_sdict(orderid, diag, log)   

    if tws is None:
        log.info("Switch to aggressive didn't get a tws... can't do anything in orderid %d" % orderid)
        return -1
    
    ## Получаем последнюю лучшую цену (равную цене при пересечении спреда) которая будет ценой нашего нового ордера

    am=algo_memory_table(dbtype)

    sideprice=am.read_value(orderid, "ValidSidePrice")

    ordertable=order_table(dbtype)
    order=ordertable.read_order_for_orderid(orderid)
    ordertable.close()
   
    if np.isnan(sideprice):
        s.update(dict(message="To Aggressive: Can't change limit for %d as got nan - will try again" % orderid))
        return -1
    
    ## обновляем ордер

    newlimit=sideprice

    order.modify(lmtPrice = newlimit)
    order.modify(orderType="LMT")
   
    iborder=from_myorder_to_IBorder(order)
    ibcontract=make_IB_contract(contract)

    am.update_value(order.orderid, "Limit", newlimit)
    am.update_value(order.orderid, "Mode", "Aggressive")
    am.close()

    tws.placeOrder(
            orderid,                                    # orderId,
            ibcontract,                                   # contract,
            iborder                                       # order
        )

    s.update(dict(limit_price=newlimit, side_price=sideprice,
                  message="NowAggressive", Mode="Aggresive"))


    return 0

def FinishOrder(dbtype, orderid, marketdata, tws, contract):
    """
    алгоритм не сработал, отменяем ордер
    """    diag=diagnostic(dbtype, system="algo",  system3=str(int(orderid)))

    s=state_from_sdict(orderid, diag, log)        log=logger()

    if tws is None:
        log.info("Finish order didn't get a tws... can't do anything in orderid %d" % orderid)
        return -1


    log=logger()
    ordertable=order_table(dbtype)

    order=ordertable.read_order_for_orderid(orderid)
   
    log.info("Trying to cancel %d because easy algo failure" % orderid)
    tws.cancelOrder(int(order.brokerorderid))
   
    order.modify(cancelled=True)
    ordertable.update_order(order)

    do_order_completed(dbtype, order)           
   
    EasyAlgo_on_complete(dbtype, order, tws)

    s.update(dict(message="NowCancelling", Mode="Finished"))

    am=algo_memory_table(dbtype)
    am.update_value(order.orderid, "Mode", "Finished")
    am.close()
   
    return -1

Частичное или полное сведение ордеров

def EasyAlgo_on_partial(dbtype, order, tws):
   """
   Функция запускается при частичном сведении ордера
    """
    diag=diagnostic(dbtype, system="algo",  system3=str(int(order.orderid)))

    diag.w(order.filledtrade, system2="filled")
    diag.w(order.filledprice, system2="fillprice")
   
    return 0


def EasyAlgo_on_complete(dbtype, order_filled, tws):
    """
   Функция запускается при полном сведении ордера
    """
    diag=diagnostic(dbtype, system="algo",  system3=str(int(order_filled.orderid)))
   
    diag.w("Finished", system2="Mode")
    diag.w(order_filled.filledtrade, system2="filled")
    diag.w(order_filled.filledprice, system2="fillprice")

    am=algo_memory_table(dbtype)
    am.update_value(order_filled.orderid, "Mode", "Finished")
    am.close()

    return 0

Заключение

Представленный алгоритм не является идеальным, но при его использовании автором затраты на исполнение ордеров снизились на 80% по сравнению с выставлением только маркет ордеров, то есть в среднем плата за исполение составила только 1/10 спреда. Это определенно серьезное улучшение торгового алгоритма и рекомендуется к применению для алгоритмической торговли.

3 Комментарии[ Ваш комментарий ]

  1. Получается фактически главным параметром исполнения ордера является время удержания ордера.

  2. Не понятный момент 

    Если мы выставляем агрессивный ордер

    ... и более 10 минут проходит, отменяем ордер

    Если агрессивный ордер это, например, выставление ордера на покупку по ask, то как он может быть не исполнен в течении 10 минут? 

    • это бывает очень часто. Ваши ордера попадают на биржу с некоторой задержкой, и может быть так, что ask уже поднялся выше и ваша заявка стала в обычную бидовую очередь
       

Сообщение

Обратите внимание: вы можете использоватьHTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>