예제 #1
0
    def __init__(self, exchange: Exchange):
        super().__init__()
        self.exchange = exchange
        self.closed_orders = {}
        self.orders_history = {}
        self.balance_history = []
        self.last_balance_check = None
        self.peak_balance = 0
        self.max_drawdown = 0
        self.last_price = None
        self.symbol = None
        self.has_position = True if "bitmex" in str(
            exchange.__class__) else False
        self.is_backtest = True if "TestEX" in str(
            exchange.__class__) or "BitmexBacktest" in str(
                exchange.__class__) else False
        exchange.seconds()

        self.candles = None
        self.last_candle = None
        self.timeframes = {
            "1m": 60,
            "5m": 300,
            "15m": 900,
            "30m": 1800,
            "1h": 3600,
            "4h": 14400,
            "6h": 21600,
            "12h": 43200,
            "1d": 86400,
            "2d": 172800,
            "4d": 345600,
            "1w": 604800,
            "2w": 1209600
        }
예제 #2
0
def get_supported_pair_for(currency: CurrencyName, exchange: ccxt.Exchange) -> str:
    assert exchange

    result = ''

    exchange.load_markets()
    market_ids = {
            f'BTC{currency.value}', 
            f'BTC/{currency.value}',
            f'XBT{currency.value}', 
            f'XBT/{currency.value}', 
            f'BTC{currency.value}'.lower(), 
            f'XBT{currency.value}'.lower()}
    market_ids_found = list(market_ids & exchange.markets_by_id.keys())
    if not market_ids_found:
        market_ids_found = list(market_ids & set([market['symbol'] for market in exchange.markets_by_id.values()]))
    if market_ids_found:
        logger.debug(f'market_ids_found: {market_ids_found}')
        market_id = market_ids_found[0]
        market = exchange.markets_by_id[exchange.market_id(market_id)] 
        if market:  
            result = market['symbol']
            logger.debug(f'Found market {market}, with symbol {result}')

    return result
예제 #3
0
def get_ohlcv(exchange: ccxt.Exchange, symbol: str,
              timeframe: str) -> pd.DataFrame:
    # Check if fetching of OHLC Data is supported
    if not exchange.has["fetchOHLCV"]:
        raise ValueError(
            '{} does not support fetching OHLC data. Please use another exchange'
            .format(exchange))

    # Check requested timeframe is available. If not return a helpful error.
    if timeframe not in exchange.timeframes:
        print('-' * 36, ' ERROR ', '-' * 35)
        print('The requested timeframe ({}) is not available from {}\n'.format(
            args.timeframe, args.exchange))
        print('Available timeframes are:')
        for key in exchange.timeframes.keys():
            print('  - ' + key)
        raise ValueError

    # Check if the symbol is available on the Exchange
    exchange.load_markets()
    if symbol not in exchange.symbols:
        print('-' * 36, ' ERROR ', '-' * 35)
        print('The requested symbol ({}) is not available from {}\n'.format(
            symbol, exchange))
        print('Available symbols are:')
        for key in exchange.symbols:
            print('  - ' + key)
        print('-' * 80)
        raise ValueError

    # Get data
    data = exchange.fetch_ohlcv(symbol, timeframe)
    header = ['Timestamp', 'Open', 'High', 'Low', 'Close', 'Volume']
    return pd.DataFrame(data, columns=header)
예제 #4
0
def timeframe_to_seconds(ticker_interval: str) -> int:
    """
    Translates the timeframe interval value written in the human readable
    form ('1m', '5m', '1h', '1d', '1w', etc.) to the number
    of seconds for one timeframe interval.
    """
    return Exchange.parse_timeframe(ticker_interval)
예제 #5
0
    def get_candle(exchange: ccxt.Exchange, currency: CurrencyName = currency) -> tuple[ccxt.Exchange, Candle | None]:
        assert exchange
        assert currency

        result = (exchange, None)
        exchange.load_markets()
        if currency.value in exchange.currencies:
            try:
                candle = None
                candle = request_single(exchange, currency)
                if candle:
                    result = exchange, candle 
            except Exception as e:
                logger.error(f'error requesting data from exchange: {e}')

        return result
예제 #6
0
 def set_sandbox(self, api: ccxt.Exchange, exchange_config: dict, name: str) -> None:
     if exchange_config.get('sandbox'):
         if api.urls.get('test'):
             api.urls['api'] = api.urls['test']
             logger.info("Enabled Sandbox API on %s", name)
         else:
             logger.warning(name, "No Sandbox URL in CCXT, exiting. "
                                  "Please check your config.json")
             raise OperationalException(f'Exchange {name} does not provide a sandbox api')
예제 #7
0
 def __init__(self, snapshot={}, depth=None):
     copy = Exchange.extend(
         snapshot, {
             'asks':
             order_book_side.IndexedAsks(snapshot.get('asks', []), depth),
             'bids':
             order_book_side.IndexedBids(snapshot.get('bids', []), depth),
         })
     super(IndexedOrderBook, self).__init__(copy, depth)
예제 #8
0
 def reset(self, snapshot):
     self['asks']._index.clear()
     for ask in snapshot.get('asks', []):
         self['asks'].storeArray(ask)
     self['bids']._index.clear()
     for bid in snapshot.get('bids', []):
         self['bids'].storeArray(bid)
     self['nonce'] = snapshot.get('nonce')
     self['timestamp'] = snapshot.get('timestamp')
     self['datetime'] = Exchange.iso8601(self['timestamp'])
예제 #9
0
def fetch_trades_unbatched(exchange: ccxt.Exchange):
    """
    Some exchanges like Binance don't support fetching all trades at
    once and need to fetch per trading pair (market).
    """
    markets = exchange.load_markets()
    trades = []
    for market in markets:
        try:
            trades += exchange.fetch_my_trades(market)
        except ReadTimeout as err:
            print(err)
            continue

        # exchange.rateLimit is milliseconds but time.sleep expects seconds
        # plus add an extra 2 seconds as some exchanges like Bitfinex have varying rate limits
        # and still return rate limit exceeded errors when using the value provided by CCXT
        time.sleep((exchange.rateLimit / 1000) + 2)
    return trades
예제 #10
0
    def get_exchange_details(exchange: ccxt.Exchange) -> ExchangeDetails:

        result = None

        assert exchange
        exchange.load_markets()

        currencies = []
        if exchange.currencies: 
            currencies = [c for c in settings.currencies 
                    if c in exchange.currencies]

        details = ExchangeDetails(
                id      = exchange.id,
                name    = exchange.name,
                url     = exchange.urls['www'],
                countries = exchange.countries,
                currencies = get_supported_currencies(exchange))    # TODO(nochiel) TEST

        result = details
        return result
예제 #11
0
 def fetch_ohlcv(self, exchange: ccxt.Exchange, symbol, since):
     while True:
         try:
             candles = exchange.fetch_ohlcv(symbol,
                                            self.dataframe,
                                            since=since,
                                            limit=60 * 12)
             return candles
         except (ccxt.errors.DDoSProtection,
                 ccxt.errors.RequestTimeout) as e:
             self.logger.debug(error_class=e.__class__.__name__,
                               exchange=exchange.id)
             time.sleep(3)
         except ccxt.errors.BaseError as e:
             self.logger.debug(error_class=e.__class__.__name__)
             self.logger.exception()
             time.sleep(5)
예제 #12
0
 def __init__(self, snapshot={}, depth=None):
     self.cache = []
     depth = depth or sys.maxsize
     defaults = {
         'bids': [],
         'asks': [],
         'timestamp': None,
         'datetime': None,
         'nonce': None,
     }
     # do not mutate snapshot
     defaults.update(snapshot)
     if not isinstance(defaults['asks'], order_book_side.OrderBookSide):
         defaults['asks'] = order_book_side.Asks(defaults['asks'], depth)
     if not isinstance(defaults['bids'], order_book_side.OrderBookSide):
         defaults['bids'] = order_book_side.Bids(defaults['bids'], depth)
     defaults['datetime'] = Exchange.iso8601(defaults.get('timestamp'))
     # merge to self
     super(OrderBook, self).__init__(defaults)
예제 #13
0
def get_history(*, 
        exchange: ccxt.Exchange, 
        since: datetime,
        limit: int,
        timeframe: str,
        pair: str) -> list[Candle] | None:

    assert exchange
    logger.debug(f'{exchange} {pair} {since}')

    result = None

    _since = round(since.timestamp() * 1e3)
    params = dict()
    if exchange == "bitfinex":
        # TODO(nochiel) Is this needed? 
        # params['end'] = round(end.timestamp() * 1e3)
        ...

    candles = None

    # @nochiel: Re. Bitmex. Rate-limiting is very aggressive on authenticated API calls.
    # For a large number of requests this throttling doesn't help and Bitmex will increase
    # its rate limit to 3600 seconds!
    # Serializing requests won't help either
    wait            = exchange.rateLimit * 1e-3
    while wait:
        try:
            candles = exchange.fetchOHLCV(
                    symbol      = pair, 
                    limit       = limit, 
                    timeframe   = timeframe, 
                    since       = _since, 
                    params      = params)

            wait = 0

        except (ccxt.errors.RateLimitExceeded, ccxt.errors.DDoSProtection) as e:
            logger.error(f'rate-limited on {exchange}: {e}')
            logger.error(f'waiting {wait} seconds on {exchange} before making another request')
            time.sleep(wait)
            wait *= 2
            if wait > 120: 
                raise Exception(f'{exchange} has rate limited spotbit') from e

        except Exception as e:
            logger.error(f'{exchange} candle request error: {e}')

    if candles:
        result = [Candle(
            timestamp   = candle[OHLCV.timestamp],
            open        = candle[OHLCV.open],
            high        = candle[OHLCV.high],
            low         = candle[OHLCV.low],
            close       = candle[OHLCV.close],
            volume      = candle[OHLCV.volume]
            )

            for candle in candles]

    return result
예제 #14
0
파일: main.py 프로젝트: shvimas/Mark-I
def get_market_data(exchange: ccxt.Exchange):
    markets = exchange.load_markets(reload=True)
    for symbol in markets:
        data = prepare_data(data=markets[symbol])
        load_to_mongo(coll=symbol, data=data)
예제 #15
0
        return Trade(symbol=trade.symbol,
                     trade_type=trade.trade_type,
                     amount=order['filled'],
                     price=order['price'])

    def reset(self):
        self._markets = self._exchange.contract
        self._exchange.close()
        time.sleep(3)
        self._exchange.get_balance()
        self._initial_balance = self._exchange.position.loc[self._base_asset,
                                                            'available']
        self._performance = pd.DataFrame([], columns=['balance', 'net_worth'])


if __name__ == '__main__':
    okex = Exchange('okex/mock-luyh-okex')

    exchange = OnetokenExchange(exchange=okex, base_asset='btc')
    exchange.reset()

    base_precision = exchange.base_precision
    asset_precision = exchange.asset_precision

    portfolio = exchange.portfolio

    trades = exchange.trades

    print('develop')
예제 #16
0
def timeframe_to_msecs(ticker_interval: str) -> int:
    """
    Same as above, but returns milliseconds.
    """
    return Exchange.parse_timeframe(ticker_interval) * 1000
예제 #17
0
def timeframe_to_minutes(ticker_interval: str) -> int:
    """
    Same as above, but returns minutes.
    """
    return Exchange.parse_timeframe(ticker_interval) // 60
예제 #18
0
 def convert_datetime_to_timestamp(self, exchange_api: ccxt.Exchange,
                                   datetime):
     return exchange_api.parse8601(datetime)
예제 #19
0
 def get_time():
     return Exchange.milliseconds()
예제 #20
0
def request_single(exchange: ccxt.Exchange, currency: CurrencyName) -> Candle | None:
    '''
    Make a single request, without having to loop through all exchanges and currency pairs.
    '''
    assert exchange and isinstance(exchange, ccxt.Exchange)
    assert currency

    exchange.load_markets()
    pair = get_supported_pair_for(currency, exchange)
    if not pair: return None

    result = None
    latest_candle = None
    dt = None

    if exchange.has['fetchOHLCV']:
        logger.debug('fetchOHLCV')

        timeframe = '1m'
        match exchange.id:
            case 'btcalpha' | 'hollaex':
                timeframe = '1h'
            case 'poloniex':
                timeframe = '5m'

        # Some exchanges have explicit limits on how many candles you can get at once
        # TODO(nochiel) Simplify this by checking for 2 canonical limits to use.
        limit = 1000
        match exchange.id:
            case 'bitstamp':
                limit = 1000
            case 'bybit':
                limit = 200
            case 'eterbase':
                limit = 1000000
            case 'exmo':
                limit = 3000
            case 'btcalpha':
                limit = 720

        since = round((datetime.now() - timedelta(hours=1)).timestamp() * 1e3)

        # TODO(nochiel) TEST other exchanges requiring special conditions: bitstamp, bitmart?
        params = []
        if exchange.id == 'bitfinex': 
            params = {
                    'limit':100,
                    'start': since,
                    'end':  round(datetime.now().timestamp() * 1e3)
                    }

        try:
            candles = exchange.fetchOHLCV(
                    symbol      = pair, 
                    timeframe   = timeframe, 
                    limit       = limit, 
                    since       = since, 
                    params      = params)

            latest_candle = candles[-1]   

        except Exception as e:
            logger.error(f'error requesting candle from {exchange.name}: {e}')

    else:       # TODO(nochiel) TEST 
        logger.debug(f'fetch_ticker: {pair}')

        candle = None
        try:
            candle = exchange.fetch_ticker(pair)
        except Exception as e:
            logger.error(f'error on {exchange} fetch_ticker: {e}')

        latest_candle = candle

    if latest_candle:
        result = Candle(
                timestamp   = latest_candle[OHLCV.timestamp],
                open        = latest_candle[OHLCV.open],
                high        = latest_candle[OHLCV.high],
                low         = latest_candle[OHLCV.low],
                close       = latest_candle[OHLCV.close],
                volume      = latest_candle[OHLCV.volume]
                )

    return result