def calculate_liquidity_args(self, token_a_balance: Wad, token_b_balance: Wad) -> Optional[dict]: """ Returns dictionary containing arguments for addLiquidity transactions Calculate amount of both tokens, given the current reserve ratio on uniswap Use accepted_slippage to calculate min off of available balance + liquidity If eth is in the pair, at least 1 eth should be left for gas """ if self.is_eth: if self.eth_position == 0: token_a_balance = token_a_balance - Wad.from_number(1) if token_a_balance < Wad.from_number(0): self.logger.info(f"Insufficient Eth balance.") return elif self.eth_position == 1: token_b_balance = token_b_balance - Wad.from_number(1) if token_b_balance < Wad.from_number(0): self.logger.info(f"Insufficient Eth balance.") return token_a_desired = min(token_a_balance, token_b_balance / self.uniswap_current_exchange_price) token_a_min = token_a_desired - (token_a_desired * self.max_add_liquidity_slippage) token_b_desired = min(token_b_balance, token_a_desired * self.uniswap_current_exchange_price) token_b_min = token_b_desired - (token_b_desired * self.max_add_liquidity_slippage) add_liquidity_args = { 'amount_a_desired': self.token_a.unnormalize_amount(token_a_desired), 'amount_b_desired': self.token_b.unnormalize_amount(token_b_desired), 'amount_a_min': self.token_a.unnormalize_amount(token_a_min), 'amount_b_min': self.token_b.unnormalize_amount(token_b_min) } return add_liquidity_args
def from_message(trade, pair: str) -> Trade: return Trade(trade_id=trade['orderId'], timestamp=int(trade['timestamp']), pair=pair, is_sell=True if trade['type'] == 'ask' else False, price=Wad.from_number(float(trade["price"])), amount=Wad.from_number(float(trade['qty'])))
def test_past_take_with_filter(self): # when self.otc.approve([self.token1], directly()) self.otc.make(pay_token=self.token1.address, pay_amount=Wad.from_number(1), buy_token=self.token2.address, buy_amount=Wad.from_number(2)).transact() # and self.otc.approve([self.token2], directly()) self.otc.take(1, Wad.from_number(0.5)).transact() # then assert len( self.otc.past_take(PAST_BLOCKS, {'maker': self.our_address.address})) == 1 assert len( self.otc.past_take(PAST_BLOCKS, {'taker': self.our_address.address})) == 1 assert len( self.otc.past_take( PAST_BLOCKS, {'maker': '0x0101010101020202020203030303030404040404'})) == 0 assert len( self.otc.past_take( PAST_BLOCKS, {'taker': '0x0101010101020202020203030303030404040404'})) == 0
def test_approve_and_make_and_getters(self): if isinstance(self.otc, MatchingMarket): pay_val = self.token1_tokenclass buy_val = self.token2_tokenclass else: pay_val = self.token1.address buy_val = self.token2.address # given assert self.otc.get_last_order_id() == 0 # when self.otc.approve([self.token1], directly()) self.otc.make(pay_val, Wad.from_number(1), buy_val, Wad.from_number(2)).transact() # then assert self.otc.get_last_order_id() == 1 # and assert self.otc.get_order(1).order_id == 1 assert self.otc.get_order(1).pay_token == self.token1.address assert self.otc.get_order(1).pay_amount == Wad.from_number(1) assert self.otc.get_order(1).buy_token == self.token2.address assert self.otc.get_order(1).buy_amount == Wad.from_number(2) assert self.otc.get_order(1).maker == self.our_address assert self.otc.get_order(1).timestamp != 0 # and assert self.otc.get_orders() == [self.otc.get_order(1)]
def to_order(pair: str, item): return Order(order_id=item['orderid'], pair=pair, is_sell=True if item['type'] == 'sell-limit' else False, price=Wad.from_number(item['price']), amount=Wad.from_number(item['orderquantity']), filled_amount=Wad.from_number(item['filledquantity']))
def test_on_kill(self): # given on_kill_mock = Mock() self.otc.on_kill(on_kill_mock) # when self.otc.approve([self.token1], directly()) self.otc.make(pay_token=self.token1.address, pay_amount=Wad.from_number(1), buy_token=self.token2.address, buy_amount=Wad.from_number(2)).transact() # and self.otc.kill(1).transact() # then on_kill = wait_until_mock_called(on_kill_mock)[0] assert on_kill.order_id == 1 assert on_kill.maker == self.our_address assert on_kill.pay_token == self.token1.address assert on_kill.pay_amount == Wad.from_number(1) assert on_kill.buy_token == self.token2.address assert on_kill.buy_amount == Wad.from_number(2) assert on_kill.timestamp != 0 assert on_kill.raw['blockNumber'] > 0
def test_past_take_with_filter(self): if isinstance(self.otc, MatchingMarket): pay_val = self.token1_tokenclass buy_val = self.token2_tokenclass else: pay_val = self.token1.address buy_val = self.token2.address # when self.otc.approve([self.token1], directly()) self.otc.make(pay_val, Wad.from_number(1), buy_val, Wad.from_number(2)).transact() # and self.otc.approve([self.token2], directly()) self.otc.take(1, Wad.from_number(0.5)).transact() # then assert len( self.otc.past_take(PAST_BLOCKS, {'maker': self.our_address.address})) == 1 assert len( self.otc.past_take(PAST_BLOCKS, {'taker': self.our_address.address})) == 1 assert len( self.otc.past_take( PAST_BLOCKS, {'maker': '0x0101010101020202020203030303030404040404'})) == 0 assert len( self.otc.past_take( PAST_BLOCKS, {'taker': '0x0101010101020202020203030303030404040404'})) == 0
def to_order(item): return Order(order_id=item['id'], pair=item['currency_pair_code'], is_sell=True if item['side'] == 'sell' else False, price=Wad.from_number(item['price']), amount=Wad.from_number(item['quantity']), filled_amount=Wad.from_number(item['filled_quantity']))
def from_trade(pair, trade): return BinanceUsTrade(trade_id=str(trade['id']), timestamp=trade['time'], pair=pair, is_sell=not trade['isBuyerMaker'], price=Wad.from_number(trade['price']), amount=Wad.from_number(trade['qty']))
def _convert_balance_to_wad(self, balance: dict, decimals: int) -> dict: wei_balance = float(balance['wei']) pending_balance = float(balance['pendingWei']) ## DyDx can have negative balances from native margin trading is_negative = False if wei_balance < 0: is_negative = True converted_balance = from_wei(abs(int(wei_balance)), 'ether') converted_pending_balance = from_wei(abs(int(pending_balance)), 'ether') if decimals == 6: converted_balance = from_wei(abs(int(wei_balance)), 'mwei') converted_pending_balance = from_wei(abs(int(pending_balance)), 'mwei') # reconvert Wad to negative value if balance is negative if is_negative == True: converted_balance = converted_balance * -1 # Handle the edge case where orders are filled but balance change is still pending if converted_balance > 0: balance['wad'] = Wad.from_number(converted_balance) - Wad.from_number(converted_pending_balance) else: balance['wad'] = Wad.from_number(converted_balance) + Wad.from_number(converted_pending_balance) return balance
def to_trade(pair, trade): return Trade(trade_id=str(trade['id']), timestamp=int(trade['created_at']), pair=pair, is_sell=trade['taker_side'] == 'buy', price=Wad.from_number(trade['price']), amount=Wad.from_number(trade['quantity']))
def from_list(item: dict, pair: str): return Order(order_id=item["id"], timestamp=item["timestamp"], pair=pair, is_sell=True if item["type"] == "ask" else False, price=Wad.from_number(item["price"]["value"]), amount=Wad.from_number(item["total"]["value"]))
def from_all_list(pair, trade): return Trade(trade_id=int(trade["tid"]), timestamp=trade["timestamp"], pair=pair, is_sell=True if trade["type"] == "sell" else False, price=Wad.from_number(trade["price"]), amount=Wad.from_number(trade["amount"]))
def place_liquidity(self): """ Main control function of Uniswap Keeper lifecycle. It will determine whether liquidity should be added, or removed and then create and submit transactions to the Uniswap Router Contract to update liquidity levels. """ exchange_token_a_balance = Wad.from_number(0) if self.uniswap.is_new_pool else self.uniswap.get_exchange_balance(self.token_a, self.uniswap.pair_address) exchange_token_b_balance = Wad.from_number(0) if self.uniswap.is_new_pool else self.uniswap.get_exchange_balance(self.token_b, self.uniswap.pair_address) self.logger.info(f"Exchange Contract {self.token_a.name} amount: {exchange_token_a_balance}; " f"Exchange Contract {self.token_b.name} amount: {exchange_token_b_balance}") add_liquidity, remove_liquidity = self.determine_liquidity_action() self.logger.info(f"Add Liquidity: {add_liquidity}; Remove Liquidity: {remove_liquidity}") if add_liquidity: receipt = self.add_liquidity() if receipt is not None: self.logger.info(f"Current liquidity tokens after adding {self.uniswap.get_current_liquidity()}") if remove_liquidity: receipt = self.remove_liquidity() if receipt is not None: self.logger.info(f"Current liquidity tokens after removing {self.uniswap.get_current_liquidity()}")
def from_all_list(pair, trade): return Trade(trade_id=None, timestamp=int(trade['date']), pair=pair, is_sell=True if trade['side'] == 'sell' else False, price=Wad.from_number(trade['price']), amount=Wad.from_number(trade['volume']))
def test_local_accounts_register_key(): # given # [that address is not recognized by ganache, this way we can be sure it's the local account being used for signing] web3 = Web3(HTTPProvider("http://localhost:8555")) web3.eth.defaultAccount = Address( '0x13314e21cd6d343ceb857073f3f6d9368919d1ef').address # and keyfile_path = pkg_resources.resource_filename( __name__, "accounts/4_0x13314e21cd6d343ceb857073f3f6d9368919d1ef.json") passfile_path = pkg_resources.resource_filename(__name__, "accounts/pass") register_key(web3, f"key_file={keyfile_path},pass_file={passfile_path}") # and # [as ganache does not know this address, we need to send some ETH to it first] eth_transfer(web3, Address(web3.eth.defaultAccount), Wad.from_number(100)) \ .transact(from_address=Address(web3.eth.accounts[0])) # when # [we deploy some test contract and mint some tokens] token = DSToken.deploy(web3, 'XYZ') token.mint(Wad.from_number(150000)).transact() # then # [these operations were successful] assert token.balance_of(Address( web3.eth.defaultAccount)) == Wad.from_number(150000)
def test_on_take_wih_filter(self): # given on_take_filter1_mock = Mock() on_take_filter2_mock = Mock() self.otc.on_take(on_take_filter1_mock, {'maker': self.our_address.address}) self.otc.on_take( on_take_filter2_mock, {'maker': '0x0101010101020202020201010101010303030303'}) # when self.otc.approve([self.token1], directly()) self.otc.make(pay_token=self.token1.address, pay_amount=Wad.from_number(1), buy_token=self.token2.address, buy_amount=Wad.from_number(2)).transact() # and self.otc.approve([self.token2], directly()) self.otc.take(1, Wad.from_number(0.5)).transact() # then assert len(wait_until_mock_called(on_take_filter1_mock)) == 1 # and time.sleep(2) assert not on_take_filter2_mock.called
def get_trades(self, pair: str, page_number: int = 1) -> List[Trade]: assert (isinstance(pair, str)) assert (isinstance(page_number, int)) result = self._http_post("/v0/trades", { 'market': pair, 'page': page_number, 'per_page': 100 })['trades'] result = filter(lambda item: item['state'] == 'confirmed', result) trades = list( map( lambda item: Trade( trade_id=int(item['id']), timestamp=int( dateutil.parser.parse(item['createdAt']).timestamp()), pair=pair, is_sell=item['type'] == 'sell', price=Wad.from_number(item['price']), amount=Wad.from_number(item['amount']), money=Wad.from_number(item['amount']) * Wad.from_number( item['price'])), result)) return sort_trades(trades)
def get_trades(self, pair: str, page_number: int = 1) -> List[Trade]: assert(isinstance(pair, str)) assert(isinstance(page_number, int)) per_page = 100 page_filter = f"page={page_number}&per_page={per_page}" result = self._http_get_signed(f"/v2/markets/{pair}/trades/mine?{page_filter}", {})['data'] totalPages = result['totalPages'] currentPage = result['currentPage'] self.logger.debug(f'totalPages={totalPages};currentPage={currentPage}') # Oldest trades are on first page trades = result['trades'] trades = list(filter(lambda item: item['status'] == 'successful', trades)) trades = list(map(lambda item: Trade(trade_id=item['transactionId'], timestamp=int(item['executedAt']/1000), pair=pair, is_sell= Address(item['buyer']) != Address(self.web3.eth.defaultAccount), price=Wad.from_number(item['price']), amount=Wad.from_number(item['amount']), createdAt=int(item['createdAt']/1000)), trades)) return sort_trades(trades)
def from_message(item: dict): return Order(order_id=item['oid'], timestamp=item['created_at'], pair=item['book'], is_sell=True if item['side'] == 'sell' else False, price=Wad.from_number(item['price']), amount=Wad.from_number(item['amount']))
def test_past_kill(self): if isinstance(self.otc, MatchingMarket): pay_val = self.token1_tokenclass buy_val = self.token2_tokenclass else: pay_val = self.token1.address buy_val = self.token2.address # when self.otc.approve([self.token1], directly()) self.otc.make(pay_val, Wad.from_number(1), buy_val, Wad.from_number(2)).transact() # and self.otc.kill(1).transact() # then past_kill = self.otc.past_kill(PAST_BLOCKS) assert len(past_kill) == 1 assert past_kill[0].order_id == 1 assert past_kill[0].maker == self.our_address assert past_kill[0].pay_token == self.token1.address assert past_kill[0].pay_amount == Wad.from_number(1) assert past_kill[0].buy_token == self.token2.address assert past_kill[0].buy_amount == Wad.from_number(2) assert past_kill[0].timestamp != 0 assert past_kill[0].raw['blockNumber'] > 0
def from_all_list(pair, trade): return Trade(trade_id=trade['trade_id'], timestamp=int(dateutil.parser.parse(trade['time']).timestamp()), pair=pair, is_sell=True if trade['side'] == 'sell' else False, price=Wad.from_number(trade['price']), amount=Wad.from_number(trade['size']))
def to_trade(pair: str, trade): return Trade(trade_id=trade['tradeId'], timestamp=int(float(trade['time'])) // 1000, pair=pair, is_sell=trade['take'] == 'sell', price=Wad.from_number(trade['price']), amount=Wad.from_number(trade['quantity']))
def get_trades(self, pair: str, **kwargs) -> List[Trade]: assert (isinstance(pair, str)) result = self._http_post("/v0/trades", {'market': pair}) result = filter(lambda item: item['completedAt'] is not None, result) trades = list( map( lambda item: Trade(trade_id=int(item['id']), timestamp=int( dateutil.parser.parse(item[ 'completedAt']).timestamp()), pair=pair, is_sell=item['type'] == 'sell', price=Wad.from_number(item['price']), amount=Wad.from_number(item['amount']), amount_symbol=item['baseToken'], money=Wad.from_number(item['amount']) * Wad. from_number(item['price']), money_symbol=item['quoteToken'], base_fee=Wad.from_number(item['baseFee']), trading_fee=Wad.from_number(item[ 'tradingFee'])), result)) trades = sort_trades(trades) trades = filter_trades(trades, **kwargs) return trades
def from_message(item: list, pair: str) -> Order: return Order(order_id=item['orderId'], timestamp=int(item['timestamp']), pair=pair, is_sell=True if item['type'] == 'ask' else False, price=Wad.from_number(float(item["price"])), amount=Wad.from_number(float(item['qty'])))
def test_get_orders(self): buy_amount_order1 = Wad.from_number(5.124988526145090209) pay_amount_order1 = Wad.from_number(5.024999999999999500) buy_amount_order2 = Wad.from_number(5.102550000000000000) pay_amount_order2 = Wad.from_number(5.000000000000000000) # given self.otc.make(p_token=self.token2_tokenclass, pay_amount=self.token2_tokenclass.unnormalize_amount( pay_amount_order1), b_token=self.token1_tokenclass, buy_amount=buy_amount_order1).transact() self.otc.make(p_token=self.token1_tokenclass, pay_amount=pay_amount_order2, b_token=self.token2_tokenclass, buy_amount=self.token2_tokenclass.unnormalize_amount( buy_amount_order2)).transact() # then assert self.otc.get_orders( self.token1_tokenclass, self.token2_tokenclass)[0].buy_amount == buy_amount_order2 assert self.token2_tokenclass.unnormalize_amount( self.otc.get_orders(self.token2_tokenclass, self.token1_tokenclass)[0].pay_amount ) == self.token2_tokenclass.unnormalize_amount(pay_amount_order1)
def test_get_orders_by_maker(self): # given maker1 = self.our_address maker2 = Address(self.web3.eth.accounts[1]) # and self.token1.transfer(maker2, Wad.from_number(500)).transact() # when self.otc.approve([self.token1], directly()) self.otc.make(pay_token=self.token1.address, pay_amount=Wad.from_number(1), buy_token=self.token2.address, buy_amount=Wad.from_number(2)).transact() # and self.web3.eth.defaultAccount = self.web3.eth.accounts[1] self.otc.approve([self.token1], directly()) self.otc.make(pay_token=self.token1.address, pay_amount=Wad.from_number(1), buy_token=self.token2.address, buy_amount=Wad.from_number(2)).transact() self.web3.eth.defaultAccount = self.web3.eth.accounts[0] # then assert len(self.otc.get_orders()) == 2 assert len(self.otc.get_orders_by_maker(maker1)) == 1 assert len(self.otc.get_orders_by_maker(maker2)) == 1 # and assert self.otc.get_orders_by_maker(maker1)[0].maker == maker1 assert self.otc.get_orders_by_maker(maker2)[0].maker == maker2
def from_our_list(pair, trade): return Trade(trade_id=trade['executionId'], timestamp=int(int(trade['eventTime']) / 1000000), pair=pair, is_sell=True if trade['side'] == 'sell' else False, price=Wad.from_number(trade['price']), amount=Wad.from_number(trade['quantity']))
def test_approve_and_make_and_getters(self): # given assert self.otc.get_last_order_id() == 0 # when self.otc.approve([self.token1], directly()) self.otc.make(pay_token=self.token1.address, pay_amount=Wad.from_number(1), buy_token=self.token2.address, buy_amount=Wad.from_number(2)).transact() # then assert self.otc.get_last_order_id() == 1 # and assert self.otc.get_order(1).order_id == 1 assert self.otc.get_order(1).pay_token == self.token1.address assert self.otc.get_order(1).pay_amount == Wad.from_number(1) assert self.otc.get_order(1).buy_token == self.token2.address assert self.otc.get_order(1).buy_amount == Wad.from_number(2) assert self.otc.get_order(1).maker == self.our_address assert self.otc.get_order(1).timestamp != 0 # and assert self.otc.get_orders() == [self.otc.get_order(1)]
def get_all_trades(self, pair: str, page_number: int = 1) -> List[Trade]: assert (isinstance(pair, str)) assert (isinstance(page_number, int)) per_page = 100 page_filter = f"page={page_number}&per_page={per_page}" result = self._http_get( f"/{self.version}/markets/{pair}/trades?{page_filter}", {})['data'] totalPages = result['totalPages'] currentPage = result['currentPage'] self.logger.debug(f'totalPages={totalPages};currentPage={currentPage}') # Oldest trades are on first page trades = result['trades'] trades = list( filter(lambda item: item['status'] == 'successful', trades)) return list( map( lambda item: Trade(trade_id=None, timestamp=int(item['executedAt'] / 1000), pair=pair, is_sell=None, price=Wad.from_number(item['price']), amount=Wad.from_number(item['amount']), createdAt=int(item['createdAt'] / 1000)), trades))