1
Fork 0
crypto_bot_training/Session_05/bots/Multi_Ticker_Mean_Reversion.py
2025-06-26 15:46:49 +02:00

261 lines
No EOL
8.6 KiB
Python

'''
Multi-Ticker Mean Reversion Strategy Overview:
Strategy Logic:
- Trades multiple cryptocurrencies using mean reversion around 15m SMA
- Uses 4h SMA for trend direction (only trade with the trend)
- BULLISH trend (4h): Only LONG when price drops below 15m SMA
- BEARISH trend (4h): Only SHORT when price rises above 15m SMA
- 5m confirmation: Requires bullish/bearish candle confirmation
Entry Conditions:
- LONG: 4h bullish + price < 15m SMA + 5m bullish confirmation
- SHORT: 4h bearish + price > 15m SMA + 5m bearish confirmation
- Entry at 0.8% deviation from 15m SMA
Exit Conditions:
- Profit target: 9% gain
- Stop loss: 8% loss
- Automatic order cancellation every 30 minutes
Risk Management:
- Position sizing based on account balance
- Multiple symbol exposure with individual PnL tracking
- Leverage: 10x (configurable)
WARNING: This strategy has not been backtested. Do not run live without proper testing.
'''
import ccxt
import pandas as pd
import numpy as np
from datetime import datetime
import time, schedule
import random
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv('BINANCE_API_KEY')
api_secret = os.getenv('BINANCE_SECRET_KEY')
exchange = ccxt.binance({
'apiKey': api_key,
'secret': api_secret,
'enableRateLimit': True,
'options': {
'defaultType': 'future',
}
})
pos_size = 30
target = 9
max_loss = -8
leverage = 10
timeframe = '15m'
limit = 97
sma = 20
params = {'timeInForce': 'GTC'}
# Popular crypto symbols for Binance futures
SYMBOLS = [
'BTCUSDT', 'ETHUSDT', 'ADAUSDT', 'DOTUSDT', 'LINKUSDT',
'LTCUSDT', 'BCHUSDT', 'XLMUSDT', 'EOSUSDT', 'TRXUSDT',
'ETCUSDT', 'DASHUSDT', 'XMRUSDT', 'ZECUSDT', 'XRPUSDT',
'BNBUSDT', 'SOLUSDT', 'AVAXUSDT', 'MATICUSDT', 'UNIUSDT',
'SUSHIUSDT', 'AAVEUSDT', 'COMPUSDT', 'MKRUSDT', 'YFIUSDT'
]
def ask_bid(symbol):
orderbook = exchange.fetch_order_book(symbol)
bid = orderbook['bids'][0][0]
ask = orderbook['asks'][0][0]
return ask, bid
def get_open_positions():
positions = exchange.fetch_positions()
active_positions = []
for pos in positions:
if pos['symbol'] in SYMBOLS and float(pos['contracts']) != 0:
active_positions.append({
'symbol': pos['symbol'],
'side': pos['side'],
'size': float(pos['contracts']),
'entry_price': float(pos['entryPrice']),
'is_long': pos['side'] == 'long'
})
return active_positions
def kill_switch(symbol):
print(f'Closing position for {symbol}')
positions = exchange.fetch_positions([symbol])
for pos in positions:
if pos['symbol'] == symbol and float(pos['contracts']) != 0:
size = float(pos['contracts'])
is_long = pos['side'] == 'long'
exchange.cancel_all_orders(symbol)
ask, bid = ask_bid(symbol)
if is_long:
exchange.create_limit_sell_order(symbol, size, ask, params)
print(f'SELL to close: {symbol}')
else:
exchange.create_limit_buy_order(symbol, size, bid, params)
print(f'BUY to close: {symbol}')
time.sleep(5)
break
def kill_switch_all():
print('Closing all positions...')
active_positions = get_open_positions()
for pos in active_positions:
kill_switch(pos['symbol'])
def get_sma_data(symbol, timeframe, limit, sma_period):
try:
bars = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=limit)
df = pd.DataFrame(bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df[f'sma{sma_period}_{timeframe}'] = df['close'].rolling(sma_period).mean()
# Add signal based on price vs SMA
_, bid = ask_bid(symbol)
df.loc[df[f'sma{sma_period}_{timeframe}'] > bid, 'sig'] = 'SELL'
df.loc[df[f'sma{sma_period}_{timeframe}'] < bid, 'sig'] = 'BUY'
# Add bullish confirmation (current close > previous close)
df['prev_close'] = df['close'].shift(1)
df['bullish_confirmation'] = df['close'] > df['prev_close']
return df
except Exception as e:
print(f'Error getting SMA data for {symbol}: {e}')
return pd.DataFrame()
def pnl_close(symbol):
positions = exchange.fetch_positions([symbol])
for pos in positions:
if pos['symbol'] == symbol and float(pos['contracts']) != 0:
entry_price = float(pos['entryPrice'])
leverage_used = float(pos['leverage'])
is_long = pos['side'] == 'long'
_, current_price = ask_bid(symbol)
if is_long:
price_diff = current_price - entry_price
else:
price_diff = entry_price - current_price
try:
pnl_percent = ((price_diff / entry_price) * leverage_used) * 100
except:
pnl_percent = 0
print(f'{symbol} PnL: {pnl_percent:.2f}%')
if pnl_percent >= target:
print(f'{symbol} hit profit target: {target}%')
kill_switch(symbol)
elif pnl_percent <= max_loss:
print(f'{symbol} hit stop loss: {max_loss}%')
kill_switch(symbol)
break
def get_current_time_info():
now = datetime.now()
comp24time = int(now.strftime('%H%M'))
return comp24time
def cancel_all_orders_scheduled():
comp24time = get_current_time_info()
# Cancel orders every 30 minutes
cancel_times = [0, 30, 100, 130, 200, 230, 300, 330, 400, 430, 500, 530,
600, 630, 700, 730, 800, 830, 900, 930, 1000, 1030, 1100,
1130, 1200, 1230, 1300, 1330, 1400, 1430, 1500, 1530, 1600,
1630, 1700, 1730, 1800, 1830, 1900, 1930, 2000, 2030, 2100,
2130, 2200, 2230, 2300, 2330]
if comp24time in cancel_times:
print('Cancelling all pending orders...')
for symbol in SYMBOLS:
try:
exchange.cancel_all_orders(symbol)
except:
pass
def bot():
# Check and close positions first
active_positions = get_open_positions()
for pos in active_positions:
pnl_close(pos['symbol'])
# Cancel orders if scheduled
cancel_all_orders_scheduled()
# Select random symbol to analyze
symbol = random.choice(SYMBOLS)
try:
# Set leverage
exchange.set_leverage(leverage, symbol)
# Get SMA data for different timeframes
df_4h = get_sma_data(symbol, '4h', 31, sma)
df_15m = get_sma_data(symbol, '15m', 97, sma)
df_5m = get_sma_data(symbol, '5m', 100, sma)
if df_4h.empty or df_15m.empty or df_5m.empty:
return
# Get current market data
ask, bid = ask_bid(symbol)
# Get signals
sig_4h = df_4h['sig'].iloc[-1]
sma_15m = df_15m[f'sma{sma}_15m'].iloc[-1]
bullish_confirmation_5m = df_5m['bullish_confirmation'].iloc[-1]
# Calculate entry levels
sma_minus_008 = sma_15m * 0.992 # 0.8% below SMA
sma_plus_008 = sma_15m * 1.008 # 0.8% above SMA
# Check if already in position
in_position = any(pos['symbol'] == symbol for pos in active_positions)
# Mean reversion strategy
if not in_position:
if sig_4h == 'BUY': # Bullish 4h trend
# Look for longs when price drops below 15m SMA
if bid <= sma_15m and bullish_confirmation_5m:
exchange.cancel_all_orders(symbol)
exchange.create_limit_buy_order(symbol, pos_size, sma_minus_008, params)
print(f'BUY to open: {symbol} at {sma_minus_008}')
elif sig_4h == 'SELL': # Bearish 4h trend
# Look for shorts when price rises above 15m SMA
if bid >= sma_15m and not bullish_confirmation_5m:
exchange.cancel_all_orders(symbol)
exchange.create_limit_sell_order(symbol, pos_size, sma_plus_008, params)
print(f'SELL to open: {symbol} at {sma_plus_008}')
except Exception as e:
print(f'Error processing {symbol}: {e}')
schedule.every(10).seconds.do(bot)
while True:
try:
schedule.run_pending()
except Exception as e:
print(f'Bot error: {e}. Sleeping 7 seconds...')
time.sleep(7)