def restore_tracking_states(self, saved_states: Dict[str, any]): self.logger().info("Restoring existing orders and positions to inflight tracker.") if saved_states.get("orders", False): self._in_flight_orders.update({ key: UniswapV3InFlightPosition.from_json(value) for key, value in saved_states["orders"].items() }) if saved_states.get("positions", False): self._in_flight_positions.update({ key: UniswapV3InFlightPosition.from_json(value) for key, value in saved_states["positions"].items() })
def test_range_position_removal(self): """ Test that positions are removed when profitability is reached. """ self.default_strategy._market_info.market._in_flight_positions[ "pos1"] = UniswapV3InFlightPosition(hb_id="pos1", token_id=1, trading_pair="ETH-USDT", fee_tier="MEDIUM", base_amount=Decimal("0"), quote_amount=Decimal("100"), lower_price=Decimal("90"), upper_price=Decimal("95")) self.default_strategy._market_info.market._in_flight_positions[ "pos1"].current_base_amount = Decimal("1") self.default_strategy._market_info.market._in_flight_positions[ "pos1"].current_quote_amount = Decimal("0") self.default_strategy._market_info.market._in_flight_positions[ "pos1"].unclaimed_base_amount = Decimal("1") self.default_strategy._market_info.market._in_flight_positions[ "pos1"].unclaimed_quote_amount = Decimal("100") self.default_strategy._market_info.market._in_flight_positions[ "pos1"].gas_price = Decimal("0") self.assertEqual( len(self.default_strategy._market_info.market._in_flight_positions ), 1) self.default_strategy.range_position_remover() self.assertEqual( len(self.default_strategy._market_info.market._in_flight_positions ), 0)
def test_range_calculation(self): """ Test that the overall range of all positions cover are calculated correctly. """ self.default_strategy._market_info.market._in_flight_positions[ "pos1"] = UniswapV3InFlightPosition(hb_id="pos1", token_id=1, trading_pair="ETH-USDT", fee_tier="MEDIUM", base_amount=Decimal("0"), quote_amount=Decimal("100"), lower_price=Decimal("90"), upper_price=Decimal("95")) self.default_strategy._market_info.market._in_flight_positions[ "pos2"] = UniswapV3InFlightPosition(hb_id="pos2", token_id=2, trading_pair="ETH-USDT", fee_tier="MEDIUM", base_amount=Decimal("0"), quote_amount=Decimal("100"), lower_price=Decimal("95"), upper_price=Decimal("100")) self.default_strategy._market_info.market._in_flight_positions[ "pos3"] = UniswapV3InFlightPosition(hb_id="pos3", token_id=3, trading_pair="ETH-USDT", fee_tier="MEDIUM", base_amount=Decimal("0"), quote_amount=Decimal("100"), lower_price=Decimal("100"), upper_price=Decimal("105")) self.default_strategy._market_info.market._in_flight_positions[ "pos4"] = UniswapV3InFlightPosition(hb_id="pos4", token_id=4, trading_pair="ETH-USDT", fee_tier="MEDIUM", base_amount=Decimal("0"), quote_amount=Decimal("100"), lower_price=Decimal("105"), upper_price=Decimal("110")) self.assertEqual( len(self.default_strategy._market_info.market._in_flight_positions ), 4) lower_bound, upper_bound = self.default_strategy.total_position_range() self.assertEqual(lower_bound, Decimal("90")) self.assertEqual(upper_bound, Decimal("110"))
def add_position(self, trading_pair: str, fee_tier: str, base_amount: Decimal, quote_amount: Decimal, lower_price: Decimal, upper_price: Decimal, token_id: int = 0): self._in_flight_positions["pos1"] = UniswapV3InFlightPosition( hb_id="pos1", token_id=token_id, trading_pair=trading_pair, fee_tier=fee_tier, base_amount=base_amount, quote_amount=quote_amount, lower_price=lower_price, upper_price=upper_price)
def start_tracking_position(self, hb_id: str, trading_pair: str, fee_tier: str, base_amount: Decimal, quote_amount: Decimal, lower_price: Decimal, upper_price: Decimal): """ Starts tracking a range position by simply adding it into _in_flight_positions dictionary. """ self._in_flight_positions[hb_id] = UniswapV3InFlightPosition( hb_id=hb_id, token_id=None, trading_pair=trading_pair, fee_tier=fee_tier, base_amount=base_amount, quote_amount=quote_amount, lower_price=lower_price, upper_price=upper_price, )
def test_in_range_sell(self): """ Test in_range_sell function. """ self.default_strategy.tick(1) self.assertFalse(self.default_strategy.in_range_sell()) self.default_strategy._market_info.market._in_flight_positions[ "pos1"] = UniswapV3InFlightPosition(hb_id="pos1", token_id=1, trading_pair="ETH-USDT", fee_tier="MEDIUM", base_amount=Decimal("0"), quote_amount=Decimal("100"), lower_price=Decimal("98"), upper_price=Decimal("101")) self.assertTrue(self.default_strategy.in_range_sell()) self.default_strategy._market_info.market._in_flight_positions = {}
def test_profitability_calculation(self): """ Test profitability calculation function works correctly """ pos = UniswapV3InFlightPosition(hb_id="pos1", token_id=1, trading_pair="HBOT-USDT", fee_tier="MEDIUM", base_amount=Decimal("0"), quote_amount=Decimal("100"), lower_price=Decimal("100"), upper_price=Decimal("101")) pos.current_base_amount = Decimal("1") pos.current_quote_amount = Decimal("0") pos.unclaimed_base_amount = Decimal("1") pos.unclaimed_quote_amount = Decimal("10") pos.gas_price = Decimal("5") result = self.default_strategy.calculate_profitability(pos) self.assertEqual(result["profitability"], (Decimal("110") - result["tx_fee"]) / Decimal("100"))
async def update_lp_order(self, update_result: Dict[str, any], tracked_pos: UniswapV3InFlightPosition): """ Unlike swap orders, lp orders only stop tracking when a remove position is detected. """ if update_result.get("confirmed", False): if update_result["receipt"].get("status", 0) == 1: transaction_results = await self._api_request("post", "eth/uniswap/v3/result", {"logs": json.dumps(update_result["receipt"]["logs"]), "pair": tracked_pos.trading_pair}) for result in transaction_results["info"]: if result["name"] == "IncreaseLiquidity" and tracked_pos.last_status == UniswapV3PositionStatus.PENDING_CREATE: token_id, amount0, amount1 = self.parse_liquidity_events(result["events"], transaction_results["baseDecimal"], transaction_results["quoteDecimal"]) tracked_pos.token_id = token_id tracked_pos.base_amount = amount0 tracked_pos.quote_amount = amount1 tracked_pos.last_status = UniswapV3PositionStatus.OPEN self.logger().info(f"Liquidity added for tokenID - {token_id}.") self.trigger_event(MarketEvent.RangePositionUpdated, RangePositionUpdatedEvent(self.current_timestamp, tracked_pos.hb_id, tracked_pos.last_tx_hash, tracked_pos.token_id, tracked_pos.base_amount, tracked_pos.quote_amount, tracked_pos.last_status.name )) self.trigger_event(MarketEvent.RangePositionCreated, RangePositionCreatedEvent(self.current_timestamp, tracked_pos.hb_id, tracked_pos.last_tx_hash, tracked_pos.token_id, tracked_pos.trading_pair, tracked_pos.fee_tier, tracked_pos.lower_price, tracked_pos.upper_price, tracked_pos.base_amount, tracked_pos.quote_amount, tracked_pos.last_status.name, tracked_pos.gas_price )) elif result["name"] == "DecreaseLiquidity" and tracked_pos.last_status == UniswapV3PositionStatus.PENDING_REMOVE: token_id, amount0, amount1 = self.parse_liquidity_events(result["events"], transaction_results["baseDecimal"], transaction_results["quoteDecimal"]) tracked_pos.token_id = token_id tracked_pos.last_status = UniswapV3PositionStatus.REMOVED self.logger().info(f"Liquidity decreased for tokenID - {token_id}.") self.trigger_event(MarketEvent.RangePositionUpdated, RangePositionUpdatedEvent(self.current_timestamp, tracked_pos.hb_id, tracked_pos.last_tx_hash, tracked_pos.token_id, tracked_pos.base_amount, tracked_pos.quote_amount, tracked_pos.last_status.name )) self.trigger_event(MarketEvent.RangePositionRemoved, RangePositionRemovedEvent(self.current_timestamp, tracked_pos.hb_id, tracked_pos.token_id)) self.stop_tracking_position(tracked_pos.hb_id) elif result["name"] == "Collect": pass # not sure how to handle this at the moment # token_id, amount0, amount1 = self.parse_liquidity_events(result["events"]) # tracked_order.update_exchange_order_id(token_id) # self.logger().info(f"Liquidity removed for tokenID - {token_id}.") else: self.logger().info( f"Error updating range position, token_id: {tracked_pos.token_id}, hb_id: {tracked_pos.hb_id}" ) self.trigger_event(MarketEvent.RangePositionFailure, RangePositionFailureEvent(self.current_timestamp, tracked_pos.hb_id)) self.stop_tracking_position(tracked_pos.hb_id) tracked_pos.last_status = UniswapV3PositionStatus.FAILED