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

300 lines
No EOL
9 KiB
Python

'''
SMA + Order Book Strategy Overview:
Direction Signal:
- Uses 20-day SMA on daily timeframe to determine market bias
- BUY when price > daily SMA, SELL when price < daily SMA
Entry Logic:
- Uses 15-minute SMA with offset levels for precise entries
- Places dual limit orders around 15m SMA (±0.1% to ±0.3%)
- Only enters when price aligns with daily SMA direction
Risk Management:
- Profit target: 8% gain
- Stop loss: 9% loss
- Order book volume analysis confirms exits
- Position size management with incremental entries
Volume Confirmation:
- Analyzes bid/ask volume ratio over 55 seconds
- Delays exits if volume ratio < 0.4 (insufficient confirmation)
WARNING: Do not run without thorough backtesting and understanding!
'''
import ccxt
import pandas as pd
import time, schedule
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',
}
})
symbol = 'BTCUSDT'
pos_size = 30
params = {'timeInForce': 'GTC'}
target = 8
max_loss = -9
vol_decimal = 0.4
def ask_bid():
orderbook = exchange.fetch_order_book(symbol)
bid = orderbook['bids'][0][0]
ask = orderbook['asks'][0][0]
return ask, bid
def daily_sma():
print('Calculating daily SMA...')
bars = exchange.fetch_ohlcv(symbol, timeframe='1d', limit=100)
df = pd.DataFrame(bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df['sma20_d'] = df['close'].rolling(20).mean()
_, bid = ask_bid()
df.loc[df['sma20_d'] > bid, 'sig'] = 'SELL'
df.loc[df['sma20_d'] < bid, 'sig'] = 'BUY'
return df
def f15_sma():
print('Calculating 15m SMA...')
bars = exchange.fetch_ohlcv(symbol, timeframe='15m', limit=100)
df = pd.DataFrame(bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df['sma20_15'] = df['close'].rolling(20).mean()
df['bp_1'] = df['sma20_15'] * 1.001
df['bp_2'] = df['sma20_15'] * 0.997
df['sp_1'] = df['sma20_15'] * 0.999
df['sp_2'] = df['sma20_15'] * 1.003
return df
def open_positions():
positions = exchange.fetch_positions([symbol])
for pos in positions:
if pos['symbol'] == symbol and float(pos['contracts']) != 0:
side = pos['side']
size = pos['contracts']
is_long = side == 'long'
return positions, True, size, is_long
return [], False, 0, None
def kill_switch():
print('Starting kill switch...')
while True:
_, has_position, position_size, is_long = open_positions()
if not has_position:
break
exchange.cancel_all_orders(symbol)
ask, bid = ask_bid()
position_size = float(position_size)
if is_long:
exchange.create_limit_sell_order(symbol, position_size, ask, params)
print(f'SELL to close: {position_size} {symbol} at {ask}')
else:
exchange.create_limit_buy_order(symbol, position_size, bid, params)
print(f'BUY to close: {position_size} {symbol} at {bid}')
time.sleep(30)
def sleep_on_close():
closed_orders = exchange.fetch_closed_orders(symbol)
for order in reversed(closed_orders):
if order['status'] == 'closed':
order_time = int(order['timestamp'] / 1000)
current_time = int(exchange.fetch_order_book(symbol)['timestamp'] / 1000)
time_since_trade = (current_time - order_time) / 60
if time_since_trade < 59:
print(f'Recent trade {time_since_trade:.1f}m ago, sleeping 60s...')
time.sleep(60)
else:
print(f'Last trade {time_since_trade:.1f}m ago, no sleep needed')
break
print('Sleep check complete')
def ob():
print('Analyzing order book volume...')
volume_data = []
for i in range(11):
orderbook = exchange.fetch_order_book(symbol)
bid_volume = sum([bid[1] for bid in orderbook['bids']])
ask_volume = sum([ask[1] for ask in orderbook['asks']])
volume_data.append({'bid_vol': bid_volume, 'ask_vol': ask_volume})
if i < 10:
time.sleep(5)
print(f'Sample {i+1}: Bid: {bid_volume}, Ask: {ask_volume}')
df = pd.DataFrame(volume_data)
total_bid_vol = df['bid_vol'].sum()
total_ask_vol = df['ask_vol'].sum()
print(f'Total volumes - Bid: {total_bid_vol}, Ask: {total_ask_vol}')
if total_bid_vol > total_ask_vol:
control_ratio = total_ask_vol / total_bid_vol
print(f'Bulls in control: {control_ratio:.3f}')
else:
control_ratio = total_bid_vol / total_ask_vol
print(f'Bears in control: {control_ratio:.3f}')
_, has_position, _, is_long = open_positions()
if has_position:
volume_under_threshold = control_ratio < vol_decimal
position_type = 'long' if is_long else 'short'
print(f'In {position_type} position. Volume under threshold: {volume_under_threshold}')
return volume_under_threshold
else:
print('Not in position')
return None
def pnl_close():
print('Checking PnL...')
positions = exchange.fetch_positions([symbol])
position = None
for pos in positions:
if pos['symbol'] == symbol and float(pos['contracts']) != 0:
position = pos
break
if position is None:
print('No position open')
return False, False, 0, None
side = position['side']
size = position['contracts']
entry_price = float(position['entryPrice'])
leverage = float(position['leverage'])
is_long = side == 'long'
_, current_price = ask_bid()
if is_long:
price_diff = current_price - entry_price
else:
price_diff = entry_price - current_price
try:
pnl_percent = ((price_diff / entry_price) * leverage) * 100
except:
pnl_percent = 0
print(f'PnL: {pnl_percent:.2f}% (Entry: {entry_price}, Current: {current_price})')
if pnl_percent > target:
print('Target hit! Checking volume...')
volume_under_threshold = ob()
if volume_under_threshold:
print(f'Volume too low, waiting 30s...')
time.sleep(30)
else:
print('Closing profitable position!')
kill_switch()
return True, True, size, is_long
elif pnl_percent <= max_loss:
print('Max loss hit! Closing position.')
kill_switch()
return True, True, size, is_long
elif pnl_percent > 0:
print('In profit but target not reached')
else:
print('In loss but within acceptable range')
# SMA stop loss check
if True:
df_15m = f15_sma()
sma_15m = df_15m.iloc[-1]['sma20_15']
stop_loss_level = sma_15m * 1.008
print(f'SMA stop level: {stop_loss_level}')
return False, True, size, is_long
def bot():
pnl_close()
sleep_on_close()
df_daily = daily_sma()
df_15m = f15_sma()
ask, bid = ask_bid()
signal = df_daily.iloc[-1]['sig']
open_size = pos_size / 2
_, in_position, current_size, _ = open_positions()
current_size = float(current_size) if current_size else 0
current_price = bid
sma_15m = df_15m.iloc[-1]['sma20_15']
if not in_position and current_size < pos_size:
exchange.cancel_all_orders(symbol)
if signal == 'BUY' and current_price > sma_15m:
print('Making BUY orders')
bp_1 = df_15m.iloc[-1]['bp_1']
bp_2 = df_15m.iloc[-1]['bp_2']
print(f'Buy prices: {bp_1}, {bp_2}')
exchange.create_limit_buy_order(symbol, open_size, bp_1, params)
exchange.create_limit_buy_order(symbol, open_size, bp_2, params)
print('Orders placed, sleeping 2 minutes...')
time.sleep(120)
elif signal == 'SELL' and current_price < sma_15m:
print('Making SELL orders')
sp_1 = df_15m.iloc[-1]['sp_1']
sp_2 = df_15m.iloc[-1]['sp_2']
print(f'Sell prices: {sp_1}, {sp_2}')
exchange.create_limit_sell_order(symbol, open_size, sp_1, params)
exchange.create_limit_sell_order(symbol, open_size, sp_2, params)
print('Orders placed, sleeping 2 minutes...')
time.sleep(120)
else:
print('Price not aligned with SMA, sleeping 10 minutes...')
time.sleep(600)
else:
print('Already in position or at size limit')
schedule.every(28).seconds.do(bot)
while True:
try:
schedule.run_pending()
except Exception as e:
print(f'ERROR: {e}. Sleeping 30 seconds...')
time.sleep(30)