diff --git a/coinbase_advanced_trader/services/order_service.py b/coinbase_advanced_trader/services/order_service.py index 7a6f5ed..e3f5b5e 100644 --- a/coinbase_advanced_trader/services/order_service.py +++ b/coinbase_advanced_trader/services/order_service.py @@ -201,24 +201,43 @@ def _place_limit_order(self, product_id: str, fiat_amount: str, limit_price: Opt if side == OrderSide.BUY else self.rest_client.limit_order_gtc_sell) - order_response = order_func( - self._generate_client_order_id(), - product_id, - str(base_size), - str(adjusted_price) - ) - - order = Order( - id=order_response['success_response']['order_id'], - product_id=product_id, - side=side, - type=OrderType.LIMIT, - size=base_size, - price=adjusted_price - ) + try: + order_response = order_func( + self._generate_client_order_id(), + product_id, + str(base_size), + str(adjusted_price) + ) + + if not order_response['success']: + error_response = order_response.to_dict().get('error_response', {}) + error_message = error_response.get('message', 'Unknown error') + preview_failure_reason = error_response.get('preview_failure_reason', 'Unknown') + error_log = (f"Failed to place a limit order. " + f"Reason: {error_message}. " + f"Preview failure reason: {preview_failure_reason}") + logger.error(error_log) + raise Exception(error_log) + + order = Order( + id=order_response['success_response']['order_id'], + product_id=product_id, + side=side, + type=OrderType.LIMIT, + size=base_size, + price=adjusted_price + ) + self._log_order_result(order_response, product_id, base_size, adjusted_price, side) + return order - self._log_order_result(order_response, product_id, base_size, adjusted_price, side) - return order + except Exception as e: + error_message = str(e) + if "Invalid product_id" in error_message: + error_log = (f"Failed to place a limit order. " + f"Reason: {error_message}. " + f"Preview failure reason: Unknown") + logger.error(error_log) + raise def _log_order_result(self, order: Dict[str, Any], product_id: str, amount: Any, price: Any = None, side: OrderSide = None) -> None: """ diff --git a/coinbase_advanced_trader/tests/test_order_service.py b/coinbase_advanced_trader/tests/test_order_service.py index dd37f56..59b5d68 100644 --- a/coinbase_advanced_trader/tests/test_order_service.py +++ b/coinbase_advanced_trader/tests/test_order_service.py @@ -110,6 +110,40 @@ def test_fiat_limit_buy(self): self.assertEqual(order.size, expected_size) self.assertEqual(order.price, adjusted_price) + def test_fiat_limit_buy_error(self): + """Test the fiat_limit_buy with a response that raises an exception.""" + product_id = "BTC-USDC" + fiat_amount = "10" + + # Mock responses with correct format + mock_order_response = { + 'success': False, + 'error_response': { + 'error': 'INSUFFICIENT_FUND', + 'message': 'Insufficient balance in source account', + 'error_details': '', + 'preview_failure_reason': 'PREVIEW_INSUFFICIENT_FUND' + } + } + self.rest_client_mock.limit_order_gtc_buy.return_value = mock_order_response + + # Mock price service to return specific values + spot_price = Decimal('50000') + base_increment = Decimal('0.00000001') + quote_increment = Decimal('0.01') + price_multiplier = Decimal('0.9995') + + self.order_service.price_service.get_spot_price.return_value = spot_price + self.order_service.price_service.get_product_details.return_value = { + 'base_increment': str(base_increment), + 'quote_increment': str(quote_increment) + } + + with self.assertRaises(Exception) as context: + order = self.order_service.fiat_limit_buy(product_id, fiat_amount) + + self.assertEqual(str(context.exception), "Failed to place a limit order. Reason: Insufficient balance in source account. Preview failure reason: PREVIEW_INSUFFICIENT_FUND") + def test_fiat_limit_sell(self): """Test the fiat_limit_sell method.""" product_id = "BTC-USDC"