def get_balance(self, atPrice="satoshi"): """ Calcule la balance en satoshi (def), usd ou xbt atPrice. Si atPrice is None, use market buy sell or mid price Voir aussi abonnement "wallet" """ satoshi_balance = self.bto.margin( )["availableMargin"] * self.get_leverage() xbt_balance = satoshi_balance * XBTSATOSHI if isinstance(atPrice, str): atPrice = atPrice.lower() if "usd" in atPrice: # renvois la balance en USD if "buy" in atPrice: current_price = self.prices("market", "buy") elif "sell" in atPrice: current_price = self.prices("market", "sell") else: current_price = self.prices("midPrice") return round_sprice(xbt_balance * current_price, self.symbol) elif "xbt" in atPrice: return xbt_balance elif is_number(atPrice): # atPrice should be float return round_sprice(xbt_balance * atPrice, self.symbol) elif atPrice is not None: raise Exception("atPrice should be str number or None") return satoshi_balance
def flexTail_offset_delta(self, refPrice=None): """Calcul l'offset flexible qui est fonction des variation des prix.""" refPrice = self.get_refPrice(refPrice) refTail = self.get_refTail(refPrice) base_ofs = refTail - refPrice current_var, currP, prevP = self.get_current_variation() scale = self.get_scale(current_var) flexOfs = round_sprice(scale * base_ofs, self.symbol) # logmsg = (f'Offset_delta: prevP={round(prevP, 2)}, currP={round(currP, 2)},' # f' var={round(current_var,2)}, scale={scale}, base_ofs={base_ofs},' # f' flexOfs={flexOfs}, refPrice={refPrice}') # self.logger.debug(logmsg) return round_sprice(flexOfs, self.symbol), round(scale * 100, 4)
def amend_trailstop(brg, order, newPegOffsetValue): """Amend la pegOffsetValue (le delta de la sécurité (hook, crochet) par rapport au prix du marché). Params: - brg <Bargain Object>:, - order <str>: one existing bitmex order, peut se résumé à l'essentiel {'orderID':...} - pegOffsetValue <int>: le delta, si >0 trigger price au dessus, (donc orderQty doit être <0 ou side sell).""" mlogger.info(f"order={order}, newPegOffsetValue={newPegOffsetValue}") newPegOffsetValue = round_sprice(newPegOffsetValue) return brg.bto.amend(order, pegOffsetValue=newPegOffsetValue, pegPriceType="TrailingStopPeg")
def get_stopTail_offset_delta(self, refPrice=None, stopTail=None): """ Renvois l'écart (en valeur) entre le prix de référence actuelle et la stopTail actuelle. Return >0 si stopTail au dessus, <0 sinon. S'assure que les prix sont définis """ refPrice = self.get_refPrice(refPrice) tail = self.get_data(nomElt="stopTail", default_ret=stopTail) if (tail - refPrice) == 0: return 0 else: offset = round_sprice(tail - refPrice, self.symbol) return offset
def amend_stop_price(brg, orderID, newStopPx): """Amend the stop price of an order. Params: - brg <Bargain Object>: - orderID <str>: one existing bitmex order, -newStopPx <int>: new stop price.""" # mlogger.debug(f'orderID={orderID}, newStopPx={newStopPx}, opts={opts}') newStopPx = round_sprice(newStopPx) try: order = {"orderID": orderID} # format à vérifier pour harmonisation return brg.bto.amend(order, stopPx=newStopPx) except Exception as e: raise (e)
def get_refTail(self, refPrice=None): """Renvois la queue de référence. (la bleue). Celle ci ne change pas d'épaisseur mais suit le prix de reférence dans ses variations. Elle sert à déclancher la mise à jour du stopTail lorsque la nouvelle Tail sera sortie du bois (ie assure une prise)""" refPrice = self.get_refPrice(refPrice) sens = 1 if self.head.lower() == "buy" else -1 epaisseur = refPrice * self.tail_perct_init / 100 ofs = -sens * epaisseur refTail = round_sprice(refPrice + ofs, self.symbol) # logmsg = f'refPrice={refPrice}, refOfs={round(ofs,2)}, sens={sens}, direction head={self.head} --> refTail={refTail}' # self.logger.debug(logmsg) return refTail
def place(brg, side, orderQty, price, **opts): """ Place a limit order. - brg a Bargain, - side, - orderQty in contract, - price """ # by default ordType='Limit' price = round_sprice(price) mlogger.debug( f"brg={brg}, orderQty:{orderQty}, side={side}, opts={opts}, price={price}" ) return brg.bto.place(orderQty=orderQty, side=side, price=price, asBulk=True, **opts)
def get_flexTail(self, refPrice=None, refTail=None): """ Compute the green tail flexible tail thickness. C'est un nombre en minTail et maxTail qui dépend du dernier delta si le tableau est rempli. JE me base sur ça pour calculer l'offset """ if pd.isna(refTail): refTail = self.get_refTail(refPrice) # if self.enought_data(): if True: refPrice = self.get_refPrice(refPrice) flexOfs, scale = self.flexTail_offset_delta(refPrice) flexTail = refPrice + flexOfs return round_sprice(flexTail, self.symbol) else: return refTail
def amend_price(brg, orderID, newPrice): """ Amend the price of an order. Params: - brg <Bargain Object>:, - order <str>: one existing bitmex order, -newPrice <int> """ # mlogger.debug(f'orderID={orderID}, newStopPx={newStopPx}, opts={opts}') newPrice = round_sprice(newPrice) try: order = {"orderID": orderID} # format à vérifier pour harmonisation return brg.bto.amend(order, price=newPrice) except Exception as e: mlogger.exception(e) # le staop est probablement déjà déclenché. return { "orderID": orderID, "clOrdID": orderID, "error": "Amending error" }
def retry( self, path, error=None, query=None, postdict=None, timeout=None, verb=None, rethrow_errors=False, max_retries=None, fast=False, exception=None, ): """Renvois la réponse du borker après un retry Relance l'ordre un certain nombre de fois, un après avoir attendu quelques secondes. Nécessite les même argurments que _curl_bitmex et dans le même ordre""" self.retries += 1 if self.retries >= max_retries: raise ke.MaxRetries(e=exception, load=postdict, extra=f"{path}: Max retries hit") tps1 = random.uniform(1, 2) tps2 = random.uniform(4, 10) if self.retries > 3 else 0 tps3 = random.uniform(20, 80) if self.retries > 6 else 0 self.logger.warning( f"retry {verb} {path} {self.retries}/{max_retries}: " f"Pause de {round_sprice(tps1+tps2+tps3, self.symbol)} sec:" f' postdict={dumps(postdict or "")}') # going to sleep sleep(tps1) if fast else sleep( round_sprice(tps1 + tps2 + tps3, self.symbol)) return self._curl_bitmex(path, query, postdict, timeout, verb, rethrow_errors, max_retries)
def _round(x): """Set default symbol""" return round_sprice(x, symbol)
def __init__( self, price, refPrice, tail_perct_init=0.5, head="buy", updatepause=6, timeBin=60, logger=None, max_var=2.6, min_flex=0.2, symbol="XBTUSD", ): """ Head is the direction, need a price (market price and ref price) and tail_perct_init (%): default. Le nb_enregistrement est la longeur de la bd des prix - Une tête sur la prix du marché référence et trois queues (tails ou tails). - La queue bleu (Qbleue) elle suit le prix du marché toujours à la même distance (même épaisseur) - La queue rouge, c'est le stop, elle ne bouge pas tant qu'il n'y a pas de bénéf. - La queue verte (flexTail) elle est d'épaisseur variable, son prix est passé au brokeur si au dessus du refPrice - min_flex est le pourcentage de la refTail jusqu'ou on peux réduire la flexTail ex. 80 de la ref tail - main_window_size c'est la taille de l'historique de ce prixObject, doit être calcule en fonction de la taille de la bin voulue et de la fréquence des mis à jour - updatepause c'est le nombre moyen de secondes entre deux mise à jour - timeBin c'est la taille de la fenêtre utilisé pour calculer la variation de prix. avec la updatepause permet d'estimer la main_window_size - max_var en quoi? et min_flex determine la flexibilité des queue. max_var est une statistique décrivant la variation nécessaire pour que la queue se réduise à min_flex. Elle est issue d'obeservation du marché voir getting_data.ipyng. la var à un très long mais fine queue. 90% < .22 - symbol: keep track of price symbol to format and round price correctly """ self.logger = get_logger(logger, sLL="INFO", name=__name__) self.head = head self.refPrice = refPrice self.symbol = symbol self.prec = PRICE_PRECISION[symbol] self.tail_perct_init = tail_perct_init # tail percent sera appliqué au prix de référence nombre entre 0 et 100 self.timeBin = timeBin self.updatePause = updatepause # essaye d'avoir un tableau plus grand que nécessaire # par défaut ~30 self.main_window_size = int(timeBin / self.updatePause * 4) # on défini une fonction pour mettre à jour la flexTail # le maxiumn de variation jamais observé pour la bin en pourcentage de variation # TODO: mieux définir cette fonction... self.max_var = max_var N = 100 self.neg_exps = self.__neg_exps(self.max_var, N) self.var_dist_hist = pd.Series(index=range(N), data=self.neg_exps) self.min_flex = min_flex # on initialise les prix et la df qui les contiendra # define self.data self.data = None self.data = self.__init_price_df(price, refPrice) # on définie l'épaisseur du stop self.tail_base_width = round_sprice( abs(self.data.refPrice.init - self.data.refTail.init), symbol)
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)