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.
|
|
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
import datetime as dt
import time
import pytz
import pandas as pd
import numpy as np
from binance.client import Client
'YOUR API KEY' y 'YOUR API SECRET' con sus credenciales reales de la API:
API_KEY = 'YOUR_API_KEY'
API_SECRET = 'YOUR_API _SECRET'
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
get_balance(balances, asset): Esta función obtiene el saldo de un activo específico en el balance de la cuenta.
def get_balance(balances, asset):
for bal in balances:
if bal.get('asset') == asset:
return float(bal['free'])
get_max_float_qty(step_size): Calcula la cantidad máxima de decimales con que se puede operar el activo.
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
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.
def get_quantity(price, amount, min_qty, max_qty, max_float_qty):
quantity = amount / price
if (quantity max_qty):
return False
quantity = np.round(quantity, max_float_qty)
return quantity
crossover(ma_fast, ma_slow): Esta función comprueba si se ha producido un cruce entre las medias móviles rápida y lenta.
def get_quantity(price, amount, min_qty, max_qty, max_float_qty):
quantity = amount / price
if (quantity max_qty):
return False
quantity = np.round(quantity, max_float_qty)
return quantity
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')
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
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
if name == "main":. Vamos a desglosar los pasos:
pd.options.mode.chained_assignment = None
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)
parser():
df = parser()
synchronize() para garantizar que el bot se inicia en el momento correcto:
step = int(period[:2]) if len(period) > 2 else int(period[0])
if 'h' in period:
step = step * 60
synchronize()
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:
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.
|
|
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
import datetime as dt
import time
import pytz
import pandas as pd
import numpy as np
from binance.client import Client
'YOUR API KEY' and 'YOUR API SECRET' with your actual API credentials:
API_KEY = 'YOUR_API_KEY'
API_SECRET = 'YOUR_API _SECRET'
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
get_balance(balances, asset): This function retrieves the balance of a specific asset from the account's balances.
def get_balance(balances, asset):
for bal in balances:
if bal.get('asset') == asset:
return float(bal['free'])
get_max_float_qty(step_size): It calculates the maximum floating quantity based on the step size.
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
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.
def get_quantity(price, amount, min_qty, max_qty, max_float_qty):
quantity = amount / price
if (quantity max_qty):
return False
quantity = np.round(quantity, max_float_qty)
return quantity
crossover(ma_fast, ma_slow): This function checks if a crossover event has occurred between the fast and slow moving averages.
def get_quantity(price, amount, min_qty, max_qty, max_float_qty):
quantity = amount / price
if (quantity max_qty):
return False
quantity = np.round(quantity, max_float_qty)
return quantity
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')
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
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
if name == "main": block. Let's break down the steps:
pd.options.mode.chained_assignment = None
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)
parser() function:
df = parser()
synchronize() function is called to ensure the bot starts at the correct time:
step = int(period[:2]) if len(period) > 2 else int(period[0])
if 'h' in period:
step = step * 60
synchronize()
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:
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