Ejemplo n.º 1
0
def main():  # noqa: E302
    global marketTable
    if (len(sys.argv) > 1):
        with open(sys.argv[1]) as f:
            config = BaseExchange.extend({}, baseConfig, json.load(f))
    else:
        config = BaseExchange.extend({}, baseConfig)
    marketTable = MarketTable(config['marketTable'])
    for id in config['exchanges'].keys():
        exchange = config['exchanges'][id]
        exConf = BaseExchange.extend(
            {}, config['exchangeDefaults'],
            exchange['options'] if 'options' in exchange else {})

        ex = getattr(ccxt, id)({
            'apiKey':
            exConf['apiKey'] if 'apiKey' in exConf else '',
            'secret':
            exConf['apiSecret'] if 'apiSecret' in exConf else '',
            'enableRateLimit':
            True,
            'verbose':
            exConf['verbose'] if 'verbose' in exConf else False,
        })
        for symbol in exchange['symbols'].keys():
            marketTable.addMarket(ex.id, symbol)
        asyncio.ensure_future(subscribe(ex, exchange['symbols'],
                                        config['symbolDefaults']),
                              loop=loop)
Ejemplo n.º 2
0
    def __init__(self):
        super(CcxtApi, self).__init__()
        self.api_key = ""
        self.api_secret = ""

        self.exchange_symbol_dict = {}
        self._contracts: typing.Dict[str, ContractData] = {}
        self._root_symbol_dict = {}
        self._exchange = Exchange()
        self._limit_context = LimitedQueryContext(self.N_RATE_LIMIT, self.PERIOD_LIMIT)
        self._local_order_manager = LocalOrderManager()

        self._callback: typing.Callable = print
Ejemplo n.º 3
0
 def __init__(self):
     Exchange.__init__(self)
     client = MaxClient(MAX_KEY, MAX_SECRET)
     self.client = client
     self.websocket_uri = None
     self.fees = {
         'trading': {
             'tierBased': False,
             'percentage': True,
             'taker': 0.0015,
             'maker': 0.001,
         }
     }
Ejemplo n.º 4
0
async def subscribe(exchange, symbols, defaultConfig):  # noqa: E302
    @exchange.on('err')
    def websocket_error(err, conxid):  # pylint: disable=W0612
        print(err)
        try:
            exchange.websocketClose(conxid)
        except ccxt.BaseError:
            pass
        for symbol in symbols.keys():
            marketTable.marketError(exchange.id, symbol, err)
        marketTable.print()

    @exchange.on('ob')
    def websocket_ob(market, ob):  # pylint: disable=W0612
        marketTable.updateMarket(exchange.id, market, ob)
        marketTable.print()

    try:
        await exchange.load_markets()
        for symbol in symbols.keys():
            sys.stdout.flush()
            config = BaseExchange.extend({}, defaultConfig, symbols[symbol])
            try:
                await exchange.websocket_subscribe('ob', symbol, config)
            except Exception as ex:
                print(ex)
                sys.stdout.flush()
                marketTable.marketError(exchange.id, symbol, ex)
                marketTable.print()
    except Exception as ex:
        print(ex)
        sys.stdout.flush()
        for symbol in symbols.keys():
            marketTable.marketError(exchange.id, symbol, ex)
        marketTable.print()
Ejemplo n.º 5
0
 def fetch_ticker(self, symbol):
     ohlcv = self._ohlcvs.get(symbol)
     if ohlcv is None:
         raise BadSymbol('ExchangeBackend: no prices for {}'.format(symbol))
     current_date = self._timeframe.date().floor('1T')
     row = ohlcv.loc[current_date]
     timestamp = int(current_date.value / 10**6)
     return {
         'symbol': symbol,
         'timestamp': timestamp,
         'datetime': Exchange.iso8601(timestamp),
         'high': row['high'],
         'low': row['low'],
         'bid': None,
         'bidVolume': None,
         'ask': None,
         'askVolume': None,
         'vwap': None,
         'open': row['open'],
         'close': row['close'],
         'last': None,
         'previousClose': None,
         'change': None,
         'percentage': None,
         'average': None,
         'baseVolume': None,
         'quoteVolume': None,
         'info': {},
     }
Ejemplo n.º 6
0
def update_market(market: Market, exchange: Exchange = None) -> Market:
    """
    Update Market Order

    Keyword arguments:
    market -- market model
    exchange -- exchange client, preload all markets to reduce requests

    return -> Market, updated market
    """
    if not exchange:
        exchange = get_client(exchange_id=market.exchange)

    market_exchange: dict = exchange.market(market.symbol)
    return get_or_create_market(response=market_exchange,
                                exchange_id=market.exchange)
Ejemplo n.º 7
0
 def __init__(self, options):
     self.options = BaseExchange.extend({}, {
         "marketsByRow": 2,
         "maxLimit": 10,
         "marketColumnWidth": 50,
     }, options)
     self.markets = {}
     self.grid = []
     self.newEmptyLine = ' ' * (self.options['marketColumnWidth'] *
                                self.options['marketsByRow'])
     self.hr = "-" * (self.options['marketColumnWidth'])
     self.bidsAdksSeparator = "." * (self.options['marketColumnWidth'])
     self.emptyCell = " " * (self.options['marketColumnWidth'])
     self.amountColumn = self.options['marketColumnWidth'] // 2
     self.height = 2 + self.options['maxLimit'] + 1 + self.options[
         'maxLimit'] + 1
Ejemplo n.º 8
0
 def fetch_ohlcv_dataframe(self,
                           symbol,
                           timeframe='1m',
                           since=None,
                           limit=None,
                           params={}):
     # Exchanges in the real world have different behaviour, when there is
     # no since parameter provided. (some use data from the beginning,
     # some from the end)
     # We return data from the beginning, because this is most likely not
     # what the user wants, so this will force the user to provide the
     # parameters, which will work with every exchange. This is a bug
     # prevention mechanism.
     ohlcv = self._ohlcvs.get(symbol)
     if ohlcv is None:
         raise BadSymbol('ExchangeBackend: no prices for {}'.format(symbol))
     pd_current_date = self._timeframe.date().floor('1T')
     if limit is None:
         limit = 5
     timeframe_sec = Exchange.parse_timeframe(timeframe)
     pd_timeframe = pandas.Timedelta(timeframe_sec, unit='s')
     ohlcv_start_date = ohlcv.index[0]
     if since is None:
         pd_since = ohlcv_start_date
     else:
         pd_since = pandas.Timestamp(since, unit='ms', tz='UTC')
     pd_since = pd_since.ceil(pd_timeframe)
     if pd_since < ohlcv_start_date:
         raise BadRequest('ExchangeBackend: fetch_ohlcv: no date availabe '
                          'at since')
     pd_until = pd_since + limit * pd_timeframe - pandas.Timedelta('1m')
     if pd_until >= pd_current_date + pd_timeframe:
         raise BadRequest('ExchangeBackend: fetch_ohlcv:'
                          ' since.ceil(timeframe) + limit * timeframe'
                          ' needs to be in the past')
     pd_until = min(pd_until, pd_current_date)
     data = ohlcv[pd_since:pd_until]
     return data.resample(pd_timeframe).agg({
         'open': 'first',
         'high': 'max',
         'low': 'min',
         'close': 'last',
         'volume': 'sum'
     })
Ejemplo n.º 9
0
def parse_params_and_execute_algorithm(AlgorithmClass):
    parser = argparse.ArgumentParser(
        epilog=HELP_EPILOG, formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument('--start-date', default='',
                        help='Date to start backtesting, ignored in live mode')
    parser.add_argument('--end-date', default='2009-01-01',
                        help='Date to end backtesting')
    parser.add_argument('--interval', default='1m',
                        help='Timedelta between each iteration')
    parser.add_argument('--exchanges', default='',
                        help='Exchange ids comma separated to load ohlcv')
    parser.add_argument('--symbols', default='',
                        help='Symbols (comma separated) to load ohlcv '
                             'per exchange')
    parser.add_argument('--data-directory', default=USER_DATA_DIR,
                        help='directory where data is stored'
                             ' (e.g. ohlcv data')
    parser.add_argument('--config-directory', default=USER_CONFIG_DIR,
                        help='directory where config is stored'
                             ' (e.g. exchange parameters')
    parser.add_argument('--auth-aliases', default='{}',
                        help='Auth aliases for different exchange'
                             ' config files')
    parser.add_argument('--live', action='store_true',
                        help='Trade live on exchanges')
    parser.add_argument('--start-balances', default='{}',
                        help='Balance at start (json): '
                             '{"exchange": {"BTC": 3}}')
    AlgorithmClass.configure_argparser(parser)
    args = parser.parse_args()
    logger = logging.getLogger(__package__)

    def split_parameters(p):
        if p == '':
            return []
        return p.split(',')

    exchange_names = split_parameters(args.exchanges)
    symbols = split_parameters(args.symbols)
    if not args.live:
        if len(exchange_names) == 0:
            logger.warning('No exchanges specified, do not load ohlcv')
        if len(symbols) == 0:
            logger.warning('No symbols specified, load all ohlcvs per each '
                           'exchange. This can lead to long start times')
    try:
        pd_interval = pandas.Timedelta(
            Exchange.parse_timeframe(args.interval), unit='s')
    except (NotSupported, ValueError):
        raise ValueError('Interval is not valid')
    auth_aliases = {}
    if args.live:
        if args.start_date != '':
            raise ValueError('Start date cannot be set in live mode')
        if args.start_balances != '{}':
            raise ValueError('Start balance cannot be set in live mode')
        pd_start_date = pandas.Timestamp.now(tz='UTC').floor(pd_interval)
        start_balances = None
        auth_aliases = json.loads(args.auth_aliases)
    else:
        pd_start_date = pandas.to_datetime(args.start_date, utc=True)
        start_balances = json.loads(args.start_balances)
    pd_end_date = pandas.to_datetime(args.end_date, utc=True)
    if pandas.isnull(pd_start_date):
        raise ValueError('Start date is not valid')
    if pandas.isnull(pd_end_date):
        raise ValueError('End date is not valid')

    return execute_algorithm(exchange_names=exchange_names,
                             symbols=symbols,
                             pd_start_date=pd_start_date,
                             pd_end_date=pd_end_date,
                             pd_interval=pd_interval,
                             conf_dir=args.config_directory,
                             data_dir=args.data_directory,
                             AlgorithmClass=AlgorithmClass,
                             args=args,
                             auth_aliases=auth_aliases,
                             live=args.live,
                             start_balances=start_balances)
Ejemplo n.º 10
0
 def iso8601(value):
     return ccxtExchange.iso8601(value)
Ejemplo n.º 11
0
 def safe_value(dictionary, key, default_value=None):
     return ccxtExchange.safe_value(dictionary, key, default_value)
Ejemplo n.º 12
0
 def nonce(self):
     return Exchange.milliseconds()
Ejemplo n.º 13
0
 def safe_float(self, dictionary, key, default_value):
     return ccxtExchange.safe_float(dictionary, key, default_value)
Ejemplo n.º 14
0
class CcxtApi(Registered):
    FULL_NAME = ''

    STATUS_MAP_REVERSE = dict()
    STATUS_MAP_REVERSE['open'] = EnumOrderStatus.PENDING
    STATUS_MAP_REVERSE['closed'] = EnumOrderStatus.FILLED
    STATUS_MAP_REVERSE['canceled'] = EnumOrderStatus.CANCELLED

    DIRECTION_MAP = dict()
    DIRECTION_MAP[EnumOrderDirection.BUY] = 'buy'
    DIRECTION_MAP[EnumOrderDirection.SELL] = 'sell'
    DIRECTION_MAP_RESERVE = {v: k for k, v in DIRECTION_MAP.items()}

    ORDER_TYPE_MAP = dict()
    ORDER_TYPE_MAP[EnumOrderType.MARKET] = 'market'
    ORDER_TYPE_MAP[EnumOrderType.LIMIT] = 'limit'
    ORDER_TYPE_MAP_REVERSE = {v: k for k, v in ORDER_TYPE_MAP.items()}

    TAG = ''

    N_RATE_LIMIT = 300
    PERIOD_LIMIT = '1m'

    N_LIMIT_ORDER = 300
    N_LIMIT_TRADE = 300

    N_LIMIT_BAR = 500
    N_LIMIT_MARKET_TRADE = 300

    @property
    def contracts(self):
        return self._contracts

    @property
    def exchange(self):
        return self._exchange

    @property
    def oms(self):
        return self._local_order_manager

    def __init__(self):
        super(CcxtApi, self).__init__()
        self.api_key = ""
        self.api_secret = ""

        self.exchange_symbol_dict = {}
        self._contracts: typing.Dict[str, ContractData] = {}
        self._root_symbol_dict = {}
        self._exchange = Exchange()
        self._limit_context = LimitedQueryContext(self.N_RATE_LIMIT, self.PERIOD_LIMIT)
        self._local_order_manager = LocalOrderManager()

        self._callback: typing.Callable = print

    def set_callback(self, callback):
        self._callback = callback

    def throttle(self):
        with self._limit_context:
            pass

    def on_order(self, order: OrderData):
        self._local_order_manager.on_order(order)
        self._callback(order)

    def on_trade(self, trade: TradeData):
        self._callback(trade)

    def on_bar(self, bar: BarData):
        self._callback(bar)

    def on_depth(self, depth: DepthData):
        self._callback(depth)

    def connect(self, api_key='', api_secret=''):
        self.api_key = api_key
        self.api_secret = api_secret
        config = {
            'apiKey': api_key,
            'secret': api_secret,
            'enableRateLimit': False,
        }
        exchange = self.FULL_NAME
        api_class = getattr(ccxt, exchange)
        self._exchange = api_class(config)
        self.fetch_contract()

        for symbol, contract in self.contracts.items():
            exchange_symbol = contract.symbol_exchange
            self.exchange_symbol_dict[exchange_symbol] = symbol

    # --------------------- trading interface ---------------------
    def create_order(self, order: OrderData, params: dict = None):
        self.throttle()
        self._local_order_manager.on_order(order)

        order = copy.copy(order)
        contract = self._contracts[order.symbol]
        symbol_root = contract.symbol_root
        if params is None:
            params = {}
        try:
            price = order.price
            if order.order_type == EnumOrderType.MARKET:
                price = None

            logger.debug('%s sending order %s', self, order.client_order_id)
            data = self._exchange.create_order(symbol_root,
                                               self.ORDER_TYPE_MAP[order.order_type],
                                               self.DIRECTION_MAP[order.direction],
                                               order.volume,
                                               price,
                                               params)
            if 'status' in data:
                status = self.STATUS_MAP_REVERSE[data['status']]
            else:
                status = EnumOrderStatus.PENDING
            order.status = status
            order.order_id = data['id']
            logger.debug('%s sent order %s successfully with order id %s and status %s ', self, order.client_order_id,
                         order.order_id, order.status)

        except ccxt.InsufficientFunds as e:
            order.status = EnumOrderStatus.REJECTED
            balance_df = self.fetch_balance()
            base_amount = balance_df.loc[contract.symbol_base]
            quote_amount = balance_df.loc[contract.symbol_quote]
            logger.warn(
                "Insufficient balance for order: %s\n%s\n%s", e.args, order,
                f'There was only {contract.symbol_base} \n'
                f'{base_amount}  \n'
                f'and {quote_amount} \n'
                f'{contract.symbol_quote} \n'
                f'in the balance. '
            )
        except ccxt.InvalidOrder as e:
            order.status = EnumOrderStatus.REJECTED
            logger.warn("InvalidOrder error: %s\n%s", e.args, order)
        except ccxt.ExchangeError as e:
            logger.warn("Exchange error: %s\n%s", e.args, order)
            order.status = EnumOrderStatus.REJECTED
        except ccxt.NetworkError as e:
            logger.warn("Network error: %s\n%s", e.args, order)
            order.status = EnumOrderStatus.ERROR
        except requests.HTTPError as e:
            logger.warn("Http error: %s\n%s", e.args, order)
            order.status = EnumOrderStatus.ERROR

        self._local_order_manager.on_order(order)
        self.on_order(order)
        return order

    def cancel_order(self, order_cancel: OrderData):
        self.throttle()
        self._local_order_manager.on_order(order_cancel)

        order_id = order_cancel.order_id
        contract = self._contracts[order_cancel.symbol]
        symbol_root = contract.symbol_root
        logger.debug('%s cancel order %s', self, order_cancel.client_order_id)
        try:
            self._exchange.cancel_order(order_id, symbol_root)
            logger.debug('%s canceled order %s successfully', self, order_cancel.client_order_id)
            order_cancel.status = EnumOrderStatus.CANCELLED
        except ccxt.errors.OrderNotFound as e:
            logger.info("Order already been closed or cancelled before: %s\n%s", e.args, order_cancel)
            order_status = self.fetch_order_status(order_cancel)
            order_cancel.on_order(order_status)
        except ccxt.errors.NetworkError as e:
            logger.info("Network error: %s\n%s", e.args, order_cancel)
            order_cancel.status = EnumOrderStatus.CANCEL_ERROR

        self._local_order_manager.on_order(order_cancel)
        self.on_order(order_cancel)
        return order_cancel

    def fetch_order_status(self, order: OrderData):
        self.throttle()
        logger.debug('fetch status for order %s', order)
        symbol = order.symbol
        symbol_root = self.contracts[symbol].symbol_root
        order_id = order.order_id
        data = self._exchange.fetch_order(order_id, symbol_root)
        order_status = copy.copy(order)

        order_status.status = self.STATUS_MAP_REVERSE[data['status']]
        order_status.executed_volume = data['filled']

        return order_status

    def fetch_order_trades(self, order: OrderData) -> typing.List[TradeData]:
        symbol_root = self.contracts[order.symbol].symbol_root
        result = self._exchange.fetch_order_trades(order.order_id, symbol_root)
        trade_list = []
        for data in result:
            trade = self._parse_trade(data)
            trade.strategy_id = order.strategy_id
            trade.client_order_id = order.client_order_id
            trade_list.append(trade)
        return trade_list

    def _parse_trade(self, data):
        trade = TradeData()
        trade.symbol = self._root_symbol_dict[data['symbol']]
        trade.order_id = data['order']
        trade.trade_id = data['id']
        trade.volume = data['amount']
        trade.price = data['price']
        trade.direction = self.DIRECTION_MAP_RESERVE[data['side']]
        trade.commission = data['fee']['cost']
        trade.commission_asset = '.'.join([data['fee']['currency'], self.TAG])
        trade.datetime = datetime.datetime.utcfromtimestamp(data['timestamp'] / 1000.0)
        return trade

    def fetch_my_trades(self, symbol, since=DEFAULT_START_TIME, params=None):
        self.throttle()
        if params is None:
            params = {}
        if symbol is None:
            symbol_root = None
        else:
            symbol_root = self.contracts[symbol].symbol_root
        start_timestamp = int(calendar.timegm(since.timetuple()) * 1000)
        data_list = self._exchange.fetch_my_trades(symbol_root, since=start_timestamp, params=params)

        trade_list = []
        for data in data_list:
            trade_list.append(self._parse_trade(data))

        return trade_list

    def _fetch_order_impl(self, function, symbol, since, params):
        self.throttle()
        if symbol is None:
            symbol_root = None
        else:
            symbol_root = self.contracts[symbol].symbol_root
        if params is None:
            params = {}

        data_list = []
        start_timestamp = int(calendar.timegm(since.timetuple()) * 1000)
        limit = self.N_LIMIT_ORDER
        while True:
            orders = function(symbol_root, since=start_timestamp, limit=limit, params=params)
            if orders is None:
                break
            if len(orders):
                data_list.extend(orders)
            if len(orders) == limit:
                start_timestamp = orders[-1]['timestamp']
            else:
                break

        order_list = []
        order_id_set = set()
        for data in data_list:
            order_id = data['id']
            if order_id not in order_id_set:
                order_id_set.add(order_id)

                order = OrderData()
                order.order_id = order_id
                order.symbol = self._root_symbol_dict[data['symbol']]
                order.datetime = datetime.datetime.utcfromtimestamp(data['timestamp'] / 1000.0)
                order.volume = data['amount']
                order.executed_volume = data['filled']
                order.executed_notional = data['cost']
                order.price = data['price']

                order.status = self.STATUS_MAP_REVERSE[data['status']]
                order.direction = self.DIRECTION_MAP_RESERVE[data['side']]
                order.order_type = self.ORDER_TYPE_MAP_REVERSE[data['type']]

                order_list.append(order)

        return order_list

    def fetch_open_orders(self, symbol, since=DEFAULT_START_TIME, params=None):
        return self._fetch_order_impl(self._exchange.fetch_open_orders, symbol, since, params)

    def fetch_close_orders(self, symbol, since=DEFAULT_START_TIME, params=None):
        return self._fetch_order_impl(self._exchange.fetch_closed_orders, symbol, since, params)

    def fetch_orders(self, symbol, since=DEFAULT_START_TIME, params=None):
        return self._fetch_order_impl(self._exchange.fetch_orders, symbol, since, params)

    def fetch_balance(self):
        self.throttle()
        try:
            # noinspection PyUnresolvedReferences
            data = self._exchange.fetch_balance()
        except ccxt.base.errors.AuthenticationError:
            return pd.DataFrame()
        account_df = pd.DataFrame([data['free'], data['used'], data['total']],
                                  index=['free_amount', 'frozen_amount', 'total_amount']).T
        index = account_df.any(axis=1)
        account_df = account_df[index]

        return account_df

    # --------------------- market data ---------------------

    def fetch_ticker(self, symbol):
        symbol_root = self.contracts[symbol].symbol_root
        ticker = self._exchange.fetch_ticker(symbol_root)
        ticker.pop('info')
        return ticker

    def fetch_bars(self, symbol, freq, start_date, end_date=None, return_df=False):
        symbol_root = self.contracts[symbol].symbol_root
        if end_date is None:
            now = datetime.datetime.utcnow()
            end_date = start_date + TIME_INTERVAL_MAP[freq] * (self.N_LIMIT_BAR - 1)
            end_date = min(end_date, now - TIME_INTERVAL_MAP[freq])
        start_timestamp = int(calendar.timegm(start_date.timetuple()) * 1000)
        end_timestamp = int(calendar.timegm(end_date.timetuple()) * 1000)
        time_delta = int(TIME_INTERVAL_MAP[freq].total_seconds() * 1000)

        ohlcv_list = []
        while start_timestamp <= end_timestamp:
            self.throttle()
            ohlcv = self._exchange.fetch_ohlcv(symbol_root, freq, start_timestamp, self.N_LIMIT_BAR)
            if ohlcv is not None and len(ohlcv):
                ohlcv_list.extend(ohlcv)
                actual_last_timestamp = ohlcv[-1][0]
                logger.debug('download {symbol}:{freq} from [{start} ~~ {end}]'.format(
                    symbol=symbol,
                    freq=freq,
                    start=datetime.datetime.fromtimestamp(start_timestamp / 1000),
                    end=datetime.datetime.fromtimestamp(actual_last_timestamp / 1000),
                ))
                expect_last_timestamp = start_timestamp + (self.N_LIMIT_BAR - 1) * time_delta
                next_timestamp = max(actual_last_timestamp, expect_last_timestamp) + time_delta
                start_timestamp = next_timestamp
            else:
                break

        # transform into data frame:
        #   1. check data duplication
        #   2. constrain data in giver date range
        #   3. timestamp -> datetime
        bar_df = pd.DataFrame(ohlcv_list, columns=['datetime', 'open', 'high', 'low', 'close', 'volume'])

        bar_list = []
        if len(bar_df):
            non_duplicate_index = ~bar_df['datetime'].duplicated()
            date_range_index = bar_df['datetime'] <= end_timestamp
            filter_index = non_duplicate_index & date_range_index

            bar_df = bar_df[filter_index]
            bar_df['datetime'] = pd.to_datetime(bar_df['datetime'], unit='ms')
            bar_df['frequency'] = freq
            bar_df['symbol'] = symbol

            if return_df:
                return bar_df

            ohlcv_records = bar_df.to_dict('records')
            for record in ohlcv_records:
                bar = BarData.from_dict(record)
                bar_list.append(bar)
        return bar_list

    def first_valid_date(self, symbol):
        self.throttle()
        symbol_root = self.contracts[symbol].symbol_root
        start_date = DEFAULT_START_TIME
        freq = '1d'
        start_timestamp = int(calendar.timegm(start_date.timetuple()) * 1000)
        ohlcv = self._exchange.fetch_ohlcv(symbol_root, freq, start_timestamp, 5)
        if len(ohlcv):
            return datetime.datetime.utcfromtimestamp(ohlcv[0][0] / 1000.0)
        else:
            return start_date

    def back_fill_bars(self, symbols, periods=50, freq='1m'):
        bar_list = []
        end_date = datetime.datetime.utcnow() - TIME_INTERVAL_MAP[freq]
        start_date = end_date - TIME_INTERVAL_MAP[freq] * periods
        for symbol in symbols:
            bars = self.fetch_bars(symbol, freq, start_date, end_date)
            bar_list.extend(bars)
        return bar_list

    def fetch_depth(self, symbol):
        self.throttle()
        symbol_root = self.contracts[symbol].symbol_root
        data = self._exchange.fetch_l2_order_book(symbol_root)
        depth = DepthData()
        depth.symbol = symbol
        for n, bid in enumerate(data['bids'][:DepthData.N_DEPTH]):
            depth.bid_prices[n] = bid[0]
            depth.bid_volumes[n] = bid[1]

        for n, ask in enumerate(data['asks'][:DepthData.N_DEPTH]):
            depth.ask_prices[n] = ask[0]
            depth.ask_volumes[n] = ask[1]

        if data['timestamp']:
            depth.datetime = datetime.datetime.utcfromtimestamp(data['timestamp'] / 1000)
        else:
            depth.datetime = datetime.datetime.utcnow()
        return depth

    # --------------------- contract information ---------------------
    def fetch_contract(self, return_df=False):
        # codes are removed
        pass

    @staticmethod
    def _get_precision(number):
        return np.float_power(10, -number)
Ejemplo n.º 15
0
def toWei(amount, decimals):
    return Exchange.to_wei(amount, decimals)
Ejemplo n.º 16
0
def fromWei(amount, decimals):
    return Exchange.from_wei(amount, decimals)
Ejemplo n.º 17
0
    def create_order(self, market, type, price, side, amount):
        self._update_orders()
        type_market = False
        type_limit = False
        if type == 'market':
            if price is not None:
                raise InvalidOrder(
                    'ExchangeAccount: market order has no price')
            type_market = True
        elif type == 'limit':
            price = _convert_float_or_raise(price, 'ExchangeAccount: price')
            type_limit = True
            if price <= 0:
                raise BadRequest('ExchangeAccount: price needs to be positive')
        else:
            raise InvalidOrder(
                'ExchangeAccount: only market and limit order supported')
        if market is None:
            raise InvalidOrder('ExchangeAccount: market is None')
        symbol = market.get('symbol')
        ohlcv = self._ohlcvs.get(symbol)
        if ohlcv is None:
            raise InvalidOrder('ExchangeAccount: no prices available for {}'
                               .format(symbol))
        if side not in ['buy', 'sell']:
            raise InvalidOrder('ExchangeAccount: side {} not supported'
                               .format(side))
        buy = side == 'buy'
        amount = _convert_float_or_raise(amount, 'ExchangeAccount: amount')
        if amount <= 0:
            raise BadRequest('ExchangeAccount: amount needs to be positive')
        base = market.get('base')
        quote = market.get('quote')
        if base is None:
            raise BadRequest('ExchangeAccount: market has no base')
        if quote is None:
            raise BadRequest('ExchangeAccount: market has no quote')

        self._last_order_id += 1
        order_id = str(self._last_order_id)
        date = self._timeframe.date()
        timestamp = int(date.value / 10e5)
        order = {
            'info': {},
            'id': order_id,
            'timestamp': timestamp,
            'datetime': Exchange.iso8601(timestamp),
            'lastTradeTimestamp': None,
            'symbol': symbol,
            'type': type,
            'side': side,
            'price': None,
            'amount': amount,
            'cost': None,
            'average': None,
            'filled': 0,
            'remaining': amount,
            'status': 'open',
            'fee': {'currency': base if buy else quote,
                    'cost': None,
                    'rate': None},
            'trades': None,
        }

        if type_market:
            # Determinie the price of the market order
            # We could use the next low/high to fill the order, but then we
            # need to wait for the next date to fill the order, otherwise we
            # would introduce a possibility to see the future price
            # (Look-Ahead Bias)
            # If we wait for the next date, we would return a market order that
            # is pending, but this should never happen in reality
            # Maybe the factor should depend on the volume
            factor = Decimal('0.0015')
            if buy:
                price = (1 + factor) * _convert_float(ohlcv['high'][date])
            else:
                price = (1 - factor) * _convert_float(ohlcv['low'][date])
            fee_percentage = market.get('taker', 0)
            fee_percentage = _convert_float_or_raise(fee_percentage,
                                                     'ExchangeAccount: fee')
            self._update_balance(price, amount, base, quote, buy,
                                 fee_percentage)
            self._fill_order(order, buy, price, timestamp, fee_percentage)
            self._closed_orders[order_id] = order
        if type_limit:
            # TODO Probably use taker fee, if the order can be filled now
            fee_percentage = market.get('maker', 0)
            fee_percentage = _convert_float_or_raise(fee_percentage,
                                                     'ExchangeAccount: fee')
            if buy:
                self._balances[quote].change_used(price * amount)
            else:
                self._balances[base].change_used(amount)
            self._open_orders[order_id] = order
            self._private_order_info[order_id] = {
                'id': order_id,
                'base': base,
                'quote': quote,
                'price': price,
                'buy': buy,
                'fee_percentage': fee_percentage,
                'fillable_date': self._limit_order_fillable_date(
                    symbol, buy, price),
            }
            self._update_next_private_order_to_update()

        return {'id': order_id,
                'info': {}}