Source code for tradeexecutor.ethereum.ethereum_protocol_adapters

"""Default protocols support for EVM blockchains.

See :py:mod:`tradeexecutor.strategy.generic.pair_configurator`.
"""

from typing import Set

from web3 import Web3
from eth_defi.aave_v3.deployment import fetch_deployment as fetch_aave_deployment
from eth_defi.uniswap_v3.deployment import fetch_deployment as fetch_uniswap_v3_deployment
from eth_defi.one_delta.deployment import fetch_deployment as fetch_1delta_deployment
from eth_defi.aave_v3.constants import AAVE_V3_DEPLOYMENTS
from eth_defi.one_delta.constants import ONE_DELTA_DEPLOYMENTS

from tradingstrategy.chain import ChainId
from tradingstrategy.exchange import ExchangeUniverse, ExchangeType

from tradeexecutor.state.identifier import TradingPairIdentifier
from tradeexecutor.strategy.default_routing_options import TradeRouting
from tradeexecutor.strategy.generic.pair_configurator import PairConfigurator, ProtocolRoutingId, ProtocolRoutingConfig
from tradeexecutor.strategy.generic.default_protocols import default_match_router, default_supported_routers
from tradeexecutor.strategy.reserve_currency import ReserveCurrency
from tradeexecutor.strategy.trading_strategy_universe import TradingStrategyUniverse


[docs]def get_exchange_type( exchange_universe: ExchangeUniverse, pair: TradingPairIdentifier, ) -> ExchangeType: assert pair.exchange_address is not None, f"Pair missing exchange_address: {pair}" exchange = exchange_universe.get_by_chain_and_factory(ChainId(pair.chain_id), pair.exchange_address) assert exchange is not None, f"Exchange address {pair.exchange_address} for pair {pair}: data not loaded" return exchange.exchange_type
[docs]def create_uniswap_v2_adapter( web3: Web3, strategy_universe: TradingStrategyUniverse, routing_id: ProtocolRoutingId, ) -> ProtocolRoutingConfig: # TODO: Avoid circular imports for now from tradeexecutor.ethereum.uniswap_v2.uniswap_v2_live_pricing import UniswapV2LivePricing from tradeexecutor.ethereum.uniswap_v2.uniswap_v2_valuation import UniswapV2PoolRevaluator from tradeexecutor.ethereum.routing_data import create_uniswap_v2_compatible_routing assert routing_id.router_name == "uniswap-v2" assert len(strategy_universe.data_universe.chains) == 1 assert len(strategy_universe.reserve_assets) == 1 reserve = strategy_universe.reserve_assets[0] assert reserve.token_symbol == "USDC" exchange_universe = strategy_universe.data_universe.exchange_universe chain_id = strategy_universe.get_single_chain() exchange = exchange_universe.get_by_chain_and_slug(chain_id, routing_id.exchange_slug) if exchange.exchange_slug == "quickswap": routing_model = create_uniswap_v2_compatible_routing( TradeRouting.quickswap_usdc, ReserveCurrency.usdc, ) elif exchange.exchange_slug == "sushi": routing_model = create_uniswap_v2_compatible_routing( TradeRouting.sushi_usdc, ReserveCurrency.usdc ) else: routing_model = create_uniswap_v2_compatible_routing( TradeRouting.uniswap_v2_usdc, ReserveCurrency.usdc ) pricing_model = UniswapV2LivePricing( web3, strategy_universe.data_universe.pairs, routing_model, ) valuation_model = UniswapV2PoolRevaluator(pricing_model) return ProtocolRoutingConfig( routing_id=routing_id, routing_model=routing_model, pricing_model=pricing_model, valuation_model=valuation_model, )
[docs]def create_uniswap_v3_adapter( web3: Web3, strategy_universe: TradingStrategyUniverse, routing_id: ProtocolRoutingId, ) -> ProtocolRoutingConfig: """Always the same.""" # TODO: Avoid circular imports for now from tradeexecutor.ethereum.uniswap_v3.uniswap_v3_live_pricing import UniswapV3LivePricing from tradeexecutor.ethereum.uniswap_v3.uniswap_v3_routing import UniswapV3Routing from tradeexecutor.ethereum.uniswap_v3.uniswap_v3_valuation import UniswapV3PoolRevaluator from tradeexecutor.ethereum.routing_data import uniswap_v3_address_map assert routing_id.router_name == "uniswap-v3" assert len(strategy_universe.data_universe.chains) == 1 assert len(strategy_universe.reserve_assets) == 1 chain_id = strategy_universe.get_single_chain() reserve_asset = strategy_universe.get_reserve_asset() exchange_universe = strategy_universe.data_universe.exchange_universe # TODO: Add intermediate tokens routing_model = UniswapV3Routing( address_map=uniswap_v3_address_map, chain_id=chain_id, reserve_token_address=reserve_asset.address, allowed_intermediary_pairs={}, ) pricing_model = UniswapV3LivePricing( web3, strategy_universe.data_universe.pairs, routing_model, ) valuation_model = UniswapV3PoolRevaluator(pricing_model) return ProtocolRoutingConfig( routing_id=routing_id, routing_model=routing_model, pricing_model=pricing_model, valuation_model=valuation_model, )
[docs]def create_1delta_adapter( web3: Web3, strategy_universe: TradingStrategyUniverse, routing_id: ProtocolRoutingId, ) -> ProtocolRoutingConfig: # TODO: Avoid circular imports for now from tradeexecutor.ethereum.one_delta.one_delta_live_pricing import OneDeltaLivePricing from tradeexecutor.ethereum.one_delta.one_delta_routing import OneDeltaRouting from tradeexecutor.ethereum.one_delta.one_delta_valuation import OneDeltaPoolRevaluator assert routing_id.router_name == "1delta" assert routing_id.lending_protocol_slug == "aave" assert routing_id.exchange_slug == "uniswap-v3" assert len(strategy_universe.data_universe.chains) == 1 assert len(strategy_universe.reserve_assets) == 1 chain_id = strategy_universe.get_single_chain() chain_slug = chain_id.get_slug() reserve_asset = strategy_universe.get_reserve_asset() assert chain_slug in AAVE_V3_DEPLOYMENTS, f"Chain {chain_slug} not supported for Aave v3" assert chain_slug in ONE_DELTA_DEPLOYMENTS, f"Chain {chain_slug} not supported for 1delta" uniswap_v3_deployment = fetch_uniswap_v3_deployment( web3, "0x1F98431c8aD98523631AE4a59f267346ea31F984", "0xE592427A0AEce92De3Edee1F18E0157C05861564", "0xC36442b4a4522E871399CD717aBDD847Ab11FE88", "0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6", ) aave_v3_deployment = fetch_aave_deployment( web3, pool_address=AAVE_V3_DEPLOYMENTS[chain_slug]["pool"], data_provider_address=AAVE_V3_DEPLOYMENTS[chain_slug]["data_provider"], oracle_address=AAVE_V3_DEPLOYMENTS[chain_slug]["oracle"], ) one_delta_deployment = fetch_1delta_deployment( web3, flash_aggregator_address=ONE_DELTA_DEPLOYMENTS[chain_slug]["broker_proxy"], broker_proxy_address=ONE_DELTA_DEPLOYMENTS[chain_slug]["broker_proxy"], quoter_address=ONE_DELTA_DEPLOYMENTS[chain_slug]["quoter"], ) address_map = { "one_delta_broker_proxy": one_delta_deployment.broker_proxy.address, "one_delta_quoter": one_delta_deployment.quoter.address, "aave_v3_pool": aave_v3_deployment.pool.address, "aave_v3_data_provider": aave_v3_deployment.data_provider.address, "aave_v3_oracle": aave_v3_deployment.oracle.address, "factory": uniswap_v3_deployment.factory.address, "router": uniswap_v3_deployment.swap_router.address, "position_manager": uniswap_v3_deployment.position_manager.address, "quoter": uniswap_v3_deployment.quoter.address } # TODO: Add intermediate tokens routing_model = OneDeltaRouting( address_map=address_map, chain_id=chain_id, reserve_token_address=reserve_asset.address, allowed_intermediary_pairs={}, ) pricing_model = OneDeltaLivePricing( web3, strategy_universe.data_universe.pairs, routing_model, ) valuation_model = OneDeltaPoolRevaluator(pricing_model) return ProtocolRoutingConfig( routing_id=routing_id, routing_model=routing_model, pricing_model=pricing_model, valuation_model=valuation_model, )
[docs]def create_aave_v3_adapter( web3: Web3, strategy_universe: TradingStrategyUniverse, routing_id: ProtocolRoutingId, ) -> ProtocolRoutingConfig: # TODO: Avoid circular imports for now from tradeexecutor.ethereum.uniswap_v3.uniswap_v3_live_pricing import UniswapV3LivePricing from tradeexecutor.ethereum.eth_valuation import EthereumPoolRevaluator from tradeexecutor.ethereum.aave_v3.aave_v3_routing import AaveV3Routing assert routing_id.router_name == "aave-v3" assert routing_id.lending_protocol_slug == "aave_v3" assert len(strategy_universe.data_universe.chains) == 1 assert len(strategy_universe.reserve_assets) == 1 chain_id = strategy_universe.get_single_chain() chain_slug = chain_id.get_slug() reserve_asset = strategy_universe.get_reserve_asset() assert chain_slug in AAVE_V3_DEPLOYMENTS, f"Chain {chain_slug} not supported for Aave v3" aave_v3_deployment = fetch_aave_deployment( web3, pool_address=AAVE_V3_DEPLOYMENTS[chain_slug]["pool"], data_provider_address=AAVE_V3_DEPLOYMENTS[chain_slug]["data_provider"], oracle_address=AAVE_V3_DEPLOYMENTS[chain_slug]["oracle"], ) address_map = { "aave_v3_pool": aave_v3_deployment.pool.address, "aave_v3_data_provider": aave_v3_deployment.data_provider.address, "aave_v3_oracle": aave_v3_deployment.oracle.address, } routing_model = AaveV3Routing( address_map=address_map, chain_id=chain_id, reserve_token_address=reserve_asset.address, allowed_intermediary_pairs={}, ) pricing_model = UniswapV3LivePricing( web3, strategy_universe.data_universe.pairs, routing_model, ) valuation_model = EthereumPoolRevaluator(pricing_model) return ProtocolRoutingConfig( routing_id=routing_id, routing_model=routing_model, pricing_model=pricing_model, valuation_model=valuation_model, )
[docs]class EthereumPairConfigurator(PairConfigurator): """Set up routes for EVM trading pairs. Supported protocols - 1delta - Uniswap v2 likes - Uniswap v3 likes - Aave v3 """
[docs] def __init__( self, web3: Web3, strategy_universe: TradingStrategyUniverse, ): """Initialise pair configuration. :param web3: Web3 connection to the active blockchain. :param strategy_universe: The initial strategy universe. TODO: Currently only reserve currency, exchange and pair data is used. Candle data is discarded. """ assert isinstance(web3, Web3) assert isinstance(strategy_universe, TradingStrategyUniverse) self.web3 = web3 super().__init__(strategy_universe)
[docs] def get_supported_routers(self) -> Set[ProtocolRoutingId]: return default_supported_routers(self.strategy_universe)
[docs] def create_config(self, routing_id: ProtocolRoutingId) -> ProtocolRoutingConfig: if routing_id.router_name == "1delta": return create_1delta_adapter(self.web3, self.strategy_universe, routing_id) elif routing_id.router_name == "uniswap-v2": return create_uniswap_v2_adapter(self.web3, self.strategy_universe, routing_id) elif routing_id.router_name == "uniswap-v3": return create_uniswap_v3_adapter(self.web3, self.strategy_universe, routing_id) elif routing_id.router_name == "aave-v3": return create_aave_v3_adapter(self.web3, self.strategy_universe, routing_id) else: raise NotImplementedError(f"Cannot route exchange {routing_id}")
[docs] def match_router(self, pair: TradingPairIdentifier) -> ProtocolRoutingId: return default_match_router(self.strategy_universe, pair)