def test_should_use_new_spreads_even_if_config_not_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_spread_importing_config(tmpdir))
        reloadable_config.logger = MagicMock()

        # when
        spread_feed = {
            "buySpread": "0.1",
            "sellSpread": "1.0"
        }
        config = reloadable_config.get_config(spread_feed)

        # then
        assert config["usedBuySpread"] == 0.2
        assert config["usedSellSpread"] == 3.0

        # and
        # [a log message that the config was loaded gets generated]
        assert reloadable_config.logger.info.call_count == 1

        # when
        spread_feed = {
            "buySpread": "0.2",
            "sellSpread": "0.5"
        }
        config = reloadable_config.get_config(spread_feed)

        # then
        assert config["usedBuySpread"] == 0.4
        assert config["usedSellSpread"] == 1.5

        # and
        # [a log message that the config was reloaded gets generated]
        assert reloadable_config.logger.info.call_count == 2
    def test_should_use_new_spreads_even_if_config_not_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_spread_importing_config(tmpdir))
        reloadable_config.logger = MagicMock()

        # when
        spread_feed = {
            "buySpread": "0.1",
            "sellSpread": "1.0"
        }
        config = reloadable_config.get_config(spread_feed)

        # then
        assert config["usedBuySpread"] == 0.2
        assert config["usedSellSpread"] == 3.0

        # and
        # [a log message that the config was loaded gets generated]
        assert reloadable_config.logger.info.call_count == 1

        # when
        spread_feed = {
            "buySpread": "0.2",
            "sellSpread": "0.5"
        }
        config = reloadable_config.get_config(spread_feed)

        # then
        assert config["usedBuySpread"] == 0.4
        assert config["usedSellSpread"] == 1.5

        # and
        # [no log message that the config was reloaded gets generated]
        # [as it was only parsed again]
        assert reloadable_config.logger.info.call_count == 1
    def test_should_read_file_again_if_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_advanced_config(tmpdir, "b"))
        reloadable_config.logger = MagicMock()

        # when
        config = reloadable_config.get_config({})

        # then
        assert config["a"] == "b"

        # and
        # [a log message that the config was loaded gets generated]
        assert reloadable_config.logger.info.call_count == 1

        # when
        self.write_advanced_config(tmpdir, "z")
        config = reloadable_config.get_config({})

        # then
        assert config["a"] == "z"

        # and
        # [a log message that the config was reloaded gets generated]
        assert reloadable_config.logger.info.call_count == 2
def test_airswap_read_no_adjacent_bands(tmpdir):
    sell_bands_file = BandConfig.two_adjacent_sell_bands_config(tmpdir)
    sell_bands_config = ReloadableConfig(str(sell_bands_file))
    airswap_sell_bands = AirswapBands.read(sell_bands_config, EmptyFeed(), FixedFeed({'canBuy': True, 'canSell': True}), History())
    assert len(airswap_sell_bands.sell_bands) == 0

    buy_bands_file = BandConfig.two_adjacent_buy_bands_config(tmpdir)
    buy_bands_config = ReloadableConfig(str(buy_bands_file))
    airswap_buy_bands = AirswapBands.read(buy_bands_config, EmptyFeed(), FixedFeed({'canBuy': True, 'canSell': True}), History())
    assert len(airswap_buy_bands.buy_bands) == 0
Example #5
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='etherdelta-market-maker-keeper')

        self.add_arguments(parser=parser)

        parser.set_defaults(cancel_on_shutdown=False,
                            withdraw_on_shutdown=False)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.sai = ERC20Token(web3=self.web3, address=self.tub.sai())
        self.gem = ERC20Token(web3=self.web3, address=self.tub.gem())

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.eth_reserve = Wad.from_number(self.arguments.eth_reserve)
        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.min_eth_deposit = Wad.from_number(self.arguments.min_eth_deposit)
        self.min_sai_deposit = Wad.from_number(self.arguments.min_sai_deposit)
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(
            self.arguments, self.tub)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        if self.eth_reserve <= self.min_eth_balance:
            raise Exception(
                "--eth-reserve must be higher than --min-eth-balance")

        assert (self.arguments.order_expiry_threshold >= 0)
        assert (self.arguments.order_no_cancel_threshold >=
                self.arguments.order_expiry_threshold)

        self.history = History()
        self.etherdelta = EtherDelta(web3=self.web3,
                                     address=Address(
                                         self.arguments.etherdelta_address))
        self.etherdelta_api = EtherDeltaApi(
            client_tool_directory="lib/pymaker/utils/etherdelta-client",
            client_tool_command="node main.js",
            api_server=self.arguments.etherdelta_socket,
            number_of_attempts=self.arguments.etherdelta_number_of_attempts,
            retry_interval=self.arguments.etherdelta_retry_interval,
            timeout=self.arguments.etherdelta_timeout)

        self.our_orders = list()
Example #6
0
    def __init__(self, args: list):
        parser = argparse.ArgumentParser(prog='bitzlato-market-maker-keeper')
        self.add_arguments(parser)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()

        self.bittrex_api = BittrexApi(
            api_server=self.arguments.bittrex_api_server,
            api_key=self.arguments.bittrex_api_key,
            secret_key=self.arguments.bittrex_secret_key,
            timeout=self.arguments.bittrex_timeout)

        self.order_book_manager = OrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency)
        self.order_book_manager.get_orders_with(
            lambda: self.bittrex_api.get_orders(self.pair()))
        self.order_book_manager.get_balances_with(
            lambda: self.bittrex_api.get_balances())
        self.order_book_manager.cancel_orders_with(
            lambda order: self.bittrex_api.cancel_order(order.order_id))
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)
        self.order_book_manager.start()
Example #7
0
class GasPriceFile(GasPrice):
    """Gas price configuration dynamically reloadable from a file.

    It is roughly an equivalent of implementation of :py:class:`pymaker.gas.IncreasingGasPrice`,
    but it uses `ReloadableConfig` to read the gas parameters from a file, and will dynamically
    reload that file whenever it changes. It allows to update the gas price dynamically
    for running keepers.

    Attributes:
        filename: Filename of the configuration file.
    """
    def __init__(self, filename: str):
        assert(isinstance(filename, str))

        self.reloadable_config = ReloadableConfig(filename)

    def get_gas_price(self, time_elapsed: int) -> Optional[int]:
        assert(isinstance(time_elapsed, int))

        config = self.reloadable_config.get_config()
        gas_price = config.get('gasPrice', None)
        gas_price_increase = config.get('gasPriceIncrease', None)
        gas_price_increase_every = config.get('gasPriceIncreaseEvery', None)
        gas_price_max = config.get('gasPriceMax', None)

        if gas_price is not None:
            if gas_price_increase and gas_price_increase_every:
                strategy = IncreasingGasPrice(gas_price, gas_price_increase, gas_price_increase_every, gas_price_max)
            else:
                strategy = FixedGasPrice(gas_price)
        else:
            strategy = DefaultGasPrice()

        return strategy.get_gas_price(time_elapsed=time_elapsed)
    def test_should_read_simple_file(self, tmpdir):
        # when
        config = ReloadableConfig(self.write_sample_config(tmpdir)).get_config({})

        # then
        assert len(config) == 1
        assert config["a"] == "b"
Example #9
0
    def _parse_configs(data: dict) -> (list, dict):
        pairs = []
        configs = {}
        for market in data['markets']:
            pair = ImtokenPair(market['pair'])
            pairs.append(pair)

            band_config = ReloadableConfig(market['bands'])

            market_args = MarketArgs(market)
            price_feed = PriceFeedFactory().create_price_feed(market_args)
            spread_feed = create_spread_feed(market_args)
            control_feed = create_control_feed(market_args)

            config = {
                'bands_config': band_config,
                'price_feed': price_feed,
                'spread_feed': spread_feed,
                'control_feed': control_feed,
                'history': History()
            }

            configs[pair.base_pair] = config
            configs[pair.counter_pair] = config

        return pairs, configs
Example #10
0
    def read(reloadable_config: ReloadableConfig, spread_feed: Feed,
             history: History):
        assert (isinstance(reloadable_config, ReloadableConfig))
        assert (isinstance(history, History))

        try:
            config = reloadable_config.get_config(spread_feed.get()[0])

            buy_bands = list(map(BuyBand, config['buyBands']))
            buy_limits = SideLimits(
                config['buyLimits'] if 'buyLimits' in config else [],
                history.buy_history)
            sell_bands = list(map(SellBand, config['sellBands']))
            sell_limits = SideLimits(
                config['sellLimits'] if 'sellLimits' in config else [],
                history.sell_history)
        except Exception as e:
            logging.getLogger().warning(
                f"Config file is invalid ({e}). Treating the config file as it has no bands."
            )

            buy_bands = []
            buy_limits = SideLimits([], history.buy_history)
            sell_bands = []
            sell_limits = SideLimits([], history.buy_history)

        return Bands(buy_bands=buy_bands,
                     buy_limits=buy_limits,
                     sell_bands=sell_bands,
                     sell_limits=sell_limits)
def test_new_sell_orders_taker_amount_success_case(tmpdir):
    bands_file = BandConfig.sample_config_dif_margins(tmpdir)
    bands_config = ReloadableConfig(str(bands_file))
    airswap_bands = AirswapBands.read(bands_config, EmptyFeed(), FixedFeed({'canBuy': True, 'canSell': True}), History())

    # maker_amount -> denominated in WETH
    maker_amount = Wad(0)
    taker_amount = Wad(1770600000000000000)
    our_sell_balance = Wad(1562000000000000000000)
    sell_limit = Wad(1562000000000000000000)
    target_price = WebSocketPriceFeed(FakeFeed({"buyPrice": "120", "sellPrice": "130"})).get_price()

    new_order = airswap_bands._new_side_orders('sell',
                                               maker_amount,
                                               taker_amount,
                                               our_sell_balance,
                                               sell_limit,
                                               airswap_bands.sell_bands[0],
                                               target_price.sell_price)

    # -- pricing logic --
    # sellPrice = 130 * avgMargin = 0.05 = 6.5
    # 130 + 6.5 = 136.5
    # taker_amount = 1.7706 / 136.5 = 0.01297142857142857142857

    assert new_order['maker_amount'].__float__() == 0.01297142857142857142857
    assert new_order['taker_amount'].__float__() == 1.7706
Example #12
0
    def __init__(self, arguments: Namespace, pyex_api: PyexAPI):

        setup_logging(arguments)

        if arguments.__contains__('web3'):
            self.web3 = arguments.web3
        else:
            web3_endpoint = f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}"
            web3_options = {"timeout": self.arguments.rpc_timeout}
            self.web3 = Web3(HTTPProvider(endpoint_uri=web3_endpoint, request_kwargs=web3_options))

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.bands_config = ReloadableConfig(arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(arguments)
        self.spread_feed = create_spread_feed(arguments)
        self.control_feed = create_control_feed(arguments)

        self.order_history_reporter = create_order_history_reporter(arguments)

        self.history = History()

        self.init_order_book_manager(arguments, pyex_api)
    def test_should_read_file_again_if_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_advanced_config(tmpdir, "b"))

        # when
        config = reloadable_config.get_config({})

        # then
        assert config["a"] == "b"

        # when
        self.write_advanced_config(tmpdir, "z")
        config = reloadable_config.get_config({})

        # then
        assert config["a"] == "z"
Example #14
0
 def create_bands(config_file):
     config = ReloadableConfig(str(config_file))
     return Bands.read(config, EmptyFeed(),
                       FixedFeed({
                           'canBuy': True,
                           'canSell': True
                       }), History())
def test_new_buy_orders_taker_amount_exceed_buy_balance_fail_case(tmpdir):
    bands_file = BandConfig.sample_config(tmpdir)
    bands_config = ReloadableConfig(str(bands_file))
    airswap_bands = AirswapBands.read(bands_config, EmptyFeed(), FixedFeed({'canBuy': True, 'canSell': True}), History())

    # maker_amount -> denominated in DAI
    maker_amount = Wad(0)
    taker_amount = Wad(11360000000000000000)
    our_buy_balance = Wad(50000000000000000)
    buy_limit = Wad(1562000000000000000000)
    target_price = WebSocketPriceFeed(FakeFeed({"buyPrice": "120", "sellPrice": "130"})).get_price()

    new_order = airswap_bands._new_side_orders('buy',
                                               maker_amount,
                                               taker_amount,
                                               our_buy_balance,
                                               buy_limit,
                                               airswap_bands.buy_bands[0],
                                               target_price.buy_price)

    # -- pricing logic --
    # buyPrice = 120 * minMargin = 0.02 = 117.6
    # maker_amount = 11.36000 / 117.6 = 0.09659863945
    # our_buy_balance = 0.050000000000000000 !!BREAK!!

    assert new_order == {}
def test_new_buy_orders_taker_amount_success_case(tmpdir):
    bands_file = BandConfig.sample_config(tmpdir)
    bands_config = ReloadableConfig(str(bands_file))
    airswap_bands = AirswapBands.read(bands_config, EmptyFeed(), FixedFeed({'canBuy': True, 'canSell': True}), History())

    # maker_amount -> denominated in DAI
    maker_amount = Wad(0)
    taker_amount = Wad(11360000000000000000)
    our_buy_balance = Wad(1562000000000000000000)
    buy_limit = Wad(1562000000000000000000)
    target_price = WebSocketPriceFeed(FakeFeed({"buyPrice": "120", "sellPrice": "130"})).get_price()

    new_order = airswap_bands._new_side_orders('buy',
                                               maker_amount,
                                               taker_amount,
                                               our_buy_balance,
                                               buy_limit,
                                               airswap_bands.buy_bands[0],
                                               target_price.buy_price)

    # -- pricing logic --
    # buyPrice = 120 * minMargin = 0.04 = 4.8
    # 120 - 4.8 = 115.2
    # maker_amount = 11.36000 / 115.2 = 0.09861111111111111111111

    assert new_order['taker_amount'].__float__() == 11.36000
    assert new_order['maker_amount'].__float__() == 0.09861111111111111111111
Example #17
0
    def test_should_read_file_again_if_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(
            self.write_advanced_config(tmpdir, "b"))

        # when
        config = reloadable_config.get_config()

        # then
        assert config["a"] == "b"

        # when
        self.write_advanced_config(tmpdir, "z")
        config = reloadable_config.get_config()

        # then
        assert config["a"] == "z"
    def test_should_read_advanced_file(self, tmpdir):
        # when
        config = ReloadableConfig(self.write_advanced_config(tmpdir, "b")).get_config({})

        # then
        assert len(config) == 2
        assert config["a"] == "b"
        assert config["c"] == "b"
Example #19
0
    def test_should_import_other_config_file(self, tmpdir):
        # when
        self.write_global_config(tmpdir, 17.0, 11.0)
        config = ReloadableConfig(self.write_importing_config(tmpdir)).get_config({})

        # then
        assert len(config) == 2
        assert config["firstValueMultiplied"] == 34.0
        assert config["secondValueMultiplied"] == 33.0
Example #20
0
    def __init__(self, reloadable_config: ReloadableConfig):
        assert (isinstance(reloadable_config, ReloadableConfig))

        config = reloadable_config.get_config()
        self.buy_bands = list(map(BuyBand, config['buyBands']))
        self.sell_bands = list(map(SellBand, config['sellBands']))

        if self._bands_overlap(self.buy_bands) or self._bands_overlap(
                self.sell_bands):
            raise Exception(f"Bands in the config file overlap")
Example #21
0
    def read(reloadable_config: ReloadableConfig, spread_feed: Feed,
             control_feed: Feed, history: History):
        assert (isinstance(reloadable_config, ReloadableConfig))
        assert (isinstance(spread_feed, Feed))
        assert (isinstance(control_feed, Feed))
        assert (isinstance(history, History))

        try:
            config = reloadable_config.get_config(spread_feed.get()[0])
            control_feed_value = control_feed.get()[0]

            buy_bands = list(map(BuyBand, config['buyBands']))
            buy_limits = SideLimits(
                config['buyLimits'] if 'buyLimits' in config else [],
                history.buy_history)
            sell_bands = list(map(SellBand, config['sellBands']))
            sell_limits = SideLimits(
                config['sellLimits'] if 'sellLimits' in config else [],
                history.sell_history)

            if 'canBuy' not in control_feed_value or 'canSell' not in control_feed_value:
                logging.getLogger().warning(
                    "Control feed expired. Assuming no buy bands and no sell bands."
                )

                buy_bands = []
                sell_bands = []

            else:
                if not control_feed_value['canBuy']:
                    logging.getLogger().warning(
                        "Control feed says we shall not buy. Assuming no buy bands."
                    )
                    buy_bands = []

                if not control_feed_value['canSell']:
                    logging.getLogger().warning(
                        "Control feed says we shall not sell. Assuming no sell bands."
                    )
                    sell_bands = []

        except Exception as e:
            logging.getLogger().exception(
                f"Config file is invalid ({e}). Treating the config file as it has no bands."
            )

            buy_bands = []
            buy_limits = SideLimits([], history.buy_history)
            sell_bands = []
            sell_limits = SideLimits([], history.buy_history)

        return Bands(buy_bands=buy_bands,
                     buy_limits=buy_limits,
                     sell_bands=sell_bands,
                     sell_limits=sell_limits)
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='paradex-market-maker-keeper')
        self.add_arguments(parser=parser)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.pair = self.arguments.pair.upper()
        self.token_buy = ERC20Token(web3=self.web3,
                                    address=Address(
                                        self.arguments.buy_token_address))
        self.token_sell = ERC20Token(web3=self.web3,
                                     address=Address(
                                         self.arguments.sell_token_address))
        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_max_decimals = None
        self.amount_max_decimals = None
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()
        self.zrx_exchange = ZrxExchangeV2(web3=self.web3,
                                          address=Address(
                                              self.arguments.exchange_address))
        self.paradex_api = ParadexApi(self.zrx_exchange,
                                      self.arguments.paradex_api_server,
                                      self.arguments.paradex_api_key,
                                      self.arguments.paradex_api_timeout)

        self.order_book_manager = OrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency, max_workers=1)
        self.order_book_manager.get_orders_with(
            lambda: self.paradex_api.get_orders(self.pair))
        self.order_book_manager.get_balances_with(lambda: self.get_balances())
        self.order_book_manager.cancel_orders_with(
            lambda order: self.paradex_api.cancel_order(order.order_id))
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)
        self.order_book_manager.start()
Example #23
0
    def test_should_reevaluate_if_other_config_file_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_importing_config(tmpdir))

        # when
        self.write_global_config(tmpdir, 17.0, 11.0)
        config = reloadable_config.get_config({})

        # then
        assert len(config) == 2
        assert config["firstValueMultiplied"] == 34.0
        assert config["secondValueMultiplied"] == 33.0

        # when
        self.write_global_config(tmpdir, 18.0, 3.0)
        config = reloadable_config.get_config({})

        # then
        assert len(config) == 2
        assert config["firstValueMultiplied"] == 36.0
        assert config["secondValueMultiplied"] == 9.0
Example #24
0
    def create_bands(config_file, rules=None):
        if rules is None:
          rules = BinanceUsRules(pair="ETH-USDC", 
                                 min_price=Wad.from_number(0.01), 
                                 max_price=Wad.from_number(100000.0), 
                                 tick_size=Wad.from_number(0.01),
                                 min_quantity=Wad.from_number(0.00001),
                                 max_quantity=Wad.from_number(9000.0),
                                 step_size=Wad.from_number(0.00001))

        config = ReloadableConfig(str(config_file))
        return BinanceBands.read(config, EmptyFeed(), FixedFeed({'canBuy': True, 'canSell': True}), History(), rules)
    def test_should_import_spreads(self, tmpdir):
        # given
        spread_feed = {"buySpread": "0.1", "sellSpread": "1.0"}

        # when
        config = ReloadableConfig(
            self.write_spread_importing_config(tmpdir)).get_config(spread_feed)

        # then
        assert len(config) == 2
        assert config["usedBuySpread"] == 0.2
        assert config["usedSellSpread"] == 3.0
Example #26
0
    def __init__(self, args: list):
        parser = argparse.ArgumentParser(prog='bibox-market-maker-keeper')

        parser.add_argument("--bibox-api-server", type=str, default="https://api.bibox.com",
                            help="Address of the Bibox API server (default: 'https://api.bibox.com')")

        parser.add_argument("--bibox-api-key", type=str, required=True,
                            help="API key for the Bibox API")

        parser.add_argument("--bibox-secret", type=str, required=True,
                            help="Secret for the Bibox API")

        parser.add_argument("--bibox-timeout", type=float, default=9.5,
                            help="Timeout for accessing the Bibox API (in seconds, default: 9.5)")

        parser.add_argument("--pair", type=str, required=True,
                            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--config", type=str, required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed", type=str, required=True,
                            help="Source of price feed")

        parser.add_argument("--price-feed-expiry", type=int, default=120,
                            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed", type=str,
                            help="Source of spread feed")

        parser.add_argument("--spread-feed-expiry", type=int, default=3600,
                            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--debug", dest='debug', action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        self.history = History()
        self.bibox_api = BiboxApi(api_server=self.arguments.bibox_api_server,
                                  api_key=self.arguments.bibox_api_key,
                                  secret=self.arguments.bibox_secret,
                                  timeout=self.arguments.bibox_timeout)

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)

        self.order_book_manager = OrderBookManager(refresh_frequency=3)
        self.order_book_manager.get_orders_with(lambda: self.bibox_api.get_orders(pair=self.pair(), retry=True))
        self.order_book_manager.get_balances_with(lambda: self.bibox_api.coin_list(retry=True))
        self.order_book_manager.start()
Example #27
0
    def __init__(self, arguments: Namespace, pyex_api: PyexAPI):

        setup_logging(arguments)

        self.bands_config = ReloadableConfig(arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(arguments)
        self.spread_feed = create_spread_feed(arguments)
        self.control_feed = create_control_feed(arguments)

        self.order_history_reporter = create_order_history_reporter(arguments)
        self.history = History()

        self.init_order_book_manager(arguments, pyex_api)
Example #28
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='0x-market-maker-keeper')
        self.add_arguments(parser=parser)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.eth_address = Address(self.arguments.eth_token_address)

        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.bands_config = ReloadableConfig(self.arguments.config)
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()

        # Delegate 0x specific init to a function to permit overload for 0xv3
        self.zrx_exchange = None
        self.zrx_relayer_api = None
        self.zrx_api = None
        self.pair = None
        self.init_zrx()

        self.placed_zrx_orders = []
        self.placed_zrx_orders_lock = Lock()

        self.order_book_manager = OrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency)
        self.order_book_manager.get_orders_with(lambda: self.get_orders())
        self.order_book_manager.get_balances_with(lambda: self.get_balances())
        self.order_book_manager.place_orders_with(self.place_order_function)
        self.order_book_manager.cancel_orders_with(self.cancel_order_function)
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)
        self.order_book_manager.start()
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='idex-market-maker-keeper')
        self.add_arguments(parser=parser)

        parser.set_defaults(cancel_on_shutdown=False,
                            withdraw_on_shutdown=False)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.sai = ERC20Token(web3=self.web3, address=self.tub.sai())
        self.gem = ERC20Token(web3=self.web3, address=self.tub.gem())

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.eth_reserve = Wad.from_number(self.arguments.eth_reserve)
        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.min_eth_deposit = Wad.from_number(self.arguments.min_eth_deposit)
        self.min_sai_deposit = Wad.from_number(self.arguments.min_sai_deposit)
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(
            self.arguments, self.tub)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        if self.eth_reserve <= self.min_eth_balance:
            raise Exception(
                "--eth-reserve must be higher than --min-eth-balance")

        self.history = History()
        self.idex = IDEX(self.web3, Address(self.arguments.idex_address))
        self.idex_api = IDEXApi(self.idex, self.arguments.idex_api_server,
                                self.arguments.idex_timeout)
Example #30
0
    def __init__(self, reloadable_config: ReloadableConfig, history: History):
        assert (isinstance(reloadable_config, ReloadableConfig))
        assert (isinstance(history, History))

        config = reloadable_config.get_config()
        self.buy_bands = list(map(BuyBand, config['buyBands']))
        self.buy_limits = SideLimits(
            config['buyLimits'] if 'buyLimits' in config else [],
            history.buy_history)
        self.sell_bands = list(map(SellBand, config['sellBands']))
        self.sell_limits = SideLimits(
            config['sellLimits'] if 'sellLimits' in config else [],
            history.sell_history)

        if self._bands_overlap(self.buy_bands) or self._bands_overlap(
                self.sell_bands):
            raise Exception(f"Bands in the config file overlap")
Example #31
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='gateio-market-maker-keeper')

        parser.add_argument("--gateio-api-server", type=str, default="https://data.gate.io",
                            help="Address of the Gate.io API server (default: 'https://data.gate.io')")

        parser.add_argument("--gateio-api-key", type=str, required=True,
                            help="API key for the Gate.io API")

        parser.add_argument("--gateio-secret-key", type=str, required=True,
                            help="Secret key for the Gate.io API")

        parser.add_argument("--gateio-timeout", type=float, default=9.5,
                            help="Timeout for accessing the Gate.io API (in seconds, default: 9.5)")

        parser.add_argument("--pair", type=str, required=True,
                            help="Token pair on which the keeper should operate")

        parser.add_argument("--config", type=str, required=True,
                            help="Buy/sell bands configuration file")

        parser.add_argument("--price-feed", type=str,
                            help="Source of price feed")

        parser.add_argument("--price-feed-expiry", type=int, default=120,
                            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--debug", dest='debug', action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        logging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s',
                            level=(logging.DEBUG if self.arguments.debug else logging.INFO))
        logging.getLogger('urllib3.connectionpool').setLevel(logging.INFO)
        logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.INFO)

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments.price_feed, self.arguments.price_feed_expiry)

        self.gateio_api = GateIOApi(api_server=self.arguments.gateio_api_server,
                                    api_key=self.arguments.gateio_api_key,
                                    secret_key=self.arguments.gateio_secret_key,
                                    timeout=self.arguments.gateio_timeout)

        self._last_order_creation = 0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='airswap-market-maker-keeper')

        self.add_arguments(parser=parser)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.airswap_api = AirswapApi(self.arguments.airswap_api_server,
                                      self.arguments.airswap_api_timeout)

        if self.arguments.buy_token_address == '0x0000000000000000000000000000000000000000':
            self.token_buy = EthToken(web3=self.web3,
                                      address=Address(
                                          self.arguments.buy_token_address))
        else:
            self.token_buy = ERC20Token(web3=self.web3,
                                        address=Address(
                                            self.arguments.buy_token_address))

        self.eth_token_sell = EthToken(
            web3=self.web3,
            address=Address(self.arguments.eth_sell_token_address))
        self.weth_token_sell = ERC20Token(
            web3=self.web3,
            address=Address(self.arguments.weth_sell_token_address))

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()
    def __init__(self, reloadable_config: ReloadableConfig, spread_feed: Feed, history: History):
        assert(isinstance(reloadable_config, ReloadableConfig))
        assert(isinstance(history, History))

        try:
            config = reloadable_config.get_config(spread_feed.get()[0])

            self.buy_bands = list(map(BuyBand, config['buyBands']))
            self.buy_limits = SideLimits(config['buyLimits'] if 'buyLimits' in config else [], history.buy_history)
            self.sell_bands = list(map(SellBand, config['sellBands']))
            self.sell_limits = SideLimits(config['sellLimits'] if 'sellLimits' in config else [], history.sell_history)
        except Exception as e:
            self.logger.warning(f"Config file is invalid ({e}). Treating the config file as it has no bands.")

            self.buy_bands = []
            self.buy_limits = SideLimits([], history.buy_history)
            self.sell_bands = []
            self.sell_limits = SideLimits([], history.buy_history)

        if self._bands_overlap(self.buy_bands) or self._bands_overlap(self.sell_bands):
            self.logger.warning("Bands in the config file overlap. Treating the config file as it has no bands.")

            self.buy_bands = []
            self.sell_bands = []