Building a Simple Trading Bot using Binance API and Python

in LeoFinance10 months ago (edited)


versión en español disponible aquí

En el mundo del comercio de criptomonedas, el uso de software de comercio automatizado puede ayudar a los comerciantes a identificar oportunidades potenciales de compra y venta en tiempo real y tomar posiciones sin la necesidad de estar frente a una computadora. En esta publicación, analizaremos el código para construir un bot de trading simple usando un par de promedios móviles. El bot utilizará la API de Binance para obtener datos de mercado en tiempo real y ejecutar operaciones basadas en estrategias predefinidas.

El objetivo real de la publicación no es crear un bot competitivo, sino crear un programa simple y fácil de entender que podamos escalar en el futuro, haciéndolo más complejo y eficiente.

Descargo de responsabilidad


La información proporcionada en esta publicación tiene fines educativos únicamente y no debe interpretarse como un consejo de inversión. El comercio automatizado conlleva un alto riesgo en el que se puede perder el capital invertido. Es importante hacer su propia investigación antes de tomar cualquier decisión de inversión.

Requisitos

  • Un dispositivo con python 3 (Windows, Linux, Mac, Termux, etc.)
  • Un editor de código (VScode, Atom, PyCharm, Vim, Emacs, etc.)
  • Una cuenta de Binance verificada.
  • Conocimiento básico de python (no es estrictamente necesario, pero te ayudará a comprender mejor el código).

Crear una API binance


Para crear una clave API de Binance, siga estos pasos:

  1. Inicie sesión en su cuenta de Binance.
  2. Haga clic en la pestaña "API Management".
  3. Haga clic en el botón "Create API".
  4. Dele un nombre a la nueva API.
  5. Seleccione los permisos que desea otorgar a su clave API.
  6. Haga clic en el botón "Save".
Una vez que haya creado una clave API, se le proporcionará una API KEY y una API SECRET. Estas son dos piezas muy importantes de información, así que asegúrese de mantenerlas a salvo. Las necesitará para conectarse a Binance y para ejecutar el bot de trading.

Configuración del entorno


Para empezar, asegúrate de que tienes Python instalado en tu máquina junto con las librerías necesarias: datetime, time, pytz, pandas, numpy y python-binance. Puedes instalar estas librerías usando pip, el instalador de paquetes de Python.


pip install pandas numpy python-binance pytz

Importar librerías


Comencemos nuestro script importando las librerías necesarias para el bot de trading:


import datetime as dt
import time
import pytz
import pandas as pd
import numpy as np
from binance.client import Client

API Key y Secret


Para acceder a la API de Binance, debes proporcionar la clave y el secreto de la API que creaste anteriormente en el sitio web de Binance. Una vez que los tengas, reemplaza las palabras 'YOUR API KEY' y 'YOUR API SECRET' con sus credenciales reales de la API:


API_KEY = 'YOUR_API_KEY'
API_SECRET = 'YOUR_API _SECRET'

Definición de variables y parámetros


A continuación, definimos varias variables y parámetros que se utilizarán en todo el código. Estos incluyen el símbolo de la criptomoneda, símbolo de referencia (por ejemplo, USDT), la cantidad de criptomoneda para el comercio, y el período de obtención de datos de velas:


crypto = 'HIVE'
ref = 'USDT'
symbol = crypto + ref
amount = 15
# Eligible periods for candlesticks 1m, 3m, 5m, 15m, 1h, etc.
period = '15m'
# Moving Average Periods
ema_f = 15
ema_s = 50

Funciones auxiliares


Anexamos varias funciones de ayuda que realizan tareas específicas:

  1. get_balance(balances, asset): Esta función obtiene el saldo de un activo específico en el balance de la cuenta.
  2. 
    def get_balance(balances, asset):
        for bal in balances:
            if bal.get('asset') == asset:
                return float(bal['free'])
    
  3. get_max_float_qty(step_size): Calcula la cantidad máxima de decimales con que se puede operar el activo.
  4. 
    def get_max_float_qty(step_size):
        max_float_qty = 0
        a = 10
        while step_size * a < 1:
          a = a*10**max_float_qty
          max_float_qty += 1
        return max_float_qty
    
  5. get_quantity(price, amount, min_qty, max_qty, max_float_qty): Esta función calcula la cantidad a tradear en función del precio, la inversion deseada y las restricciones de cantidad mínima/máxima del asset.
  6. 
    def get_quantity(price, amount, min_qty, max_qty, max_float_qty):
        quantity = amount / price
        if (quantity < min_qty or quantity > max_qty):
            return False
        quantity = np.round(quantity, max_float_qty)
        return quantity
  7. crossover(ma_fast, ma_slow): Esta función comprueba si se ha producido un cruce entre las medias móviles rápida y lenta.
  8. 
    def get_quantity(price, amount, min_qty, max_qty, max_float_qty):
        quantity = amount / price
        if (quantity < min_qty or quantity > max_qty):
            return False
        quantity = np.round(quantity, max_float_qty)
        return quantity
    

Sincronización


La función synchronize() es responsable de sincronizar las acciones del bot con la hora actual UTC. Obtiene los últimos datos de velas y calcula el siguiente tiempo de sincronización añadiendo el "step" especificado (en minutos) a la última marca de tiempo. La función entonces espera hasta que el tiempo actual exceda el tiempo de sincronización, asegurando que el bot opera de acuerdo a los intervalos de tiempo deseados.


def synchronize():
    candles = client.get_klines(symbol=symbol,interval=period,limit=1)
    timer = pd.to_datetime(float(candles[0][0]) * 1000000)
    start = timer + dt.timedelta(minutes=step)
    print('Synchronizing .....')
    while dt.datetime.now(dt.timezone.utc) < pytz.UTC.localize(start):
        time.sleep(1)
    time.sleep(2)
    print('Bot synchronized')

Análisis de datos


La función parser() se define para obtener y analizar datos de velas de la API de Binance. Utiliza el método client.get_klines() para recuperar los datos históricos del mercado para el símbolo y el intervalo de tiempo especificados. Los datos se convierten en un DataFrame de pandas para su posterior análisis. Las columnas innecesarias se eliminan, y las restantes se convierten a sus tipos de datos apropiados. La columna "hora" se convierte a un formato de fecha y hora, y los valores EMA se calculan utilizando la función ewm() de pandas. Finalmente, se añade una columna 'operation' al DataFrame y se inicializa con valores NaN. La función devuelve el DataFrame analizado.


def parser(limit=ema_s+1):
    candles = client.get_klines(symbol=symbol,interval=period,limit=limit)
    df = pd.DataFrame(candles)
    df = df.drop([6, 7, 8, 9, 10, 11], axis=1)
    df.columns = ['time', 'open', 'high', 'low', 'close', 'volume']
    df[['time', 'open', 'high', 'low', 'close', 'volume']] = df[['time', 'open', 'high', 'low', 'close', 'volume']].astype(float)
    df['time'] = pd.to_datetime(df['time'] * 1000000)
    df['ema_s'] = df['close'].ewm(span=ema_s).mean()
    df['ema_f'] = df['close'].ewm(span=ema_f).mean()
    df['operation'] = np.nan
    return df

Operaciones


La función operate() determina si se ejecuta una orden de compra o de venta en función de la estrategia de cruce de las EMA. Si la EMA rápida (ema_f) cruza por encima de la EMA lenta (ema_s), se ejecuta una orden de compra. Del mismo modo, si la EMA rápida cruza por debajo de la EMA lenta, se ejecuta una orden de venta. La función interactúa con la API de Binance utilizando los métodos como order_market_buy() y order_market_sell() para ejecutar las órdenes. La función devuelve el tipo de operación ('BUY' o 'SELL') realizada.


def operate(df):
    operation = ''
    price = df['close'].iloc[-1]
    if crossover(df.ema_f.values[-2:], df.ema_s.values[-2:]):
        quantity = get_quantity(price, amount, min_qty, max_qty, max_float_qty)
        balances = client.get_account()['balances']
        balance = get_balance(balances, ref)
        if not quantity:
            print('No Quantity available')
        elif balance <= amount:
            print(f'No {ref} to buy {crypto}')
        else:
            order = client.order_market_buy(
                symbol=symbol,
                quantity=quantity)
            print('BUY')
            operation = 'BUY'
    elif crossover(df.ema_s.values[-2:], df.ema_f.values[-2:]):
        quantity = get_quantity(price, amount, min_qty, max_qty, max_float_qty)
        balances = client.get_account()['balances']
        balance = get_balance(balances, crypto)
        if not quantity:
            print('No Quantity available')
        elif balance < quantity:
            print(f'No {crypto} to sell for {ref}')
        else:
            order = client.order_market_sell(
                symbol=symbol,
                quantity=quantity)
            print('SELL')
            operation = 'SELL'
    return operation

Ejecución principal


La lógica principal de trading se implementa en el bloque if name == "main":. Vamos a desglosar los pasos:

  1. Desactivar los mensajes de advertencia de pandas para la asignación encadenada:
  2. 
    pd.options.mode.chained_assignment = None
    
  3. Crear un objeto cliente de Binance y obtener la información del par que se va a operar:
  4. 
    client = Client(API_KEY, API_SECRET)
    info = client.get_symbol_info(symbol)
    min_qty = float(info['filters'][1].get('minQty'))
    step_size = float(info['filters'][1].get('stepSize'))
    max_qty = float(info['filters'][1].get('maxQty'))
    max_float_qty = get_max_float_qty(step_size)
    
  5. Se inicializa el marco de datos llamando a la función parser():
  6. 
    df = parser()
    
  7. La variable "step" se determina en función del intervalo de tiempo especificado, Se llama a la función synchronize() para garantizar que el bot se inicia en el momento correcto:
  8. 
    step = int(period[:2]) if len(period) > 2 else int(period[0])
    if 'h' in period:
        step = step * 60
    synchronize()
    
  9. El bucle principal comienza con un temporizador para medir el tiempo de ejecución. Vuelve a llamar a la función parser() para obtener los datos más recientes, asigna el tipo de operación a la última fila de la columna "operation" mediante la función operate() y añade la nueva fila al DataFrame existente. Si el DataFrame supera un determinado límite de tamaño, se recorta para mantener un conjunto de datos manejable. A continuación, el DataFrame se guarda en un archivo CSV con fines de registro. Por último, el bucle calcula el tiempo de retardo necesario para alcanzar el intervalo deseado y duerme durante el tiempo calculado:
  10. 
    while True:
        temp = time.time()
        data_df = parser()
        data_df['operation'].iloc[-1] = operate(data_df.tail(2))
        df = pd.concat([df, data_df.tail(1)], ignore_index=True)
        if df.shape[0] > 10000:
            df = df.tail(10000)
        df.to_csv(f'./{symbol}_{period}.csv')
        delay = time.time() - temp
        idle = 60 * step - delay
        print(f'idle = {idle} seconds')
        time.sleep(idle)
    

Hemos revisado paso a paso el código para construir un bot de trading de criptomonedas utilizando la API de Binance. El bot obtiene datos históricos del mercado, realiza análisis técnicos utilizando cruces EMA, y ejecuta órdenes de compra o venta basadas en condiciones predefinidas. Al entender este código, puedes avanzar en la construccion de tus propios bots de trading o personalizar el existente para adaptarlo a tus estrategias. Se puede mejorar aún más este código mediante la incorporación de indicadores técnicos adicionales, estrategias de gestión de riesgos y técnicas de backtesting. En el próximo post intentaré mejorar este bot añadiendo más funcionalidades.

Recuerda, los bots de trading implican riesgos financieros, y es esencial probar y validar a fondo tus estrategias antes de desplegarlas en entornos de trading en vivo.

Puedes consultar todo este código en mi GitHub y si tienes alguna pregunta o sugerencia no dudes en dejar un comentario.


referencias: pytz | python-binance

In the world of cryptocurrency trading, the use of automated trading software can help traders identify potential buying and selling opportunities in real time and take positions without the need to be in front of a computer. In this post, will walk you through the code to build a simple trading bot using a pair of moving averages. The bot will use the Binance API to get real-time market data and execute trades based on predefined strategies.

The real goal of the post is not to create a competitive bot, but rather to build a simple, easy to understand program that we can scale in the future, making it more complex and efficient.

Disclaimer


The information provided in this post is for educational purposes only and should not be construed as investment advice. Automated trading carries a high risk in which the invested capital can be lost. It is important to do your own research before making any investment decisions.

Prerequisites

  • A device with python 3 (Windows, Linux, Mac, Termux, etc.)
  • A code editor (VScode, Atom, PyCharm, Vim, Emacs, etc. )
  • A verified Binance account.
  • Basic python knowledge (not strictly necessary, but it will help you to understand the code better.)

Creating a binance API


To create a Binance API key, follow these steps:

  1. Log in to your Binance account.
  2. Click on the "API Management" tab.
  3. Click on the "Create API" button.
  4. Give your API key a name.
  5. Select the permissions that you want to grant to your API key.
  6. Click on the "Save" button.

Once you have created an API key, you will be given an API key and a secret. These are two very important pieces of information, so make sure that you keep them safe. You will need them to connect to Binance and to run your trading bot.

Setting Up the Environment


To get started, make sure you have Python installed on your machine along with the required libraries: datetime, time, pytz, pandas, numpy, and python-binance. You can install these libraries using pip, the Python package installer.


pip install pandas numpy python-binance pytz

Importing Libraries


Let's begin our script by importing the necessary libraries for the trading bot:


import datetime as dt
import time
import pytz
import pandas as pd
import numpy as np
from binance.client import Client

API Key and Secret


To access the Binance API, you need to provide the API key and secret that you created before from the Binance website. Once you have them, replace the placeholders 'YOUR API KEY' and 'YOUR API SECRET' with your actual API credentials:


API_KEY = 'YOUR_API_KEY'
API_SECRET = 'YOUR_API _SECRET'

Defining Variables and Parameters


Next, we define several variables and parameters that will be used throughout the code. These include the cryptocurrency symbol, reference symbol (e.g., USDT), the amount of cryptocurrency to trade, and the period for fetching candlestick data:


crypto = 'HIVE'
ref = 'USDT'
symbol = crypto + ref
amount = 15
# Eligible periods for candlesticks 1m, 3m, 5m, 15m, 1h, etc.
period = '15m'
# Moving Average Periods
ema_f = 15
ema_s = 50

Helper Functions


The code includes several helper functions that perform specific tasks:

  1. get_balance(balances, asset): This function retrieves the balance of a specific asset from the account's balances.
  2. 
    def get_balance(balances, asset):
        for bal in balances:
            if bal.get('asset') == asset:
                return float(bal['free'])
    
  3. get_max_float_qty(step_size): It calculates the maximum floating quantity based on the step size.
  4. 
    def get_max_float_qty(step_size):
        max_float_qty = 0
        a = 10
        while step_size * a < 1:
          a = a*10**max_float_qty
          max_float_qty += 1
        return max_float_qty
    
  5. get_quantity(price, amount, min_qty, max_qty, max_float_qty): This function calculates the quantity to trade based on the price, desired amount, and minimum/maximum quantity constraints.
  6. 
    def get_quantity(price, amount, min_qty, max_qty, max_float_qty):
        quantity = amount / price
        if (quantity < min_qty or quantity > max_qty):
            return False
        quantity = np.round(quantity, max_float_qty)
        return quantity
  7. crossover(ma_fast, ma_slow): This function checks if a crossover event has occurred between the fast and slow moving averages.
  8. 
    def get_quantity(price, amount, min_qty, max_qty, max_float_qty):
        quantity = amount / price
        if (quantity < min_qty or quantity > max_qty):
            return False
        quantity = np.round(quantity, max_float_qty)
        return quantity
    

Synchronization


The synchronize() function is responsible for synchronizing the bot's actions with the current time. It fetches the latest candlestick data and calculates the next synchronization time by adding the specified step (in minutes) to the last timestamp. The function then waits until the current time exceeds the synchronization time, ensuring the bot operates according to the desired time intervals.</


def synchronize():
    candles = client.get_klines(symbol=symbol,interval=period,limit=1)
    timer = pd.to_datetime(float(candles[0][0]) * 1000000)
    start = timer + dt.timedelta(minutes=step)
    print('Synchronizing .....')
    while dt.datetime.now(dt.timezone.utc) < pytz.UTC.localize(start):
        time.sleep(1)
    time.sleep(2)
    print('Bot synchronized')

Parsing Data


The parser() function is defined to fetch and parse candlestick data from the Binance API. It uses the client.get_klines() method to retrieve historical market data for the specified symbol and time interval. The data is then converted into a pandas DataFrame for further analysis. Unnecessary columns are dropped, and the remaining columns are converted to their appropriate data types. The 'time' column is converted to a datetime format, and EMA values are calculated using the ewm() function from pandas. Finally, an 'operation' column is added to the DataFrame and initialized with NaN values. The parsed DataFrame is returned by the function.


def parser(limit=ema_s+1):
    candles = client.get_klines(symbol=symbol,interval=period,limit=limit)
    df = pd.DataFrame(candles)
    df = df.drop([6, 7, 8, 9, 10, 11], axis=1)
    df.columns = ['time', 'open', 'high', 'low', 'close', 'volume']
    df[['time', 'open', 'high', 'low', 'close', 'volume']] = df[['time', 'open', 'high', 'low', 'close', 'volume']].astype(float)
    df['time'] = pd.to_datetime(df['time'] * 1000000)
    df['ema_s'] = df['close'].ewm(span=ema_s).mean()
    df['ema_f'] = df['close'].ewm(span=ema_f).mean()
    df['operation'] = np.nan
    return df

Trading Operations


The operate() function determines whether to execute a buy or sell order based on the EMA crossover strategy. If the fast EMA (ema_f) crosses above the slow EMA (ema_s), a buy order is executed. Similarly, if the fast EMA crosses below the slow EMA, a sell order is executed. The function interacts with the Binance API using methods like order_market_buy() and order_market_sell() to execute the orders. The function returns the operation type ('BUY' or 'SELL') performed.


def operate(df):
    operation = ''
    price = df['close'].iloc[-1]
    if crossover(df.ema_f.values[-2:], df.ema_s.values[-2:]):
        quantity = get_quantity(price, amount, min_qty, max_qty, max_float_qty)
        balances = client.get_account()['balances']
        balance = get_balance(balances, ref)
        if not quantity:
            print('No Quantity available')
        elif balance <= amount:
            print(f'No {ref} to buy {crypto}')
        else:
            order = client.order_market_buy(
                symbol=symbol,
                quantity=quantity)
            print('BUY')
            operation = 'BUY'
    elif crossover(df.ema_s.values[-2:], df.ema_f.values[-2:]):
        quantity = get_quantity(price, amount, min_qty, max_qty, max_float_qty)
        balances = client.get_account()['balances']
        balance = get_balance(balances, crypto)
        if not quantity:
            print('No Quantity available')
        elif balance < quantity:
            print(f'No {crypto} to sell for {ref}')
        else:
            order = client.order_market_sell(
                symbol=symbol,
                quantity=quantity)
            print('SELL')
            operation = 'SELL'
    return operation

Main Execution


The main trading logic is implemented in the if name == "main": block. Let's break down the steps:

  1. Disable the pandas warning messages for chained assignment:
  2. 
    pd.options.mode.chained_assignment = None
    
  3. Create a Binance client object and retrieve symbol information:
  4. 
    client = Client(API_KEY, API_SECRET)
    info = client.get_symbol_info(symbol)
    min_qty = float(info['filters'][1].get('minQty'))
    step_size = float(info['filters'][1].get('stepSize'))
    max_qty = float(info['filters'][1].get('maxQty'))
    max_float_qty = get_max_float_qty(step_size)
    
  5. Initialize the dataframe by calling the parser() function:
  6. 
    df = parser()
    
  7. The step variable is determined based on the specified time interval, The synchronize() function is called to ensure the bot starts at the correct time:
  8. 
    step = int(period[:2]) if len(period) > 2 else int(period[0])
    if 'h' in period:
        step = step * 60
    synchronize()
    
  9. The main loop starts with a timer to measure execution time. It calls the parser() function again to fetch the latest data, assigns the operation type to the last row of the 'operation' column using the operate() function, and appends the new row to the existing DataFrame. If the DataFrame exceeds a certain size limit, it is trimmed to maintain a manageable dataset. The DataFrame is then saved to a CSV file for record-keeping purposes. Finally, the loop calculates the time delay required to achieve the desired interval and sleeps for the calculated duration:
  10. 
    while True:
        temp = time.time()
        data_df = parser()
        data_df['operation'].iloc[-1] = operate(data_df.tail(2))
        df = pd.concat([df, data_df.tail(1)], ignore_index=True)
        if df.shape[0] > 10000:
            df = df.tail(10000)
        df.to_csv(f'./{symbol}_{period}.csv')
        delay = time.time() - temp
        idle = 60 * step - delay
        print(f'idle = {idle} seconds')
        time.sleep(idle)
    

    We explored the code for building a simple crypto trading bot using the Binance API. The code fetches historical market data, performs technical analysis using EMA crossovers, and executes buy or sell orders based on predefined conditions. By understanding this code, you can gain insights into building your own trading bots or customize the existing one to suit your trading strategies. You can further enhance this code by incorporating additional technical indicators, risk management strategies, and portfolio balancing techniques. In the next post I will try to improve this bot by adding more functionality.

    Remember, trading bots involve financial risks, and it's essential to thoroughly test and validate your strategies before deploying them in live trading environments.

    You can check all this code on my GitHub and if you have any questions or suggestions please feel free to leave a comment.

    references: pytz | python-binance

    Sort:  

    Wow! Keep up the great work 👍

    thank you, I will try my best 😀

    That was a great job, interesting how Binance distributes their API for free, I've seen some similar interfaces charging a fee on the process, nice tutorial my friend.

    thanks, Binance is great, let's hope they continue to be there for a long time.