def place_order(self, is_sell: bool, pay_token: Address, pay_amount: Wad, buy_token: Address, buy_amount: Wad, fee_address: Address, pair: str) -> Order: assert (isinstance(is_sell, bool)) assert (isinstance(pay_token, Address)) assert (isinstance(pay_amount, Wad)) assert (isinstance(buy_token, Address)) assert (isinstance(buy_amount, Wad)) assert (isinstance(fee_address, Address)) assert (isinstance(pair, str)) expiration = int((datetime.datetime.today() + datetime.timedelta(hours=6)).strftime("%s")) order = ZrxOrder(exchange=self.tethfinex, maker=Address(self.tethfinex.web3.eth.defaultAccount), taker=fee_address, maker_fee=Wad(0), taker_fee=Wad(0), pay_token=pay_token, pay_amount=(pay_amount / buy_amount) * buy_amount if is_sell else pay_amount, buy_token=buy_token, buy_amount=buy_amount if is_sell else (buy_amount / pay_amount) * pay_amount, salt=self.tethfinex.random_salt(), fee_recipient=fee_address, expiration=expiration, exchange_contract_address=self.tethfinex.address, ec_signature_r=None, ec_signature_s=None, ec_signature_v=None) signed_order = self.tethfinex.sign_order(order) data = { "type": 'EXCHANGE LIMIT', "symbol": f"t{pair}", "amount": str(buy_amount) if is_sell else str(f"-{pay_amount}"), "price": str(pay_amount / buy_amount) if is_sell else str(buy_amount / pay_amount), "meta": signed_order.to_json(), "protocol": '0x' } side = "SELL" if is_sell else "BUY" self.logger.info( f"Placing order ({side}, amount {data['amount']} of {pair}," f" price {data['price']})...") result = self._http_post("/trustless/v1/w/on", data) self.logger.info(f"Placed order #{result[0]}") return result[0]
def get_trades(self, pair: Pair, page_number: int = 1) -> List[Trade]: assert(isinstance(pair, Pair)) assert(isinstance(page_number, int)) assert(page_number == 1) orders = self._http_authenticated("GET", "/v0/user_history", {}) # filter orders by our pair orders = list(filter(lambda item: Address(item['baseTokenAddress']) == pair.sell_token and Address(item['quoteTokenAddress']) == pair.buy_token, orders)) trades = [] for order in orders: is_sell = order['side'] == 'sell' price = Wad.from_number(order['price']) events = order['timeline'] for fill in filter(lambda event: event['action'] == 'filled', events): amount = Wad(int(fill['amount'])) timestamp = int(fill['timestamp']) intent_id = fill['intentID'] is_settled = any(event['action'] == 'settled' and event['intentID'] == intent_id for event in events) if is_settled: trades.append(Trade(trade_id=order['orderHash'] + "_" + intent_id, timestamp=timestamp, pair=pair, is_sell=is_sell, price=price, amount=amount)) return sort_trades(trades)
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 place_order(self, pair: str, is_sell: bool, price: Wad, amount: Wad) -> str: assert (isinstance(pair, str)) assert (isinstance(is_sell, bool)) assert (isinstance(price, Wad)) assert (isinstance(amount, Wad)) side = "limit_buy" if is_sell is False else "limit_sell" currency = pair.split('-')[0] # Coinone krw price precision must be specified based upon a given range float_price = Wad.__float__(price) price_prec = self._calc_price_precision(float_price) price = round(round(float_price / price_prec, 0) * price_prec, 0) data = { "currency": currency, "price": str(price), "qty": str(round(Wad.__float__(amount), 2)) } self.logger.info( f"Placing order ({side}, amount {data['qty']} of {pair}," f" price {data['price']})...") response = self._http_authenticated_request("POST", f"/v2/order/{side}", data) order_id = "" if response['result'] == 'success': order_id = response['orderId'] self.logger.info(f"Placed order (#{order_id})") return order_id
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_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 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, 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 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 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 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 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 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 _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 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_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 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 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 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 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 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 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 cancel_order(self, order_id: str, pair: str, price: Wad, amount: Wad, is_sell: bool) -> bool: assert (isinstance(order_id, str)) assert (isinstance(pair, str)) assert (isinstance(is_sell, bool)) assert (isinstance(price, Wad)) assert (isinstance(amount, Wad)) self.logger.info(f"Cancelling order #{order_id}...") currency = pair.split('-')[0] is_ask = 1 if is_sell is True else 0 data = { "order_id": order_id, "currency": currency, "price": str(round(Wad.__float__(price), 2)), # quote token is always krw "qty": str(round(Wad.__float__(amount), 2)), "is_ask": is_ask } result = self._http_authenticated_request("POST", f"/v2/order/cancel", data) return True if result["result"] == "success" else False
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 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_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 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 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_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 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"]))