Help plz with this Python script

Dear Binance Forum Community,

I am encountering an issue with a Python trading script designed for Binance Futures. The script connects successfully to Binance using API keys stored in a .env file and fetches data as expected. However, it fails to place orders due to a “Notional insufficient” error, despite having a balance of 200 USDT and using a 5x leverage.

Here is the last log generated by the script, showing the error:

VALORES ACTUALES
Precio Actual: 94915.1000
Banda Superior: 95079.0716
Banda Inferior: 94915.4084
SMA: 94997.2400
High: 94930.0000, Low: 94915.1000
!!!PRECIO BAJO BANDA DE BOLLINGER!!!
Condiciones para abrir LONG cumplidas. Precio Actual: 94915.1
Error al intentar abrir la orden: Notional insuficiente: 94.92 USDT. Mínimo requerido: 100 USDT.

The script dynamically adjusts the quantity to comply with Binance’s minimum notional requirements (100 USDT), but even after adjustments, the issue persists. I tested the API connection with a more basic script, which successfully places test and real orders, confirming that the API credentials are valid.

To help debug, I am sharing the entire script below. If anyone has a similar script or can offer advice on how to address this issue, I would greatly appreciate it.
Full Script:

import os
import math
import time
import pandas as pd
from binance.client import Client
from binance.exceptions import BinanceAPIException
from dotenv import load_dotenv
from colorama import init, Fore, Style

Inicializar colorama

init(autoreset=True)

Cargar claves API desde el archivo .env

load_dotenv()
API_KEY = os.getenv(‘BINANCE_API_KEY’)
API_SECRET = os.getenv(‘BINANCE_API_SECRET’)

Inicializar cliente de Binance

client = Client(API_KEY, API_SECRET)

Función para redondear valores según restricciones de Binance

def ajustar_precision(valor, paso):
return math.floor(valor / paso) * paso

Función para calcular cantidad ajustada para cumplir con notional mínimo

def ajustar_cantidad_minima(price, step_size, min_notional):
cantidad = ajustar_precision(min_notional / price, step_size)
return cantidad

Función para enviar órdenes

def enviar_orden(symbol, side, price, quantity, tick_size, step_size, min_notional):
“”"
Envía una orden al mercado después de validar restricciones.
“”"
# Ajustar precio y cantidad
price = ajustar_precision(price, tick_size)
quantity = ajustar_precision(quantity, step_size)
notional = price * quantity

# Ajustar la cantidad si el notional es insuficiente
if notional < min_notional:
    quantity = ajustar_cantidad_minima(price, step_size, min_notional)
    notional = price * quantity

if notional < min_notional:  # Asegurar que el notional cumple con el mínimo
    raise ValueError(f"Notional insuficiente: {notional:.2f} USDT. Mínimo requerido: {min_notional:.2f} USDT.")

print(f"Enviando orden {side}: {quantity} BTC a {price} USDT (Notional: {notional:.2f})")

# Intentar orden de prueba
try:
    client.futures_create_test_order(
        symbol=symbol,
        side=side,
        type='LIMIT',
        timeInForce='GTC',
        price=price,
        quantity=quantity
    )
    print(Fore.GREEN + "Orden de prueba exitosa.")
except BinanceAPIException as e:
    print(Fore.RED + f"Error en orden de prueba: {e}")
    return None

# Enviar orden real
try:
    order = client.futures_create_order(
        symbol=symbol,
        side=side,
        type='LIMIT',
        timeInForce='GTC',
        price=price,
        quantity=quantity
    )
    print(Fore.GREEN + "Orden colocada exitosamente.")
    return order
except BinanceAPIException as e:
    print(Fore.RED + f"Error al colocar la orden: {e}")
    return None

Obtener restricciones del mercado

def obtener_restricciones(symbol):
market_info = client.futures_exchange_info()
for item in market_info[‘symbols’]:
if item[‘symbol’] == symbol:
price_filter = next(f for f in item[‘filters’] if f[‘filterType’] == ‘PRICE_FILTER’)
lot_size = next(f for f in item[‘filters’] if f[‘filterType’] == ‘LOT_SIZE’)
min_notional = next((float(f[‘minNotional’]) for f in item[‘filters’] if ‘minNotional’ in f), 0.0)
return {
‘tickSize’: float(price_filter[‘tickSize’]),
‘stepSize’: float(lot_size[‘stepSize’]),
‘minNotional’: min_notional
}
raise ValueError(f"No se encontraron restricciones para el símbolo: {symbol}")

Calcular Bandas de Bollinger

def calcular_bollinger(data, window=20, num_std=2):
data[‘SMA’] = data[‘close’].rolling(window=window).mean()
data[‘STD’] = data[‘close’].rolling(window=window).std()
data[‘Upper’] = data[‘SMA’] + num_std * data[‘STD’]
data[‘Lower’] = data[‘SMA’] - num_std * data[‘STD’]
return data

def obtener_datos_klines(symbol, interval, limit=100):
klines = client.futures_klines(symbol=symbol, interval=interval, limit=limit)
df = pd.DataFrame(klines, columns=[
‘open_time’, ‘open’, ‘high’, ‘low’, ‘close’, ‘volume’,
‘close_time’, ‘quote_asset_volume’, ‘number_of_trades’,
‘taker_buy_base_asset_volume’, ‘taker_buy_quote_asset_volume’, ‘ignore’
])
df[‘close’] = df[‘close’].astype(float)
df[‘high’] = df[‘high’].astype(float)
df[‘low’] = df[‘low’].astype(float)
return df

def main():
print(“=== SISTEMA DE TRADING BINANCE FUTURES ===”)

# Configuración inicial
stop_loss = float(input("Ingresa el Stop Loss en porcentaje (ejemplo: 2 para 2%): "))
interval = input("Ingresa la temporalidad (ejemplo: 1m, 5m, 1h): ").strip()
leverage = int(input("Ingresa el apalancamiento (ejemplo: 10): "))
symbol = 'BTCUSDT'

try:
    # Configurar apalancamiento
    client.futures_change_leverage(symbol=symbol, leverage=leverage)
    print(Fore.GREEN + f"Apalancamiento configurado a {leverage}x")
except BinanceAPIException as e:
    print(Fore.RED + f"Error al configurar el apalancamiento: {e}")
    return

# Obtener restricciones del mercado
try:
    restricciones = obtener_restricciones(symbol)
    print(Fore.CYAN + f"Restricciones del mercado: {restricciones}")
except ValueError as e:
    print(Fore.RED + f"Error al obtener restricciones: {e}")
    return

# Solicitar porcentaje del saldo a utilizar
porcentaje_saldo = float(input("Ingresa el porcentaje del saldo que deseas utilizar (ejemplo: 50 para 50%): "))
if porcentaje_saldo <= 0 or porcentaje_saldo > 100:
    print(Fore.RED + "Error: El porcentaje debe estar entre 0 y 100.")
    return

# Obtener saldo disponible
try:
    account_info = client.futures_account()
    usdt_balance = float(account_info['availableBalance'])
    trade_size_usdt = usdt_balance * (porcentaje_saldo / 100)
    print(Fore.YELLOW + f"Saldo disponible en USDT: {usdt_balance:.2f}")
    print(Fore.CYAN + f"El script utilizará {trade_size_usdt:.2f} USDT en cada operación.")
except BinanceAPIException as e:
    print(Fore.RED + f"Error al obtener el saldo: {e}")
    return

posicion_abierta = None

while True:
    try:
        # Obtener datos de mercado
        df = obtener_datos_klines(symbol, interval)
        df = calcular_bollinger(df)
        ultimo = df.iloc[-1]
        precio_actual = float(ultimo['close'])
        banda_superior = float(ultimo['Upper'])
        banda_inferior = float(ultimo['Lower'])
        sma = float(ultimo['SMA'])
        high = float(ultimo['high'])
        low = float(ultimo['low'])

        # Imprimir log detallado
        print(f"\n{Fore.BLUE + Style.BRIGHT}VALORES ACTUALES")
        print(Fore.YELLOW + f"Precio Actual: {precio_actual:.4f}")
        print(Fore.CYAN + f"Banda Superior: {banda_superior:.4f}")
        print(Fore.CYAN + f"Banda Inferior: {banda_inferior:.4f}")
        print(Fore.MAGENTA + f"SMA: {sma:.4f}")
        print(Fore.GREEN + f"High: {high:.4f}, Low: {low:.4f}")

        # Mensaje de alerta si el precio alcanza una banda de Bollinger
        if precio_actual >= banda_superior:
            print(Fore.LIGHTRED_EX + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!***********************PRECIO SOBRE BANDA DE BOLLINGER***********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
        elif precio_actual <= banda_inferior:
            print(Fore.LIGHTRED_EX + "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!***********************PRECIO BAJO BANDA DE BOLLINGER***********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")

        # Verificar condiciones para abrir LONG
        if not posicion_abierta and precio_actual <= banda_inferior:
            print(Fore.BLUE + f"Condiciones para abrir LONG cumplidas. Precio Actual: {precio_actual}")
            cantidad_btc = ajustar_precision(trade_size_usdt / precio_actual, restricciones['stepSize'])

            try:
                orden = enviar_orden(
                    symbol=symbol,
                    side='BUY',
                    price=precio_actual,
                    quantity=cantidad_btc,
                    tick_size=restricciones['tickSize'],
                    step_size=restricciones['stepSize'],
                    min_notional=restricciones['minNotional']
                )
                if orden:
                    posicion_abierta = 'LONG'
                    print(Fore.GREEN + "Posición LONG abierta exitosamente.")
            except ValueError as e:
                print(Fore.RED + f"Error al intentar abrir la orden: {e}")

        # Verificar condiciones para cerrar la posición
        if posicion_abierta == 'LONG' and precio_actual >= banda_superior:
            print(Fore.BLUE + "Condiciones para cerrar LONG cumplidas.")
            # Lógica para cerrar posición (similar a abrir, pero con 'SELL')
            posicion_abierta = None

    except BinanceAPIException as e:
        print(Fore.RED + f"Error de Binance API: {e}")
    except Exception as e:
        print(Fore.RED + f"Error general: {e}")

if name == “main”:
main()

Key Details:

Environment: Python, binance library
API Connection: Verified working with another script
Account Balance: 200 USDT
Leverage Used: 5x
API Keys: Stored in a .env file for security

If you have a similar working script or any suggestions to improve the notional calculation logic, please share. Your input would be invaluable to solving this issue.

Thank you in advance for your help!

Best regards,