示例#1
0
    def get_relative_lh_temps(self):
        """Renvoie les conditions relatives de type temps."""
        mask = self.get_temps_cond().values
        assert sum(
            mask) == 2, f"mask={mask}, self.cond_frame={self.cond_frame}"

        sorted_cond = self.cond_frame.loc[mask, "value"].sort_values()
        low, high = sorted_cond.values

        return low - now(), high - now()
示例#2
0
    def position(self, symbol=None):
        data = {
            "account": "mlk",
            "symbol": self.symbol,
            "currency": self.symbol,
            "underlying": None,
            "quoteCurrency": "USD",
            "commission": None,
            "initMarginReq": None,
            "maintMarginReq": None,
            "riskLimit": None,
            "leverage": 1,
            "prevRealisedPnl": None,
            "prevClosePrice": None,
            "openingTimestamp": None,
            "currentTimestamp": None,
            "timestamp": now(),
            "avgEntryPrice": None,
            "lastPrice": self.current_dum_price,
            "currentCost": "?",
            "currentQty": 100,
            "liquidationPrice": "?",
            "breakEvenPrice": None,
        }

        return data
示例#3
0
    def get_exec_with_(self, srcKey_, minTransacTime, debug_=False):
        """
        Récupère les clOrdID associés à srcKey_ si ils existent.

        ajoute une condition de temps
        """
        _execution = self.execution()
        # Ordres exécutés et ordonnés dans l'ordre ascendant
        exec_orders = (_execution.loc[:, ["clOrdID", "transactTime"]]
                       if len(_execution) else [])
        if debug_:
            return _execution.loc[:, EXECOLS] if len(_execution) else []

        seenIDs: Set[str] = set()
        clOrdIDs = []

        if len(exec_orders):
            for i, clID in enumerate(exec_orders["clOrdID"]):
                clID_not_seen = clID not in seenIDs
                possible_srID = self.get_srcKey(clID) == srcKey_
                in_last_minute = exec_orders.iloc[i].loc["transactTime"] > (
                    now() - Timedelta("30s"))
                if clID_not_seen and possible_srID and in_last_minute:
                    clOrdIDs.append(clID)
                    seenIDs |= set([clID])

        return clOrdIDs
示例#4
0
    def fin_essai(self, i, n, close=False, dr_pause=None, dr_essai_theo=None):
        """
        Affiche les infos de finalisation de l'esssai et close quantity close.

        # on fait varie le temps d'attente entre les essais.
        # Il dépend du temps de l'essai
        """
        # info sur les résultats
        if close:
            # self.brg.cancel_and_close(quantity=close)
            # pourrai avoir un pb de timed out ici
            # sleep(randint(1, 3))
            pass

        # revoir le calcul de la balance
        self.resultats.loc[now(), "balance"] = self.balance()
        res_delta = self.resultats.iloc[-1] - self.resultats.iloc[-2]
        self.resultats.iloc[-1].loc["benef"] = res_delta.loc["balance"]

        # info sur la durée de l'essai
        self.logger.info(f"#### Fin de l'essai {i+1}/{n}, Résultats:\n"
                         f"{self.resultats.iloc[-1,:]}")

        # on ne fait une pause que si il reste assez du temps pour le faire
        if not self.oct.main_oc.timed_out() and i + 1 < n:
            self.pause(dr_pause, dr_essai_theo)
示例#5
0
    def pause(self, dr_pause, dr_essai_theo):
        """
        Défini un temps d'attente variable entre les différents essais.

        - dr_pause est en secondes
        """

        dr_pause = 10 if dr_pause is None else dr_pause

        try:
            dr_essai = now() - self.tpsDebEssai
            dr_delta = dr_essai - dr_essai_theo
            if dr_delta.seconds > 0:
                pause = dr_pause
            else:
                pause = dr_delta.seconds + dr_pause

            # attend au moins 10 secondes puis un nb aléatoire
            # qui suit une loi exponentielle de param dr_pause
            rnd_wait = np.floor(np.random.exponential(pause))
            dr_pause = pd.Timedelta(10 + rnd_wait, unit="s")

            self.logger.info(
                f"Temps de l'essai {dr_essai} (theo: {dr_essai_theo})."
                f"  Going to sleep for {dr_pause}.")

            sleep(dr_pause.seconds)
        except Exception as e:
            self.logger.error(f"{e} with dr_pause={dr_pause}, "
                              f"dr_essai_theo={dr_essai_theo} pause={pause}")
示例#6
0
    def hasbeen_hooked(self):
        """
        Update self.is_hooked.

        If new hooked update conditions relative to new price and time
        else return only hook
        """
        self.logger.debug(f"is_hooked={self.is_hooked} and condition,\n "
                          f"{self.condition.__repr__(short=False)}, "
                          f"cond is hooked ? {self.condition.is_hooked()}, "
                          f"hookedSrcID={self.condition.hookedSrcID}")

        if self.is_hooked:
            return True

        if self.condition.is_hooked():
            self.logger.info(f"self.condition={self.condition}")
            self.is_hooked = True
            self.startTime = now()
            self.logger.debug(f"* *Before update* * {self.__repr__(False)}")
            self.update_cond_with_relative_values()
            self.logger.info(
                f"{self} is hooking with ID '{self.condition.hookedSrcID}'\n"
                f"_Newly Updated conditions_:\n "
                f"- from:\nself.init_cond={self.init_cond_frame}\n"
                f"- to:\n{self.condition.__repr__(False)}")

        return None
示例#7
0
        def update_timeleft(timeout=timeOut, starttime=startTime, now_=None):
            """
            Update time left

            -now_: is sometime set by default.
            """
            _now = now_ if now_ else now()
            # convert to seconds +/-
            _timeleft = (timeout + starttime - _now).delta / 10e8

            return _timeleft if _timeleft > 0 else 0
示例#8
0
    def get_relative(self, genre_, op_):
        """Renvoie les nouvelles valeurs relative au prix et temps courant."""
        relative_values = self.get_relative_values()

        if genre_ == "price":
            cPrice = self.get_current_price()
            return cPrice + relative_values[genre_][op_]

        elif genre_ == "temps":
            cTime = now()
            return cTime + relative_values[genre_][op_]
示例#9
0
    def evalue_une_condition(self, cond):
        """
        Évalue une condition en fonction du genre.

        Les plus simple sont prix et temps.
        """
        if cond.genre in self.price_list:
            current_price = self.brg.prices(cond.genre)
            return self.evalue(current_price, cond.op, cond.value)

        elif cond.genre in ["temps"]:
            return self.evalue(now(), cond.op, cond.value)
        elif cond.genre == "hook":
            return self.evalue_un_hook(cond)
示例#10
0
    def get_current_variation(self):
        """Renvois le % de var des prix moyen pour la dernière et avant dernière timeBin."""
        if self.data is None:
            return 0, 0, 0
        # on suppose que le data à les données nécessaire pour les calculs suivants
        # définit les mask avec les unité de temps Unit Time (UT)
        oneUTAgo = now() - pd.Timedelta(self.timeBin, "s")
        twoUTAgo = now() - pd.Timedelta(2 * self.timeBin, "s")
        currUT_mask = (self.data.date > oneUTAgo).values
        prevUT_mask = (self.data.date > twoUTAgo).values & list(
            not_array(currUT_mask))

        mean_curr_price = self.data.loc[currUT_mask].refPrice.mean()
        mean_prev_price = self.data.loc[prevUT_mask].refPrice.mean()

        if any(pd.isna([mean_prev_price, mean_curr_price])):
            return 0, mean_prev_price, mean_curr_price
        else:
            return (
                (mean_curr_price - mean_prev_price) / mean_prev_price * 100,
                mean_curr_price,
                mean_prev_price,
            )
示例#11
0
    def get_bucketed_trades(
        self,
        startDate=None,
        endDate=None,
        binSize="1m",
        extra_filter=None,
        count=100,
        columns=None,
        reverse=True,
    ):
        """Returns historical data bucketed by binSize
        -  columns: timestamp, symbol,  open,  high,  low,  close,  trades,  volume,  vwap,\
        lastSize,  turnover,  homeNotional,  foreignNotional,
        - count
        - option for bin = [1m,5m,1h,1d].
        endDate and startDate format 2016-12-27T11:00Z (isoformat)"""
        if endDate is None:
            endDate = now().isoformat()
        if startDate is None:
            units = multiply_time_unit(24, TC[binSize])
            startDate = (pd.Timestamp(dt.datetime.now() -
                                      pd.Timedelta(units)).round(
                                          TC[binSize]).isoformat())

        path = "trade/bucketed"
        query = {
            "symbol": self.symbol,
            "binSize": binSize,
            "count": count,
            "startDate": startDate,
            "endDate": endDate,
            "reverse": reverse,
        }

        if columns is not None:
            query["columns"] = columns
        if extra_filter is not None:
            query["filter"] = extra_filter

        verb = "GET"

        if count < 750:
            sleep(HTTP_SIMPLE_RATE_LIMITE)
            trades = self._curl_bitmex(path, query=query, verb=verb)
        else:
            # paginer avec start et end date
            sleep(HTTP_SIMPLE_RATE_LIMITE)
            trades = self._curl_bitmex(path, query=query, verb=verb)
        return trades
示例#12
0
    def get_current_prices(
        self,
        price,
        refPrice,
        refTail=None,
        stopTail=None,
        flexTail=None,
        sOfsD=None,
        sOfsP=None,
        fOfsD=None,
        fScale=None,
    ):
        """Renvois les prix actuels pour remplir ou mettre à jour la df"""
        if refPrice is None:
            refPrice = self.get_refPrice()
        if refTail is None:
            refTail = self.get_refTail(refPrice)
        if stopTail is None:
            stopTail = refTail
        if sOfsD is None:
            # on pass refPrice car celui de la db is nan at this point
            sOfsD = self.get_stopTail_offset_delta(refPrice, stopTail)
        if sOfsP is None:
            sOfsP = self.get_stopTail_offset_per(refPrice, stopTail)

        if flexTail is None:
            flexTail = self.get_flexTail(refPrice, refTail)

        if fOfsD is None:
            fOfsD, fScale = self.flexTail_offset_delta(refPrice)

        current_prices = OrderedDict([
            ("date", now()),
            ("price", price),
            ("refPrice", refPrice),
            ("stopTail", stopTail),
            ("refTail", refTail),
            ("flexTail", flexTail),
            ("sOfsD", sOfsD),
            ("fScale", fScale),
        ])
        # # attention à l'ordre
        # current_prices = (now(), price, refPrice, stopTail, refTail, flexTail, sOfsD,
        #                   fOfsD, sOfsP, fScale)

        return pd.Series(current_prices)
示例#13
0
    def __init__(
        self,
        postOnly=POST_ONLY,
        live=False,
        symbol=SYMBOL,
        orderIDPrefix=ORDERID_PREFIX,
        timeout=TIMEOUT,
        logger=None,
        dbo=None,
    ):
        """Initialisation dbo is a dummy bitMEX object used for testing."""
        self.logger = get_logger(logger, name=__name__, sLL="INFO")

        self.symbol = symbol
        self.precision = PRICE_PRECISION[symbol]
        self.last_check_time = now() - Timedelta(
            2, unit="D")  # on s'assure d'être dans le passé.
        self.cached_refPrices = None

        self.live = live
        if self.live and dbo is None:
            baseUrl, apiKey, apiSecret = LIVE_URL, LIVE_KEY, LIVE_SECRET
        else:
            baseUrl, apiKey, apiSecret = TEST_URL, TEST_KEY, TEST_SECRET

        if dbo:
            self.bto = dbo
            self.dbo = dbo
        else:
            # si on ne passe pas l'argument dummy BitMEX object
            # On en crée un réel
            self.bto = BitMEX(
                base_url=baseUrl,
                symbol=self.symbol,
                apiKey=apiKey,
                apiSecret=apiSecret,
                orderIDPrefix=orderIDPrefix,
                postOnly=postOnly,
                timeout=timeout,
                logger=self.logger,
            )
            self.dbo = None

        self.logger.info(f"Fini init {self}")
示例#14
0
    def get_data(self, nomElt, temps="current", default_ret=None):
        """ Renvois un element du data si il existe.
        Si refPrice ou date demandé, envois des valeurs par défaut si n'a rien trouvé dans data
        """
        if self.data is not None:
            elt = self.data.loc[temps].loc[nomElt]
            if not pd.isna(elt):
                return elt

        if nomElt == "refPrice":
            return self.refPrice
        elif nomElt == "date":
            return now()
        elif nomElt in ["stopTail", "flexTail"]:
            if default_ret is None:
                self.logger.warning("Attention default_ret is none and used")
            return default_ret
        else:
            expmsg = f"Check nomElt={nomElt}, temps={temps} et data={self.data}"
            raise Exception(expmsg)
示例#15
0
    def __init__(
        self,
        send_queue,
        order,
        cond,
        valid_queue=None,
        logger=None,
        nameT=None,
        timeout=None,
        symbol="XBTUSD",
    ):
        """
        Une queue, pour passer les ordres, un ordre à passer si la condition est validée.
        - l'ordre (order) peut être stopé prématurement en mettant stop=True
        - order est un dict avec keys: side, orderQty..
        - un timeout pendant lequel l'ordre (dont l'évaluation de sa condition) est actif
        def 2 jours
        - hook : nom du hook, ou de l'abbrevation qui sert de hook.
        - sLL= debug level
        -symbol: symbol for this order, def. XBTUSD
        """
        Thread.__init__(self, name=nameT)

        self.logger = get_logger(logger, sLL="INFO", name=__name__)
        self.symbol = symbol
        self.send_queue = send_queue
        self.valid_queue = valid_queue

        self.condition = cond
        self.stop = False
        self.orderIDPrefix = "mlk_"
        self.order = order  # a dict ex. {'side': 'buy', 'orderQty': 100, 'options'...}
        self.oclid = newClID(abbv_=nameT)
        self.order["clOrdID"] = self.oclid

        # default for timeOut (2 days)
        # could add a timecond eg cVraieTpsDiffA(timeOut.seconds or delta)
        self.timeOut = setdef_timedelta(timeout, pd.Timedelta(2, unit="D"))
        self.startTime = now()
示例#16
0
    def start_server(self):
        """Démarre les services."""
        # canal échange ordre, serveur dispacheur
        self.fileDattente: Queue = Queue()

        # canal échange ordre, serveur dispacheur
        self.fileDeConfirmation: Queue = Queue()

        # connexion avec Bitmex
        self.brg: Bargain = Bargain(live=self.live,
                                    logger=self.logger,
                                    dbo=self.dbo,
                                    symbol=self.symbol)
        # Serveur dispacheur d'ordre
        self.chrs: Chronos = Chronos(
            self.brg,
            self.fileDattente,
            self.fileDeConfirmation,
            logger=self.logger,
        )
        self.chrs.start()
        # Resultats financiers
        self.resultats.loc[now(), :] = (self.balance(), np.nan)
示例#17
0
 def elapsed_time(self):
     """Return the elapsed time in seconds."""
     return now() - self.startTime
示例#18
0
    def prices(self, typeprice=None, side="buy", symbol_=None):
        """
        Show summary of current prices.

        typeprice can be 'delta', 'indexPrice', 'market', 'askPrice',

        'midPrice', 'ref_delta','market_maker', 'lastMidPrice' or None
        (for all instrument prices)
        - symbol if not user bargain symbol but maybe could want other price
        """
        _symbol = self.symbol if symbol_ is None else symbol_

        prices = {
            k: v
            for (k, v) in self.bto.instrument(_symbol).items() if "rice" in k
        }
        # prices.keys = 'maxPrice', 'prevClosePrice', 'prevPrice24h', 'highPrice',
        # 'lastPrice', 'lastPriceProtected', 'bidPrice', 'midPrice', 'askPrice',
        # 'impactBidPrice', 'impactMidPrice', 'impactAskPrice', 'markPrice',
        # 'markPrice', 'indicativeSettlePrice', 'lowPrice',
        # execInst, MarkPrice, LastPrice, IndexPrice

        ret = None
        typeprice = "" if typeprice is None else typeprice

        if typeprice == "delta":
            ret = prices["askPrice"] - prices["bidPrice"]

        elif typeprice.lower() == "indexprice":
            # S'assurer qu'il n'y a pas deux appels consécutif à moins de x seconde
            # minisytème de cache  jusquà 11s avant nouvel appel au broker
            timeLaps = now() - self.last_check_time
            msg = (f'Checking cached price'
                   f' timeLaps={timeLaps}, now={now()},'
                   f' last_check_time={self.last_check_time}'
                   f' self.cached_refPrices={self.cached_refPrices}')

            if (self.cached_refPrices is None
                    or timeLaps > Timedelta(randint(2, 11), unit="s")
                    or self.bto.dummy):
                cached_refPrices = self.get_most_recent_settlement_price()
                self.last_check_time = now()

                self.cached_refPrices = cached_refPrices
                msg += f'>>>> New Cached_refPrices={cached_refPrices} <<<<.'

            self.logger.debug(msg)

            ret = self.cached_refPrices

        elif typeprice.lower() == "lastprice":
            # askPrice > bidPrice
            ret = prices["askPrice"] if side == "buy" else prices["bidPrice"]
        elif typeprice == "market_maker":
            ret = prices["bidPrice"] if side == "buy" else prices["askPrice"]
        elif typeprice.lower() == "lastmidprice":
            ret = self.prices("midPrice")  # this is close to the last price
        elif typeprice.lower() in ["market", "market_price", "markprice"]:
            # fairePrice is the marketPrice dans XBT check markMethod
            ret = prices["markPrice"]
        elif typeprice == "ref_delta":
            ret = self.prices("midPrice") - self.prices("indexPrice")
        elif typeprice:
            ret = prices[typeprice]

        return prices if not ret else round_sprice(ret, self.symbol)
示例#19
0
    def wait_for_change(
        self,
        valconditions=None,
        rcvload=None,
        timeout: pd.Timedelta = None,
        waitstep: float = 1,
    ):
        """
        Boucle qui attend de recevoir dans order execution from ws.

        ordertype: orderstatus.
        - timeout doit être un pd.Timedelta,
        - waitStep en second
        ordertype is 'ordStatus our triggered et ordstatus is
        """
        self.logger.debug("Thread started.")

        # defaults
        timeOut = setdef_timedelta(timeout, default=pd.Timedelta(60, unit="m"))

        clOrdID = (self.brg.bto.dummyID if self.brg.bto.dummy else
                   self.get_ID_from(rcvload, "clOrdID"))

        startTime = now()

        def update_timeleft(timeout=timeOut, starttime=startTime, now_=None):
            """
            Update time left

            -now_: is sometime set by default.
            """
            _now = now_ if now_ else now()
            # convert to seconds +/-
            _timeleft = (timeout + starttime - _now).delta / 10e8

            return _timeleft if _timeleft > 0 else 0

        timeLeft = update_timeleft()

        while timeLeft > 0 and not self.is_changed_(clOrdID, valconditions):
            sleep(waitstep)
            timeLeft = update_timeleft()
            if timeLeft % 298 == 0:
                # logging every 4:58
                self.logger.info(f"timeLeft={timeLeft}s. Still waiting...")
                sleep(1)  # avoid too much logging and throwtlle the system

        # block until next reply
        reply = self.wait_for_reply(timeout=timeLeft)

        # problème avec les reply None

        if rcvload["order"]["ordType"] == "cancel":
            self.logger.info(f"ordType is cancel. rcvload={rcvload}")
            replyID = rcvload["order"]["clOrdID"]
        else:
            # mais si ce n'est pas la bonne reply ?
            replyID = (rcvload["order"]["clOrdID"] if reply is None else
                       self.get_ID_from(reply, "clOrdID"))

        seenReplyIDs: Dict[Any, Any] = {}

        while replyID != clOrdID and timeLeft:
            # saving ID already seen to avoid clutering logs
            if replyID is not None:
                seenReplyIDs[replyID] = seenReplyIDs.get(replyID, 0) + 1

            if seenReplyIDs.get(replyID, 0) < 2:
                self.logger.debug(f"*No match ID* for clOrdID={clOrdID}"
                                  f" in {trim_dic(reply, trimid=12)}!")
            if reply:
                # to be sure not to put Nones back in the loop
                self.reply_queue.put(reply)

            try:
                # can get None here, not good... if timeout..
                reply = self.wait_for_reply(timeout=timeLeft)
                replyID = (clOrdID if reply is None else self.get_ID_from(
                    reply, "clOrdID"))
            except Exception as e:
                self.logger.error(
                    f"reply={reply}, timeLeft={timeLeft}, replyID={replyID}"
                    f"clOrdID={clOrdID}")
                raise (e)

            timeLeft = update_timeleft()

            self.logger.debug(
                f"Getting out of the loop. seenReplyIds={seenReplyIDs},"
                f" timeLeft={timeLeft}, reply={reply}.")

        # ici il y a en fait le cas des reply error d'ammending
        validation = (reply if timeLeft and reply is not None
                      and not reply.get("error", False) else False)

        self.logger.info(
            f"Attendu {timeOut - pd.Timedelta(timeLeft, unit='s')}."
            f"Validation is {bool(validation)}.")

        self.valid_queue.put({
            "brokerReply": reply,
            "exgLoad": rcvload,
            "execValidation": validation
        })
示例#20
0
    def go(
        self,
        tps_run,
        prix,
        essais,
        side,
        q,
        tp,
        atype,
        oType=False,
        nameT=None,
        updatepause=None,
        logpause=None,
        dr_pause=None,
        tType=None,
        timeout=None,
        sDelta=None,
        hook=None,
    ):
        """
        Loop over three conditions.

        - times interval in offset from now,
        - a prix interval in offset from now
        - and the number of essais.
        Will try to lauch a trailorder avec les même paramètres
        utiliser des nombres <0 si nécessaire

        - atype define if price and quantity are set in %.
        use the string %p%q to set price and/or q in %
        sinon le prix est définie en différentiel par rapport au indexPrice price
        q is in % of available margin in [0,100]
        - tp peut être en % (defaut), absolue ou différentielle
        ajouter tA ou tD and les atypes
        - le temps est en minutes
        - si présent dr_pause définie le temps d'attendre entre deux ordres
        - hook: un tuple (srcName: ordre sur lequel se hooker et s
        rcStatus, le satus que cette ordre doit avoir pour déclencher le hook
        srcName doit être un nom d'un autre ordre.   La fin de src name est -S ou
        -P pour distinguer les ordres primaire ou secondaire.
        puis _ et un lettre pour le status   eg. src-S_C
        """
        # self.logger.info(
        #     f"#### Go : tps_run={tps_run}, prix={prix}, essais={essais},"
        #     f" side={side}, q={q}, tp={tp}, atype={atype}, oType={oType},"
        #     f" tType={tType}, sDelta={sDelta}, dr_pause={dr_pause}m,"
        #     f" timeOut={timeout} m, balance={self.balance()}"
        # )
        _info = {
            "tps_run": tps_run,
            "prix": prix,
            "essais": essais,
            "side": side,
            "q": q,
            "tp": tp,
            "atype": atype,
            "oType": oType,
            "tType": tType,
            "sDelta": sDelta,
            "dr_pause": dr_pause,
            "timeout": f"{timeout}m",
            "balance": self.balance(),
        }
        self.logger.info(f"#### Go with args :\n{pd.Series(_info)}")

        # Init. des paramètres temps pour la condition de validité de l'ocp
        self.tpsDeb = now() + pd.Timedelta(tps_run[0], unit="m")
        self.tpsFin = now() + pd.Timedelta(tps_run[1], unit="m")
        tfmt = "%Y-%m-%dT%H:%M"
        repTpsDeb = self.tpsDeb.strftime(tfmt)
        repTpsFin = self.tpsFin.strftime(tfmt)

        # #### Setting defaults ####

        # Infos sur la durée moyenne de l'essai attendue.
        if pd.isna(dr_pause):
            dr_pause = 0
            dr_moy = (self.tpsFin - self.tpsDeb) / essais
        else:
            dr_moy = pd.Timedelta(dr_pause, unit="m")

        dr_essai_theo = dr_moy + pd.Timedelta(30, unit="s")

        if pd.isna(timeout):
            timeOut = (self.tpsFin - self.tpsDeb) / essais
        else:
            timeOut = pd.Timedelta(timeout, unit="m")

        if pd.isna(sDelta):
            sDelta = 2

        # Expanding shortcut for ref price type:
        opType, ordType, execInst = price_type_trad(
            oType, side)  # decl du main order.
        tpType, tOrdType, tExecInst = price_type_trad(tType,
                                                      side)  # decl du stop.

        # #### On commence la boucle qui va gérer le run.
        # i.e. la relance des ordres pendant la période de validité des conditions
        i = 0
        while i < essais and not self.stop:
            self.tpsDebEssai = now()  # on sauvegarde le temps de départ

            # Traitement des paramètres de l'ordre suivant atype
            self.logger.info(
                f"oType={opType, ordType, execInst}, tType={tpType, tOrdType, tExecInst}"
            )
            oPrices, _q, _tp, tailPrices = set_order_args(
                prix,
                q,
                tp,
                atype,
                self.brg,
                opType,
                tpType,
                recompute=True,
                side=side,
                symbol=self.symbol,
            )

            _info = {
                "Balance": f"{self.balance()}$",
                "tps_run": (repTpsDeb, repTpsFin),
                "début": self.tpsDebEssai,
                "pause": dr_moy,
                "hook": hook,
                "oPrices": oPrices,
                "tPrice": tailPrices,
                "side": side,
                "q": _q,
                "tp": round(_tp, 4),
                "sDelta": sDelta,
                "opType": (opType, ordType, execInst),
                "tpType": (tpType, tOrdType, tExecInst),
                "timeOut": timeOut,
            }
            self.logger.info(
                f"### Essais {i+1}/{essais}, ({nameT}):\n{pd.Series(_info)}")

            # L'order Price type (déclencheur pour Touched & stop) est déjà dans execInst
            order = create_order(side, _q, opType, ordType, execInst, oPrices,
                                 sDelta)

            # On initialise les arguments condition pour les ordres principaux
            kwargs = {
                "send_queue":
                self.fileDattente,
                "order":
                order,
                "cond":
                cVraieTpsDeA(self.brg,
                             self.tpsDeb,
                             self.tpsFin,
                             logger=self.logger),
                "valid_queue":
                self.fileDeConfirmation,
                "logger":
                self.logger,
                "nameT":
                f"{nameT}-PO",
                "timeout":
                timeOut,
                "symbol":
                self.symbol,
            }

            self.logger.debug(f"~~~~ Order = {order}")
            if hook:
                _hSrc, _status = hook.split("_")
                _hStatus = ordStatusTrans[_status]
                # hook formé du 'nom-S_F' avec code ou nom-P_C eg.
                # <nom>-<src_secondair|src_principal>_<ordTargetStatus>-<SO|PO>id..
                self.ocp = HookOrder(
                    hSrc=_hSrc,
                    hStatus=_hStatus,
                    excludeIDs_=self.hookedIDs,
                    **kwargs,
                )

                # add hook conditions
                hookCond = cHook(self.brg,
                                 _hSrc,
                                 _hStatus,
                                 exclIDs=self.hookedIDs)
                self.ocp.add_condition(hookCond)
                self.ocp.condition.set_excludeClIDs(self.hookedIDs)
            else:
                self.ocp = OrderConditionned(**kwargs)

            self.logger.debug(f"~~~~ Order = {order}")

            # On ajoute la condition de prix
            # ou ["LastPrice", 'IndexPrice', 'MarkPrice']
            self.ocp.add_condition(
                cVraiePrixDeA(self.brg,
                              opType,
                              oPrices[0],
                              oPrices[1],
                              logger=self.logger))

            msg = f"### OrdrePrincipal défini ### {self.ocp.__repr__(short=False)}"
            self.logger.info(msg)

            # on accroche un stop (tail, trail) suiveur à l'ordre principal
            self.oct: TrailStop = TrailStop(
                self.ocp,
                self.brg,
                pegOffset_perc=_tp,
                updatepause=updatepause,
                logger=self.logger,
                logpause=logpause,
                nameT=f"{nameT}-SO",
                refPrice=tpType,
                execinst=tExecInst,
                ordtype=tOrdType,
            )

            # on active le tout, à partir de la tail
            try:
                self.oct.start()
                self.oct.join()

                # il s'agit ensuite de bloquer jusqu'à execution du stoptrace.
                # cela nécessite, conditions de l'ocp et du stop validées

            except Exception as e:
                self.logger.warning("#### Exception %s. Stopping -->\n%s" %
                                    (e, self.oct.condition))
                self.fin_des_essais("ERROR", close=False)
                break

            self.fin_essai(
                i,
                essais,
                close=False,
                dr_pause=dr_pause,
                dr_essai_theo=dr_essai_theo,
            )
            # avant d'essaie aussi et s'assurer que le stop a bien été exécuté
            i += 1

            if hook:
                # On sauvegarde les clOrdID  qui on déclenché le hook
                self.hookedIDs |= set([self.ocp.condition.hookedSrcID])
                self.logger.info(f">>>> Updating hookedIDs={self.hookedIDs}")

        self.fin_des_essais(essais, close=False)
        sys.exit()