コード例 #1
0
ファイル: execution_strategy.py プロジェクト: cambrian/quant
    def __init__(
        self,
        size,
        micro_trend_hl,
        micro_accel_hl,
        trend_hl,
        accel_hl,
        edge_trend_hl,
        edge_accel_hl,
        warmup_data,
    ):
        self.size = size

        warmup_prices = warmup_data.xs("price", axis=1, level=1)
        latest_prices = warmup_prices.iloc[-1]

        self.micro_trend_estimator = TrendEstimator(
            HoltEma(micro_trend_hl, micro_accel_hl), latest_prices)
        self.trend_estimator = TrendEstimator(HoltEma(trend_hl, accel_hl),
                                              latest_prices)
        for _, prices in warmup_prices.iloc[-4 * accel_hl:].iterrows():
            self.micro_trend_estimator.step(prices)
            self.trend_estimator.step(prices)
        self.edge_trend_estimator = TrendEstimator(
            HoltEma(edge_trend_hl, edge_accel_hl))

        if not self.trend_estimator.ready:
            Log.warn(
                "Execution strategy initialized but had insufficient warmup data. Will \
                warm up slowly in real time.")
        else:
            Log.info("Execution strategy initialized and warm.")
コード例 #2
0
ファイル: dummy.py プロジェクト: cambrian/quant
 def add_order(self, order):
     net_size = order.size * (1 if order.side == Side.BUY else -1)
     self.__positions[order.pair.base] += net_size
     self.__positions[order.pair.quote] -= (
         net_size + order.size * self.__fees["taker"]) * order.price
     Log.info("dummy order", order)
     Log.info("dummy positions", self.__positions)
     return OpenOrder(order, order.id)
コード例 #3
0
ファイル: dummy.py プロジェクト: cambrian/quant
 def step_time(self):
     """Returns False if all data has been exhausted."""
     self.time += 1
     if self.time >= len(self.__data.index):
         return False
     frame = self.__data.iloc[self.time]
     for ep, price in frame.xs("price", level=1).iteritems():
         if ep.exchange_id != self.id:
             continue
         ep = ExchangePair(self.id, ep.pair)
         dummy_book = OrderBook(ep, [BookLevel(price, 1)],
                                [BookLevel(price, 1)])
         self.__book_queues[ep.pair].put(dummy_book)
         self.__latest_books[ep.pair] = dummy_book
     Log.info("dummy-step", self.time)
     return True
コード例 #4
0
ファイル: kalman.py プロジェクト: cambrian/quant
    def __init__(
        self, window_size, movement_hl, trend_hl, cointegration_period, warmup_signals, warmup_data
    ):
        self.window_size = window_size
        self.moving_prices = HoltEma(movement_hl, trend_hl, trend_hl)
        self.moving_err_from_prev_fair = Emse(trend_hl)
        self.cointegration_period = cointegration_period
        self.sample_counter = 0
        self.r = None
        self.r2 = None
        # TODO: do some checks for length/pairs of warmup signals/outputs
        prices = pd.concat(
            [warmup_signals.xs("price", axis=1, level=1), warmup_data.xs("price", axis=1, level=1)],
            axis=1,
            sort=False,
        )
        volumes = pd.concat(
            [
                warmup_signals.xs("volume", axis=1, level=1),
                warmup_data.xs("volume", axis=1, level=1),
            ],
            axis=1,
            sort=False,
        )
        self.price_history = RingBuffer(self.window_size, dtype=(np.float64, len(prices.columns)))
        self.price_history.extend(prices.values)

        for _, p in prices.iloc[-trend_hl * 4 :].iterrows():
            self.moving_prices.step(p)

        self.moving_volumes = Ema(movement_hl, volumes.mean())

        self.moving_variances = TrendEstimator(
            Emse(window_size / 2, (prices.diff()[1:] ** 2).mean()), prices.iloc[-1]
        )

        self.prev_fair = Gaussian(self.moving_prices.value, [1e100 for _ in prices.columns])

        self.coint_f = pd.DataFrame(
            1, index=warmup_signals.columns.unique(0), columns=prices.columns
        )

        if len(self.price_history) < self.window_size or not self.moving_prices.ready:
            Log.warn("Insufficient warmup data. Price model will warm up (slowly) in real time.")
        else:
            Log.info("Price model initialized and warm.")
コード例 #5
0
ファイル: bitfinex.py プロジェクト: cambrian/quant
 def add_order(self, order):
     assert order.exchange_id == self.id
     payload = {
         "request": "/v1/order/new",
         "nonce": self.__bfxv1._nonce(),
         # Bitfinex v1 API expects "BTCUSD", v2 API expects "tBTCUSD":
         "symbol": Bitfinex.encode_trading_pair(order.pair)[1:],
         "amount": str(order.size),
         "price": str(order.price),
         "exchange": "bitfinex",
         "side": order.side.name.lower(),
         "type": self.__order_types[order.order_type],
         "is_postonly": order.maker_only,
     }
     try:
         response = self.__bfxv1._post("/order/new",
                                       payload=payload,
                                       verify=True)
     except TypeError as err:
         Log.warn("Bitfinex _post type error: {}".format(err))
     except Exception as err:
         Log.warn("Swallowing unexpected error: {}".format(err))
         return None
     Log.debug("Bitfinex-order-response", response)
     if "id" in response:
         order = OpenOrder(order, response["id"])
         if not response["is_live"]:
             order.update_status(Order.Status.REJECTED)
         elif not response["is_cancelled"]:
             order.update_status(Order.Status.CANCELLED)
         return order
     return None
コード例 #6
0
ファイル: __main__.py プロジェクト: cambrian/quant
def dummy_main():
    pairs = [BTC_USDT, ETH_USDT, XRP_USDT, LTC_USDT, NEO_USDT, EOS_USDT]
    Log.info("Loading dummy data.")
    data = pd.read_hdf("research/data/1min.h5")
    data = data.resample("15Min").first()
    window_size = 500

    converter = UsdConverter()
    aggregator = SignalAggregator(window_size, {"total_market": [p.base for p in pairs]})
    Log.info("Processing warmup data.")
    warmup_data = data.iloc[:window_size]
    data = data.iloc[window_size:]
    warmup_data = warmup_data.apply(converter.step, axis=1)
    warmup_signals = warmup_data.apply(aggregator.step, axis=1)

    Log.info("Initializing components.")
    kalman_strategy = strategy.Kalman(
        window_size=window_size,
        movement_hl=288,
        trend_hl=256,
        cointegration_period=96,
        warmup_signals=warmup_signals,
        warmup_data=warmup_data,
    )
    # use same params for trend and micro trend because 15min is too wide for micro trends to have
    # effect
    execution_strategy = ExecutionStrategy(10, 3, 9, 3, 9, 4, 12, warmup_data)

    dummy_exchange = DummyExchange(
        THREAD_MANAGER, BINANCE, data, {"maker": 0.00075, "taker": 0.00075}
    )
    executor = Executor(THREAD_MANAGER, {dummy_exchange: pairs}, execution_strategy)
    while True:
        if not dummy_exchange.step_time():
            break
        frame = dummy_exchange.frame(pairs)
        frame_usd = converter.step(frame)
        signals = aggregator.step(frame_usd)
        kalman_fairs = kalman_strategy.tick(frame_usd, signals)
        fairs = kalman_fairs & Gaussian(
            frame_usd.xs("price", level=1), [1e100 for _ in frame_usd.xs("price", level=1).index]
        )
        Log.info("fairs", fairs)
        executor.tick_fairs(fairs)
コード例 #7
0
ファイル: bitfinex.py プロジェクト: cambrian/quant
 def on_close(ws):
     Log.warn("WS closed unexpectedly for exchange {}".format(self.id))
     Log.info("restarting WS for exchange {}".format(self.id))
     time.sleep(3)
     self.__track_positions()
コード例 #8
0
ファイル: bitfinex.py プロジェクト: cambrian/quant
 def on_error(ws, error):
     Log.warn(
         "WS error within __track_positions for exchange {}: {}".format(
             self.id, error))
コード例 #9
0
ファイル: executor.py プロジェクト: cambrian/quant
    def __trade(self, wait_for_other_trade=False):
        """
        If `wait_for_other_trade` is false, doesn't try to trade if there is another thread
        attempting to trade. If true, it will wait for the other thread to finish and try
        immediately after.
        TODO: requires that __latest_fairs and self.__exchange_pairs have the same indexing. Make
        this explicit or don't require it.
        """
        if self.__latest_fairs is None:
            Log.warn(
                "Attempted to trade but executor has not received any fairs.")
            return

        # component warmup may not be synchronized
        for ep in self.__latest_fairs.mean.index:
            if not ep in self.__latest_books:
                Log.warn(
                    "Attempted to trade but executor has no book data for exchange pair:",
                    ep)
                return

        if wait_for_other_trade:
            self.__trade_lock.acquire()
        elif not self.__trade_lock.acquire(blocking=False):
            Log.debug("Other thread trading.")
            return

        bids = pd.Series(index=self.__latest_fairs.mean.index)
        asks = pd.Series(index=self.__latest_fairs.mean.index)
        fees = pd.Series(index=self.__latest_fairs.mean.index)
        positions = {}
        # self.__books_lock.acquire()
        for exchange_pair in self.__latest_fairs.mean.index:
            exchange = self.__exchanges[exchange_pair.exchange_id]
            book = self.__latest_books[exchange_pair]
            bids[exchange_pair] = book.bids[0].price
            asks[exchange_pair] = book.asks[0].price
            fees[exchange_pair] = exchange.fees["taker"]
            positions[exchange.id, exchange_pair.
                      base] = exchange.positions[exchange_pair.base] or 0
        # self.__books_lock.release()
        positions = pd.Series(positions)
        Log.info("Positions", positions)

        order_sizes = self.execution_strategy.tick(positions, bids, asks,
                                                   self.__latest_fairs,
                                                   fees).fillna(0.0)
        Log.debug("Order size", order_sizes)

        for exchange_pair, order_size in order_sizes.items():
            if order_size == 0:
                continue
            exchange = self.__exchanges[exchange_pair.exchange_id]
            side = Side.BUY if order_size > 0 else Side.SELL
            price = (asks if order_size > 0 else bids)[exchange_pair]
            order = Order(self.__next_order_id(), exchange_pair, side,
                          Order.Type.IOC, price, abs(order_size))
            exchange.add_order(order)  # TODO: require this to be async?
            Log.info("sent order", order)
        self.__trade_lock.release()
コード例 #10
0
ファイル: __main__.py プロジェクト: cambrian/quant
def main():
    pairs = [BTC_USD, ETH_USD, XRP_USD, LTC_USD, EOS_USD, BCH_USD, BSV_USD]
    window_size = 9999  # biggest window that will fit in 2 api calls to candles

    Log.info("Connecting to exchanges.")
    with open("keys/bitfinex.json") as bitfinex_key_file:
        bitfinex_keys = json.load(bitfinex_key_file)
    bitfinex = Bitfinex(THREAD_MANAGER, bitfinex_keys, pairs)

    Log.info("Fetching warmup data.")
    warmup_data = bitfinex.get_warmup_data(pairs, window_size, "1m")

    Log.info("Prepping warmup data.")
    converter = UsdConverter()
    aggregator = SignalAggregator(window_size, {"total_market": [p.base for p in pairs]})
    warmup_data = warmup_data.apply(converter.step, axis=1)
    warmup_signals = warmup_data.apply(aggregator.step, axis=1)

    Log.info("Initializing components.")
    kalman_strategy = strategy.Kalman(
        window_size=window_size,
        movement_hl=1440 * 3,
        trend_hl=window_size,
        cointegration_period=720,
        warmup_signals=warmup_signals,
        warmup_data=warmup_data,
    )
    execution_strategy = ExecutionStrategy(500, 1, 3, 45, 135, 60, 180, warmup_data)
    executor = Executor(THREAD_MANAGER, {bitfinex: pairs}, execution_strategy)

    beat = Beat(60000)
    while beat.loop():
        Log.info("Beat")
        bfx_frame = bitfinex.frame(pairs)
        frame_usd = converter.step(bfx_frame)
        signals = aggregator.step(frame_usd)
        kalman_fairs = kalman_strategy.tick(frame_usd, signals)
        fairs = kalman_fairs & Gaussian(
            frame_usd.xs("price", level=1), [1e100 for _ in frame_usd.xs("price", level=1).index]
        )
        Log.info("fairs", fairs)
        executor.tick_fairs(fairs)