Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when placing a simple MARKET order #200

Open
Izem0 opened this issue Feb 28, 2023 · 8 comments
Open

Error when placing a simple MARKET order #200

Izem0 opened this issue Feb 28, 2023 · 8 comments

Comments

@Izem0
Copy link

Izem0 commented Feb 28, 2023

Using this code:

account_response = client.private.get_account()
position_id = account_response.data['account']['positionId']
market_data = client.public.get_markets(market=MARKET_MATIC_USD)
price = market_data.data['markets'][MARKET_MATIC_USD]['oraclePrice']
expiration_epoch_seconds = int((pd.Timestamp.utcnow() + pd.Timedelta(weeks=1)).timestamp())

placed_order = client.private.create_order(
  position_id=position_id, # required for creating the order signature
  market=MARKET_MATIC_USD,
  side=ORDER_SIDE_SELL,
  order_type=ORDER_TYPE_MARKET,
  post_only=False,
  size='10',
  price=str(price),
  limit_fee='1',
  expiration_epoch_seconds=expiration_epoch_seconds,
  time_in_force=TIME_IN_FORCE_GTT,
)

I get this error: dydx3.errors.DydxApiError: DydxApiError(status_code=400, response={'errors': [{'msg': 'Order with timeInForce: GTT, triggerPrice: undefined or trailingPercent: undefined will not be treated as MARKET'}]})

When I try with time_in_force = 'FOK' or 'IOC', the order is placed but cancelled immediately.

Thanks

@jadch
Copy link

jadch commented Mar 12, 2023

I've been having the same issue with the node SDK 🙃

It's honestly hard to understand why a small market order sometimes fails to be filled, especially when there is obviously liquidity in the order books. It's also weird to have to pass a price when creating a market order.

Would love an explanation or to know how to stop this from happening.

@sirEven
Copy link

sirEven commented Mar 12, 2023

Have you guys tried placing these market orders with the best bid or best ask price from the orderbook? I have no experience doing it with oracle price - however I do my market orders with orderbook data and they normally go through. If they don't however bribing with a slightly better price (resubmitting with a price that is slightly worse for you) helps.
I think there is an example in the dydx3 github repo.

Edit:
Here is the example: https://github.com/dydxprotocol/dydx-v3-python/blob/master/examples/orders.py

@Izem0
Copy link
Author

Izem0 commented Mar 16, 2023

Just tried with different prices from the orderbook but the error remains.

The example order in the docs you provided uses a LIMIT order type, not a MARKET one. I have no issue when placing a LIMIT order, but I still have with a MARKET order.

@traderblakeq
Copy link

I am in the same boat, I cant figure this "MARKET" order out.

  • Can we please get some more explanation?, and some good examples or similar ?.

@traderblakeq
Copy link

traderblakeq commented Apr 5, 2023

This does work though: (But still I would like some more explanation on the logic)

    position_id = account_response.data['account']['positionId']
    market_data = private_client.public.get_markets(market=MARKET_BTC_USD)
    price = float(market_data.data['markets'][MARKET_BTC_USD]['oraclePrice'])
    expiration_epoch_seconds = int((pd.Timestamp.utcnow() + pd.Timedelta(weeks=1)).timestamp())
    
    order_params = {
        'position_id': position_id, 
        'market' : MARKET_BTC_USD,
        'side' : ORDER_SIDE_SELL,
        'order_type' : ORDER_TYPE_MARKET,
        'post_only': False,
        'size' : '0.01',
        'price' : str(round(price,0)),
        'limit_fee' : '0.015',
        'expiration_epoch_seconds' : expiration_epoch_seconds,
        'time_in_force' : TIME_IN_FORCE_IOC,
        }
    
    order_response = private_client.private.create_order(**order_params)
    order_id = order_response.data['order']['id']

@traderblakeq
Copy link

For other users to use if of any good: ( This is my take ):

def sell_market_order(symbol: str, size: float , percent_slippage=0.003):
    """
    Place a market sell order for a specified symbol and size, considering the specified percent slippage.

    Args:
        symbol (str): The trading pair symbol (e.g., 'BTC-USD', 'ETH-USD').
        size (float): The size of the asset to sell.
        percent_slippage (float, optional): The maximum allowed slippage percentage. Defaults to 0.003 (0.3%).

    Returns:
        str: The ID of the created sell order.

    """

    market_data = client.public.get_markets(market=symbol)
    price = float(market_data.data['markets'][symbol]['oraclePrice'])
    price = price * (1 - percent_slippage) # min allowed price incl slippage 
    expiration_epoch_seconds = int((pd.Timestamp.utcnow() + pd.Timedelta(weeks=1)).timestamp())
    
    order_params = {
        'position_id': position_id, 
        'market' : symbol,
        'side' : ORDER_SIDE_SELL,
        'order_type' : ORDER_TYPE_MARKET,
        'post_only': False,
        'size' : str(size),
        'price' : str(round(price,0)),
        'limit_fee' : '0.0015',
        'expiration_epoch_seconds' : expiration_epoch_seconds,
        'time_in_force' : TIME_IN_FORCE_IOC,
        }
    
    order_response = client.private.create_order(**order_params)
    order_id = order_response.data['order']['id']

    return order_id



def buy_market_order(symbol: str, size: float, percent_slippage=0.003):
    """
    Place a market buy order for a specified symbol and size, considering the specified percent slippage.

    Args:
        symbol (str): The trading pair symbol (e.g., 'BTC-USD', 'ETH-USD').
        size (float): The size of the asset to buy.
        percent_slippage (float, optional): The maximum allowed slippage percentage. Defaults to 0.003 (0.3%).

    Returns:
        str: The ID of the created buy order.

    """

    market_data = client.public.get_markets(market=symbol)
    price = float(market_data.data['markets'][symbol]['oraclePrice'])
    price = price * (1 + percent_slippage) # max allowed price incl slippage 
    expiration_epoch_seconds = int((pd.Timestamp.utcnow() + pd.Timedelta(weeks=1)).timestamp())
    
    order_params = {
        'position_id': position_id, 
        'market' : symbol,
        'side' : ORDER_SIDE_BUY,
        'order_type' : ORDER_TYPE_MARKET,
        'post_only': False,
        'size' : str(size),
        'price' : str(round(price,0)),
        'limit_fee' : '0.0015',
        'expiration_epoch_seconds' : expiration_epoch_seconds,
        'time_in_force' : TIME_IN_FORCE_IOC,
        }
    
    order_response = client.private.create_order(**order_params)
    order_id = order_response.data['order']['id']

    return order_id

@trimblomit
Copy link

any progress on this? i dunno why it needs to worry about price for a market order and I cant figure out why it will create an order and then not create it another time

@traderblakeq
Copy link

As it is a DEX you need Price variable to Control slippage. (DYDX matching will though always ensure you get the best price possible on the order book).

Here is some of my code, hope it helps.

loss]

def place_market_order(self, symbol: str, size: float, side: str, atr: float, percent_slippage=0.003) -> Optional[str]:
    """
    Place a market order for a specified symbol, size, and side, considering the specified percent slippage.

    Args:
        symbol (str): The trading pair symbol (e.g., 'BTC-USD', 'ETH-USD').
        size (float): The size of the asset to buy or sell.
        side (str): The order side ('BUY' or 'SELL').
        percent_slippage (float, optional): The maximum allowed slippage percentage. Defaults to 0.003 (0.3%).

    Returns:
        Optional[str]: The ID of the created order or None if an error occurred.

    """
    try:
        market_data = self.client.public.get_markets(market=symbol).data
        if market_data is None:
            raise ValueError(f"Market data not found for symbol: {symbol}")

        price = float(market_data['markets'][symbol]['indexPrice'])
        if price <= 0:
            raise ValueError(f"Invalid indexPrice: {price}")

        tick_size = float(market_data['markets'][symbol]['tickSize'])
        if tick_size <= 0:
            raise ValueError(f"Invalid tick size: {tick_size}")

        if side not in ['BUY', 'SELL']:
            raise ValueError("Invalid side value. Must be 'BUY' or 'SELL'.")

        # Adjust price based on slippage and side
        if side == 'BUY':
            price = price * (1 + percent_slippage) # max allowed price incl slippage
        elif side == 'SELL':
            price = price * (1 - percent_slippage) # min allowed price incl slippage

        # Normalize price to tick size
        price = round(price / tick_size) * tick_size
        decimals = abs((decimal.Decimal(market_data['markets'][symbol]['indexPrice']).as_tuple().exponent))
        expiration_epoch_seconds = int((pd.Timestamp.utcnow() + pd.Timedelta(weeks=1)).timestamp())
        order_ids = dict()


        order_params = {
            'position_id': self.position_id, 
            'market' : symbol,
            'side' : side,
            'order_type' : ORDER_TYPE_MARKET,
            'post_only': False,
            'size' : str(size),
            'price' : str(round(price, decimals)),
            'limit_fee' : '0.0015',
            'expiration_epoch_seconds' : expiration_epoch_seconds,
            'time_in_force' : TIME_IN_FORCE_IOC,
            }

        order_response = self.client.private.create_order(**order_params)
        order_id = order_response.data['order']['id']
        if not order_id:
           self.logger.error(f'place {side} market order for {symbol} did not go through')
           return
        
        order_ids['position'] = order_response.data['order']
        order_ids['position']['decimals'] = str(decimals)
        order_ids['position']['tickSize'] = str(tick_size)
       
        return order_ids

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

5 participants