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,