OCO invalid parameters
14:37 24 Apr 2025

so I have tried all the possible combinations of parameters to use when trying to set the OCO parameters. However every time remove the aboveType/belowType, i am being sent a message that i need to include them OCO order failed: Mandatory parameter 'aboveType' was not sent, was empty/null, or malformed. πŸ” Full error response: {'code': -1102, 'msg': "Mandatory parameter 'aboveType' was not sent, was empty/null, or malformed."}

and when i include them, i receive this message: OCO order failed: Not all sent parameters were read; read '9' parameter(s) but was sent '13'. πŸ” Full error response: {'code': -1104, 'msg': "Not all sent parameters were read; read '9' parameter(s) but was sent '13'."}

This is my full code can anyone help me ? I am using the latest version of python and my libraries are included at the beginning of the code for more information. All up-to-date ! Obviously I am using Websockets to not abuse from the API.

from binance.client import Client
from binance.enums import *
from binance import ThreadedWebsocketManager
from decimal import Decimal, ROUND_DOWN
from binance.exceptions import BinanceAPIException
import os
import time
import threading
import requests
import asyncio

# Testnet API keys (get from https://testnet.binance.vision/)
API_KEY = ''
API_SECRET = ''

# Use TESTNET URLs
BASE_URL = 'https://testnet.binance.vision'

client = Client(API_KEY, API_SECRET, testnet=True)

client.API_URL = BASE_URL

print(client.API_URL)

symbol = 'BNBFDUSD'
quote_asset = 'FDUSD'
base_asset = 'BNB'

tick_size = step_size = min_qty = None
last_price = None
min_notional = None


def get_symbol_filters(symbol):
    global tick_size, step_size, min_qty, min_notional
    min_notional = None  # ensure default value
    url = f"{BASE_URL}/api/v3/exchangeInfo"
    response = requests.get(url)
    data = response.json()
    for s in data['symbols']:
        if s['symbol'] == symbol:
            for f in s['filters']:
                if f['filterType'] == 'PRICE_FILTER':
                    tick_size = Decimal(f['tickSize'])
                elif f['filterType'] == 'LOT_SIZE':
                    step_size = Decimal(f['stepSize'])
                    min_qty = Decimal(f['minQty'])
                elif f['filterType'] == 'MIN_NOTIONAL':
                    min_notional = Decimal(f['minNotional'])
            break

    print(f"Filters for {symbol}:")
    print(f"  Tick size:     {tick_size}")
    print(f"  Step size:     {step_size}")
    print(f"  Min qty:       {min_qty}")
    print(f"  Min notional:  {min_notional if min_notional else 'N/A'}")



def round_step(value, step):
    value = Decimal(value)
    precision = abs(step.normalize().as_tuple().exponent)
    return value.quantize(Decimal(f"1e-{precision}"), rounding=ROUND_DOWN)


def get_trade_quantity(price, spend_amount):
    qty = Decimal(spend_amount) / Decimal(price)
    qty = round_step(qty, step_size)

    notional = qty * Decimal(price)
    
    # Use min_notional from exchange filters if available, otherwise use $10 fallback
    effective_min_notional = min_notional if min_notional else Decimal('10')

    if notional < effective_min_notional:
        raise Exception(f"Trade notional {notional} is below min notional {effective_min_notional}")
    
    return max(qty, min_qty)



def handle_socket_message(msg):
    global last_price
    if msg['e'] == 'trade':
        last_price = float(msg['p'])
        print(f"Live price: {last_price}")

# Thread-safe wrapper for socket
def start_socket_in_thread():
    def run_socket():
        twm = ThreadedWebsocketManager(api_key=API_KEY, api_secret=API_SECRET)
        twm.start()
        twm.start_trade_socket(callback=handle_socket_message, symbol=symbol.lower())
        # Keep the socket alive
        twm.join()  # Keeps the thread running
    thread = threading.Thread(target=run_socket)
    thread.daemon = True
    thread.start()

async def wait_for_price():
    while last_price is None:
        await asyncio.sleep(0.5)

def place_oco_order(entry_price, quantity):
    tp_price = round(entry_price + 0.20, 2)
    stop_price = round(entry_price - 0.01, 2)
    stop_limit_price = round(stop_price - 0.01, 2)

    oco_params = {
        'symbol': symbol,
        'side': SIDE_SELL,
        'quantity': str(quantity),
        'price': str(tp_price),               # Take profit
        'stopPrice': str(stop_price),         # Trigger
        'stopLimitPrice': str(stop_limit_price),  # Limit price after stop
        'stopLimitTimeInForce': TIME_IN_FORCE_GTC
    }

    import json
    print("\nπŸ“€ Sending OCO order payload:")
    print(json.dumps(oco_params, indent=2))

    try:
        order = client.create_oco_order(**oco_params)
        print(f"βœ… OCO order placed:\n{json.dumps(order, indent=2)}")
    except BinanceAPIException as e:
        print(f"\n❌ OCO order failed: {e.message}")
        if hasattr(e, 'response'):
            try:
                print("πŸ” Full error response:")
                print(e.response.json())
            except Exception as ex:
                print("Failed to parse full error JSON:", str(ex))
        else:
            print("No additional error response available.")


def market_buy(qty):
    order = client.order_market_buy(symbol=symbol, quantity=str(qty))
    print("Buy filled:", order)
    return order

async def main():
    get_symbol_filters(symbol)

    balance = client.get_asset_balance(asset=quote_asset)
    print(f"Current balance: {balance['free']} {quote_asset}")
    spend = Decimal(input(f"How much {quote_asset} per trade (Max {balance['free']})? "))

    # Start WebSocket in background thread
    start_socket_in_thread()

    await wait_for_price()

    qty = get_trade_quantity(last_price, spend)
    print(f"Placing buy at {last_price}, Qty: {qty}")
    market_buy(qty)

    await asyncio.sleep(2)
    place_oco_order(last_price, qty)

    await asyncio.sleep(5)

if __name__ == '__main__':
    asyncio.run(main())

Can anyone help identify the solution ?

I tried to remove each parameter one by one but all of them are needed. I remember reading in some posts (i couldn't find the link again to show it to you), that aboveType/belowType are used in futures only. This is a python program for BNBFDUSD trading only on spot !!! I do not intend to take it to futures.

I escaped many issues using this post but i am stuck with the fictional 4 parameters that appear out of nowhere (unless i misunderstood something).

text

python-3.x binance