''' Market Maker Strategy Overview: Market Making Logic: - Places buy orders near recent lows, sell orders near recent highs - Uses dynamic range based on recent high-low spread - Only trades within configured percentage of recent range extremes Risk Management: - ATR-based volatility filtering (no trading if volatility too high) - Emergency position size limits with automatic closure - Time-based position limits (auto-close after time limit) - Stop losses with trailing functionality Entry Conditions: - BUY: When bid drops below (low + range_percentage) - SELL: When ask rises above (high - range_percentage) - Range calculated from recent N-period high/low - Filtered out during high volatility periods Exit Conditions: - Profit target: 0.4% gain from entry - Stop loss: 0.1% loss from entry - Time limit: 2 hours maximum hold time - Emergency size limit: $1100 maximum risk Technical Filters: - Uses 5-minute timeframe with 180-period lookback (15 hours) - ATR volatility filter prevents trading in choppy markets - Recent high/low trend analysis prevents bad entries WARNING: This strategy is complex and has not been fully backtested. Use only for educational purposes without proper testing. WARNING: Do not run without thorough backtesting and understanding! ''' import ccxt import pandas as pd import time from datetime import datetime, timedelta import schedule import warnings import os from dotenv import load_dotenv warnings.filterwarnings("ignore") 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', } }) # Configuration size = 4200 symbol = 'DYDXUSDT' perc_from_lh = 0.35 close_seconds = 60 * 47 # 47 minutes trade_pause_mins = 15 max_lh = 1250 timeframe = '5m' num_bars = 180 max_risk = 1100 sl_perc = 0.1 exit_perc = 0.004 max_tr = 550 quartile = 0.33 time_limit = 120 # minutes sleep_time = 30 params = {'timeInForce': 'GTC'} def get_bid_ask(symbol=symbol): orderbook = exchange.fetch_order_book(symbol) bid = orderbook['bids'][0][0] ask = orderbook['asks'][0][0] bid_vol = orderbook['bids'][0][1] ask_vol = orderbook['asks'][0][1] return ask, bid, ask_vol, bid_vol def get_open_positions(symbol=symbol): positions = exchange.fetch_positions([symbol]) for pos in positions: if pos['symbol'] == symbol and float(pos['contracts']) != 0: return { 'side': pos['side'], 'size': float(pos['contracts']), 'entry_price': float(pos['entryPrice']), 'is_long': pos['side'] == 'long', 'has_position': True } return { 'side': None, 'size': 0, 'entry_price': 0, 'is_long': None, 'has_position': False } def size_kill(symbol=symbol): position = get_open_positions(symbol) if position['has_position']: position_cost = abs(position['size'] * position['entry_price']) if position_cost > max_risk: print(f'EMERGENCY: Position size ${position_cost} exceeds max risk ${max_risk}') kill_switch(symbol) print('Emergency closure complete. Sleeping 72 hours...') time.sleep(260000) def kill_switch(symbol=symbol): print('KILL SWITCH ACTIVATED') while True: position = get_open_positions(symbol) if not position['has_position']: break exchange.cancel_all_orders(symbol) ask, bid, _, _ = get_bid_ask(symbol) if position['is_long']: exchange.create_limit_sell_order(symbol, position['size'], ask, params) print(f'SELL to close: {position["size"]} at {ask}') else: exchange.create_limit_buy_order(symbol, position['size'], bid, params) print(f'BUY to close: {position["size"]} at {bid}') time.sleep(30) def get_order_status(symbol=symbol): open_orders = exchange.fetch_open_orders(symbol) has_stop_loss = any(order['type'] == 'stop_market' for order in open_orders) has_close_order = any(order['type'] == 'limit' for order in open_orders) has_entry_order = len(open_orders) > 0 and not get_open_positions(symbol)['has_position'] return { 'has_both': has_stop_loss and has_close_order, 'needs_stop_loss': not has_stop_loss, 'needs_close_order': not has_close_order, 'has_entry_pending': has_entry_order } def calculate_atr(df, period=7): df['prev_close'] = df['close'].shift(1) df['high_low'] = abs(df['high'] - df['low']) df['high_prev_close'] = abs(df['high'] - df['prev_close']) df['low_prev_close'] = abs(df['low'] - df['prev_close']) df['tr'] = df[['high_low', 'high_prev_close', 'low_prev_close']].max(axis=1) df['atr'] = df['tr'].rolling(period).mean() return df def get_pnl(symbol=symbol): position = get_open_positions(symbol) if not position['has_position']: return "No position", 0, 0 ask, bid, _, _ = get_bid_ask(symbol) current_price = bid entry_price = position['entry_price'] if position['is_long']: price_diff = current_price - entry_price else: price_diff = entry_price - current_price try: pnl_percent = (price_diff / entry_price) * 100 except: pnl_percent = 0 pnl_text = f'PnL: {pnl_percent:.2f}%' print(pnl_text) return pnl_text, ask, bid def create_stop_order(symbol, reference_price, direction): position = get_open_positions(symbol) position_size = position['size'] if position['has_position'] else size if direction == 'SELL': # Stop for long position stop_price = reference_price * 1.001 exchange.create_order( symbol, 'stop_market', 'sell', position_size, None, params={'stopPrice': stop_price} ) print(f'Stop loss created: SELL at {stop_price}') elif direction == 'BUY': # Stop for short position stop_price = reference_price * 0.999 exchange.create_order( symbol, 'stop_market', 'buy', position_size, None, params={'stopPrice': stop_price} ) print(f'Stop loss created: BUY at {stop_price}') def bot(): print('\n---- MARKET MAKER ACTIVE ----\n') # Check PnL and get current prices pnl_info = get_pnl(symbol) ask = pnl_info[1] bid = pnl_info[2] # Emergency size check size_kill() # Get market data bars = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=num_bars) df = pd.DataFrame(bars[:-1], columns=['timestamp', 'open', 'high', 'low', 'close', 'volume']) df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms') # Calculate technical indicators df = calculate_atr(df) # Get range data low = df['low'].min() high = df['high'].max() range_size = high - low avg_price = (high + low) / 2 print(f'Range: {low:.2f} - {high:.2f} | Size: {range_size:.2f} | Avg: {avg_price:.2f}') # Check if range is too large (high volatility) if range_size > max_lh: print('RANGE TOO LARGE - No trading') kill_switch() return # Check recent trend (no new highs/lows in last 17 bars) recent_lows = df['low'].tail(17).tolist() no_trading = any(low >= recent_low for recent_low in recent_lows) if no_trading: print('Recent trend unfavorable - No trading') return # Get position and order status position = get_open_positions(symbol) order_status = get_order_status(symbol) # Calculate entry levels open_range = range_size * perc_from_lh sell_to_open_limit = high - open_range buy_to_open_limit = low + open_range print(f'Entry levels - Buy below: {buy_to_open_limit:.2f}, Sell above: {sell_to_open_limit:.2f}') # Calculate exit price for existing position if position['has_position']: if position['is_long']: exit_price = position['entry_price'] * (1 + exit_perc) else: exit_price = position['entry_price'] * (1 - exit_perc) # Check time limit for existing positions current_time = int(time.time()) # Main trading logic if not position['has_position'] and not order_status['has_entry_pending']: # Look for entry opportunities if ask > sell_to_open_limit: print('SELLING to open - Price above sell level') exchange.cancel_all_orders(symbol) # Create sell order sell_price = ask * 1.0009 exchange.create_limit_sell_order(symbol, size, sell_price, params) print(f'SELL order placed at {sell_price}') # Create stop loss create_stop_order(symbol, high, 'SELL') time.sleep(300) # 5 minute pause elif bid < buy_to_open_limit: print('BUYING to open - Price below buy level') exchange.cancel_all_orders(symbol) # Create buy order buy_price = bid * 0.9991 exchange.create_limit_buy_order(symbol, size, buy_price, params) print(f'BUY order placed at {buy_price}') # Create stop loss create_stop_order(symbol, low, 'BUY') time.sleep(300) # 5 minute pause else: print('Price in middle of range - No entry') elif position['has_position']: # Manage existing position if order_status['needs_close_order']: # Create close order at profit target if position['is_long']: exchange.create_limit_sell_order(symbol, position['size'], exit_price, params) print(f'Close order: SELL at {exit_price}') else: exchange.create_limit_buy_order(symbol, position['size'], exit_price, params) print(f'Close order: BUY at {exit_price}') if order_status['needs_stop_loss']: # Create missing stop loss if position['is_long']: create_stop_order(symbol, low, 'BUY') else: create_stop_order(symbol, high, 'SELL') else: print('All orders in place - Monitoring...') time.sleep(12) print('=' * 40) schedule.every(25).seconds.do(bot) while True: try: schedule.run_pending() time.sleep(15) except Exception as e: print(f'Bot error: {e}') print('Sleeping 75 seconds...') time.sleep(75)