''' 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)