Spaces:
Running
Running
Upload 36 files
Browse files- api_server.py +512 -0
- app.py +194 -0
- backtester/__pycache__/engine.cpython-312.pyc +0 -0
- backtester/engine.py +411 -0
- config/pairs.yaml +4 -0
- config/settings.yaml +17 -0
- core/__pycache__/data_engine.cpython-312.pyc +0 -0
- core/__pycache__/exchange.cpython-312.pyc +0 -0
- core/__pycache__/indicators.cpython-312.pyc +0 -0
- core/__pycache__/risk.cpython-312.pyc +0 -0
- core/__pycache__/strategy.cpython-312.pyc +0 -0
- core/__pycache__/trade_monitor.cpython-312.pyc +0 -0
- core/__pycache__/websockets.cpython-312.pyc +0 -0
- core/data_engine.py +241 -0
- core/exchange.py +186 -0
- core/indicators.py +254 -0
- core/risk.py +267 -0
- core/strategy.py +190 -0
- core/trade_monitor.py +232 -0
- core/websockets.py +274 -0
- logs/errors.json +272 -0
- logs/errors.log +241 -0
- logs/performance.jsonl +3 -0
- logs/scalper.log +512 -0
- logs/trades.log +0 -0
- main.py +143 -0
- requirements.txt +17 -0
- services/logger.py +226 -0
- services/telegram.py +21 -0
- test/MAINNET_STATUS.md +16 -0
- test/README.md +481 -0
- test/get_chat_id.py +124 -0
- test/huggingface_README.md +318 -0
- test/test_api.py +167 -0
- test/test_api_client.py +81 -0
- test/test_api_raw.py +176 -0
api_server.py
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, BackgroundTasks, HTTPException
|
| 2 |
+
from pydantic import BaseModel
|
| 3 |
+
import uvicorn
|
| 4 |
+
import asyncio
|
| 5 |
+
import threading
|
| 6 |
+
import time
|
| 7 |
+
from datetime import datetime, timedelta
|
| 8 |
+
from typing import Dict, List, Optional
|
| 9 |
+
import yaml
|
| 10 |
+
import os
|
| 11 |
+
from dotenv import load_dotenv
|
| 12 |
+
|
| 13 |
+
from core.exchange import BybitExchange
|
| 14 |
+
from core.strategy import ScalpingStrategy
|
| 15 |
+
from core.risk import RiskManager
|
| 16 |
+
from core.data_engine import DataEngine
|
| 17 |
+
from core.trade_monitor import TradeMonitor
|
| 18 |
+
from core.websockets import BybitWebSocket
|
| 19 |
+
from services.logger import log, log_trade, log_performance
|
| 20 |
+
from services.telegram import send_telegram
|
| 21 |
+
|
| 22 |
+
load_dotenv()
|
| 23 |
+
|
| 24 |
+
app = FastAPI(title="Bybit Scalping Bot API", version="1.0.0")
|
| 25 |
+
|
| 26 |
+
class TradeSession(BaseModel):
|
| 27 |
+
symbol: str
|
| 28 |
+
duration_hours: int = 18
|
| 29 |
+
max_trades: Optional[int] = None
|
| 30 |
+
|
| 31 |
+
class BotStatus(BaseModel):
|
| 32 |
+
is_running: bool
|
| 33 |
+
active_sessions: List[Dict]
|
| 34 |
+
total_pnl: float
|
| 35 |
+
trades_today: int
|
| 36 |
+
uptime: str
|
| 37 |
+
|
| 38 |
+
# Global bot state
|
| 39 |
+
bot_instances = {}
|
| 40 |
+
active_sessions = {}
|
| 41 |
+
session_reports = {}
|
| 42 |
+
|
| 43 |
+
def load_config():
|
| 44 |
+
settings = yaml.safe_load(open("config/settings.yaml"))
|
| 45 |
+
pairs = yaml.safe_load(open("config/pairs.yaml"))["pairs"]
|
| 46 |
+
return settings, pairs
|
| 47 |
+
|
| 48 |
+
def create_bot_instance(symbol: str):
|
| 49 |
+
"""Create a dedicated bot instance for a symbol"""
|
| 50 |
+
settings, pairs = load_config()
|
| 51 |
+
|
| 52 |
+
exchange = BybitExchange()
|
| 53 |
+
data_engine = DataEngine()
|
| 54 |
+
strategy = ScalpingStrategy(data_engine)
|
| 55 |
+
risk_manager = RiskManager(exchange)
|
| 56 |
+
monitor = TradeMonitor(exchange, strategy, risk_manager, data_engine)
|
| 57 |
+
ws_client = BybitWebSocket(callback=lambda data: handle_ws_data(data, symbol))
|
| 58 |
+
|
| 59 |
+
return {
|
| 60 |
+
'exchange': exchange,
|
| 61 |
+
'data_engine': data_engine,
|
| 62 |
+
'strategy': strategy,
|
| 63 |
+
'risk_manager': risk_manager,
|
| 64 |
+
'monitor': monitor,
|
| 65 |
+
'ws_client': ws_client,
|
| 66 |
+
'symbol': symbol,
|
| 67 |
+
'start_time': datetime.now(),
|
| 68 |
+
'trades': [],
|
| 69 |
+
'pnl': 0.0
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
def handle_ws_data(data, symbol):
|
| 73 |
+
"""Handle WebSocket data for specific symbol"""
|
| 74 |
+
if symbol in bot_instances:
|
| 75 |
+
bot = bot_instances[symbol]
|
| 76 |
+
if "topic" in data:
|
| 77 |
+
topic = data["topic"]
|
| 78 |
+
payload = data["data"]
|
| 79 |
+
|
| 80 |
+
if topic.startswith("tickers."):
|
| 81 |
+
ticker_symbol = topic.split(".")[1]
|
| 82 |
+
if ticker_symbol == symbol:
|
| 83 |
+
bot['data_engine'].update_price(symbol, float(payload["lastPrice"]))
|
| 84 |
+
|
| 85 |
+
elif topic.startswith("kline."):
|
| 86 |
+
parts = topic.split(".")
|
| 87 |
+
interval = parts[1]
|
| 88 |
+
ticker_symbol = parts[2]
|
| 89 |
+
if ticker_symbol == symbol:
|
| 90 |
+
for candle in payload:
|
| 91 |
+
candle_data = {
|
| 92 |
+
'timestamp': candle['start'],
|
| 93 |
+
'open': float(candle['open']),
|
| 94 |
+
'high': float(candle['high']),
|
| 95 |
+
'low': float(candle['low']),
|
| 96 |
+
'close': float(candle['close']),
|
| 97 |
+
'volume': float(candle['volume'])
|
| 98 |
+
}
|
| 99 |
+
bot['data_engine'].update_candle(symbol, interval, candle_data)
|
| 100 |
+
|
| 101 |
+
async def run_trading_session(symbol: str, duration_hours: int, max_trades: Optional[int] = None):
|
| 102 |
+
"""Run a trading session for a specific symbol"""
|
| 103 |
+
session_id = f"{symbol}_{int(time.time())}"
|
| 104 |
+
|
| 105 |
+
log(f"π Starting trading session for {symbol} (Duration: {duration_hours}h)")
|
| 106 |
+
|
| 107 |
+
if symbol not in bot_instances:
|
| 108 |
+
bot_instances[symbol] = create_bot_instance(symbol)
|
| 109 |
+
|
| 110 |
+
bot = bot_instances[symbol]
|
| 111 |
+
active_sessions[session_id] = {
|
| 112 |
+
'symbol': symbol,
|
| 113 |
+
'start_time': datetime.now(),
|
| 114 |
+
'end_time': datetime.now() + timedelta(hours=duration_hours),
|
| 115 |
+
'duration_hours': duration_hours,
|
| 116 |
+
'max_trades': max_trades,
|
| 117 |
+
'trades_executed': 0,
|
| 118 |
+
'pnl': 0.0,
|
| 119 |
+
'status': 'running'
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
try:
|
| 123 |
+
# Start WebSocket
|
| 124 |
+
await bot['ws_client'].connect()
|
| 125 |
+
await asyncio.sleep(2)
|
| 126 |
+
await bot['ws_client'].subscribe_ticker([symbol])
|
| 127 |
+
await bot['ws_client'].subscribe_kline([symbol], ["1", "5"])
|
| 128 |
+
await bot['ws_client'].subscribe_orderbook([symbol], depth=25)
|
| 129 |
+
await bot['ws_client'].subscribe_trades([symbol])
|
| 130 |
+
|
| 131 |
+
# Start monitoring
|
| 132 |
+
monitor_task = asyncio.create_task(bot['monitor'].start_monitoring())
|
| 133 |
+
|
| 134 |
+
# Run for specified duration
|
| 135 |
+
end_time = datetime.now() + timedelta(hours=duration_hours)
|
| 136 |
+
trades_count = 0
|
| 137 |
+
|
| 138 |
+
while datetime.now() < end_time:
|
| 139 |
+
await asyncio.sleep(1)
|
| 140 |
+
|
| 141 |
+
# Check trade limits
|
| 142 |
+
if max_trades and trades_count >= max_trades:
|
| 143 |
+
log(f"π― Max trades reached for {symbol} session")
|
| 144 |
+
break
|
| 145 |
+
|
| 146 |
+
# Update session stats
|
| 147 |
+
session = active_sessions[session_id]
|
| 148 |
+
session['pnl'] = bot['risk_manager'].daily_pnl
|
| 149 |
+
session['trades_executed'] = len(bot['risk_manager'].trade_history)
|
| 150 |
+
|
| 151 |
+
# Stop monitoring
|
| 152 |
+
bot['monitor'].stop_monitoring()
|
| 153 |
+
await bot['ws_client'].close()
|
| 154 |
+
|
| 155 |
+
# Generate report
|
| 156 |
+
await generate_session_report(session_id)
|
| 157 |
+
|
| 158 |
+
except Exception as e:
|
| 159 |
+
log(f"β Error in trading session for {symbol}: {e}")
|
| 160 |
+
active_sessions[session_id]['status'] = 'error'
|
| 161 |
+
|
| 162 |
+
async def generate_session_report(session_id: str):
|
| 163 |
+
"""Generate and send trading session report"""
|
| 164 |
+
if session_id not in active_sessions:
|
| 165 |
+
return
|
| 166 |
+
|
| 167 |
+
session = active_sessions[session_id]
|
| 168 |
+
symbol = session['symbol']
|
| 169 |
+
|
| 170 |
+
if symbol in bot_instances:
|
| 171 |
+
bot = bot_instances[symbol]
|
| 172 |
+
risk_manager = bot['risk_manager']
|
| 173 |
+
|
| 174 |
+
# Calculate session metrics
|
| 175 |
+
total_trades = len(risk_manager.trade_history)
|
| 176 |
+
winning_trades = sum(1 for trade in risk_manager.trade_history if trade['pnl'] > 0)
|
| 177 |
+
losing_trades = total_trades - winning_trades
|
| 178 |
+
win_rate = winning_trades / total_trades if total_trades > 0 else 0
|
| 179 |
+
total_pnl = sum(trade['pnl'] for trade in risk_manager.trade_history)
|
| 180 |
+
|
| 181 |
+
# Generate report
|
| 182 |
+
report = f"""
|
| 183 |
+
π TRADING SESSION REPORT - {symbol}
|
| 184 |
+
βββββββββββββββββββββββββββββββββββββββββββ
|
| 185 |
+
|
| 186 |
+
π Session Details:
|
| 187 |
+
β’ Duration: {session['duration_hours']} hours
|
| 188 |
+
β’ Start Time: {session['start_time'].strftime('%Y-%m-%d %H:%M:%S')}
|
| 189 |
+
β’ End Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
| 190 |
+
|
| 191 |
+
π Performance Metrics:
|
| 192 |
+
β’ Total Trades: {total_trades}
|
| 193 |
+
β’ Winning Trades: {winning_trades}
|
| 194 |
+
β’ Losing Trades: {losing_trades}
|
| 195 |
+
β’ Win Rate: {win_rate:.1%}
|
| 196 |
+
β’ Total P&L: ${total_pnl:.2f}
|
| 197 |
+
|
| 198 |
+
π― Risk Management:
|
| 199 |
+
β’ Max Loss Limit: ${risk_manager.max_daily_loss}
|
| 200 |
+
β’ Risk per Trade: {risk_manager.risk_per_trade*100}%
|
| 201 |
+
β’ Leverage Used: {risk_manager.leverage}x
|
| 202 |
+
|
| 203 |
+
π Trade Summary:
|
| 204 |
+
"""
|
| 205 |
+
|
| 206 |
+
# Add individual trade details
|
| 207 |
+
for i, trade in enumerate(risk_manager.trade_history[-10:], 1): # Last 10 trades
|
| 208 |
+
pnl_emoji = "π’" if trade['pnl'] > 0 else "π΄"
|
| 209 |
+
report += f"{i}. {trade['side']} @ ${trade['entry_price']:.2f} β ${trade['exit_price']:.2f} {pnl_emoji} P&L: ${trade['pnl']:.2f}\n"
|
| 210 |
+
|
| 211 |
+
report += f"\nβββββββββββββββββββββββββββββββββββββββββββ"
|
| 212 |
+
|
| 213 |
+
# Send to Telegram
|
| 214 |
+
send_telegram(report)
|
| 215 |
+
|
| 216 |
+
# Save report
|
| 217 |
+
session_reports[session_id] = {
|
| 218 |
+
'session': session,
|
| 219 |
+
'metrics': {
|
| 220 |
+
'total_trades': total_trades,
|
| 221 |
+
'win_rate': win_rate,
|
| 222 |
+
'total_pnl': total_pnl,
|
| 223 |
+
'winning_trades': winning_trades,
|
| 224 |
+
'losing_trades': losing_trades
|
| 225 |
+
},
|
| 226 |
+
'trades': risk_manager.trade_history,
|
| 227 |
+
'report': report
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
log(f"π Session report generated for {symbol}")
|
| 231 |
+
|
| 232 |
+
# Mark session as completed
|
| 233 |
+
session['status'] = 'completed'
|
| 234 |
+
session['end_time'] = datetime.now()
|
| 235 |
+
|
| 236 |
+
@app.get("/")
|
| 237 |
+
async def root():
|
| 238 |
+
"""API root endpoint"""
|
| 239 |
+
return {"message": "Bybit Scalping Bot API", "version": "1.0.0", "status": "running"}
|
| 240 |
+
|
| 241 |
+
@app.get("/status")
|
| 242 |
+
async def get_status():
|
| 243 |
+
"""Get bot status"""
|
| 244 |
+
total_pnl = 0
|
| 245 |
+
total_trades = 0
|
| 246 |
+
|
| 247 |
+
for session in active_sessions.values():
|
| 248 |
+
if session['status'] == 'running':
|
| 249 |
+
total_pnl += session.get('pnl', 0)
|
| 250 |
+
total_trades += session.get('trades_executed', 0)
|
| 251 |
+
|
| 252 |
+
return BotStatus(
|
| 253 |
+
is_running=bool(active_sessions),
|
| 254 |
+
active_sessions=[
|
| 255 |
+
{
|
| 256 |
+
'session_id': sid,
|
| 257 |
+
'symbol': session['symbol'],
|
| 258 |
+
'status': session['status'],
|
| 259 |
+
'start_time': session['start_time'].isoformat(),
|
| 260 |
+
'pnl': session.get('pnl', 0),
|
| 261 |
+
'trades': session.get('trades_executed', 0)
|
| 262 |
+
}
|
| 263 |
+
for sid, session in active_sessions.items()
|
| 264 |
+
],
|
| 265 |
+
total_pnl=total_pnl,
|
| 266 |
+
trades_today=total_trades,
|
| 267 |
+
uptime=str(datetime.now() - datetime.fromtimestamp(time.time()))
|
| 268 |
+
)
|
| 269 |
+
|
| 270 |
+
@app.post("/start/{symbol}")
|
| 271 |
+
async def start_symbol_trading(symbol: str, background_tasks: BackgroundTasks, duration_hours: int = 18, max_trades: Optional[int] = None):
|
| 272 |
+
"""Start trading for a specific symbol"""
|
| 273 |
+
settings, configured_pairs = load_config()
|
| 274 |
+
|
| 275 |
+
if symbol not in configured_pairs:
|
| 276 |
+
raise HTTPException(status_code=400, detail=f"Symbol {symbol} not configured. Available: {configured_pairs}")
|
| 277 |
+
|
| 278 |
+
# Check if already running
|
| 279 |
+
for session in active_sessions.values():
|
| 280 |
+
if session['symbol'] == symbol and session['status'] == 'running':
|
| 281 |
+
raise HTTPException(status_code=400, detail=f"Trading already active for {symbol}")
|
| 282 |
+
|
| 283 |
+
# Start trading session
|
| 284 |
+
background_tasks.add_task(run_trading_session, symbol, duration_hours, max_trades)
|
| 285 |
+
|
| 286 |
+
message = f"π Started trading session for {symbol} ({duration_hours}h duration)"
|
| 287 |
+
send_telegram(message)
|
| 288 |
+
log(message)
|
| 289 |
+
|
| 290 |
+
return {"message": message, "symbol": symbol, "duration_hours": duration_hours}
|
| 291 |
+
|
| 292 |
+
@app.post("/stop/{symbol}")
|
| 293 |
+
async def stop_symbol_trading(symbol: str):
|
| 294 |
+
"""Stop trading for a specific symbol"""
|
| 295 |
+
stopped_sessions = []
|
| 296 |
+
|
| 297 |
+
for session_id, session in active_sessions.items():
|
| 298 |
+
if session['symbol'] == symbol and session['status'] == 'running':
|
| 299 |
+
session['status'] = 'stopped'
|
| 300 |
+
session['end_time'] = datetime.now()
|
| 301 |
+
stopped_sessions.append(session_id)
|
| 302 |
+
|
| 303 |
+
if symbol in bot_instances:
|
| 304 |
+
bot = bot_instances[symbol]
|
| 305 |
+
bot['monitor'].stop_monitoring()
|
| 306 |
+
await bot['ws_client'].close()
|
| 307 |
+
|
| 308 |
+
if stopped_sessions:
|
| 309 |
+
message = f"π Stopped trading for {symbol}"
|
| 310 |
+
send_telegram(message)
|
| 311 |
+
log(message)
|
| 312 |
+
return {"message": message, "stopped_sessions": stopped_sessions}
|
| 313 |
+
else:
|
| 314 |
+
raise HTTPException(status_code=404, detail=f"No active trading session found for {symbol}")
|
| 315 |
+
|
| 316 |
+
@app.get("/sessions")
|
| 317 |
+
async def get_sessions():
|
| 318 |
+
"""Get all trading sessions"""
|
| 319 |
+
return {
|
| 320 |
+
"active_sessions": active_sessions,
|
| 321 |
+
"completed_sessions": session_reports
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
@app.get("/report/{session_id}")
|
| 325 |
+
async def get_session_report(session_id: str):
|
| 326 |
+
"""Get detailed report for a specific session"""
|
| 327 |
+
if session_id in session_reports:
|
| 328 |
+
return session_reports[session_id]
|
| 329 |
+
else:
|
| 330 |
+
raise HTTPException(status_code=404, detail="Session report not found")
|
| 331 |
+
|
| 332 |
+
@app.get("/logs/analysis")
|
| 333 |
+
async def get_analysis_logs(lines: int = 50):
|
| 334 |
+
"""Get recent analysis logs"""
|
| 335 |
+
try:
|
| 336 |
+
log_file = "logs/scalper.log"
|
| 337 |
+
if os.path.exists(log_file):
|
| 338 |
+
with open(log_file, 'r') as f:
|
| 339 |
+
all_lines = f.readlines()
|
| 340 |
+
# Filter for analysis-related logs
|
| 341 |
+
analysis_lines = [line for line in all_lines if any(keyword in line for keyword in [
|
| 342 |
+
'SIGNAL', 'EMA', 'RSI', 'volume', 'orderbook', 'analysis', 'strategy'
|
| 343 |
+
])]
|
| 344 |
+
recent_lines = analysis_lines[-lines:]
|
| 345 |
+
return {
|
| 346 |
+
"logs": recent_lines,
|
| 347 |
+
"count": len(recent_lines),
|
| 348 |
+
"total_analysis_logs": len(analysis_lines)
|
| 349 |
+
}
|
| 350 |
+
else:
|
| 351 |
+
return {"error": "Log file not found", "logs": [], "count": 0}
|
| 352 |
+
except Exception as e:
|
| 353 |
+
raise HTTPException(status_code=500, detail=f"Error reading logs: {e}")
|
| 354 |
+
|
| 355 |
+
@app.get("/logs/live")
|
| 356 |
+
async def get_live_logs(lines: int = 20):
|
| 357 |
+
"""Get recent live logs (streaming)"""
|
| 358 |
+
try:
|
| 359 |
+
log_file = "logs/scalper.log"
|
| 360 |
+
if os.path.exists(log_file):
|
| 361 |
+
with open(log_file, 'r') as f:
|
| 362 |
+
all_lines = f.readlines()
|
| 363 |
+
recent_lines = all_lines[-lines:]
|
| 364 |
+
return {
|
| 365 |
+
"logs": recent_lines,
|
| 366 |
+
"count": len(recent_lines),
|
| 367 |
+
"latest_timestamp": recent_lines[-1].split(' - ')[0] if recent_lines else None
|
| 368 |
+
}
|
| 369 |
+
else:
|
| 370 |
+
return {"error": "Log file not found", "logs": [], "count": 0}
|
| 371 |
+
except Exception as e:
|
| 372 |
+
raise HTTPException(status_code=500, detail=f"Error reading logs: {e}")
|
| 373 |
+
|
| 374 |
+
@app.post("/start_all")
|
| 375 |
+
async def start_all_pairs(background_tasks: BackgroundTasks, duration_hours: int = 18, max_trades: Optional[int] = None):
|
| 376 |
+
"""Start trading sessions for all configured pairs"""
|
| 377 |
+
settings, configured_pairs = load_config()
|
| 378 |
+
|
| 379 |
+
started_sessions = []
|
| 380 |
+
failed_sessions = []
|
| 381 |
+
|
| 382 |
+
for symbol in configured_pairs:
|
| 383 |
+
# Check if already running
|
| 384 |
+
already_running = any(session['symbol'] == symbol and session['status'] == 'running'
|
| 385 |
+
for session in active_sessions.values())
|
| 386 |
+
|
| 387 |
+
if already_running:
|
| 388 |
+
failed_sessions.append({"symbol": symbol, "reason": "Already running"})
|
| 389 |
+
continue
|
| 390 |
+
|
| 391 |
+
try:
|
| 392 |
+
# Start trading session
|
| 393 |
+
background_tasks.add_task(run_trading_session, symbol, duration_hours, max_trades)
|
| 394 |
+
started_sessions.append({"symbol": symbol, "duration_hours": duration_hours})
|
| 395 |
+
except Exception as e:
|
| 396 |
+
failed_sessions.append({"symbol": symbol, "reason": str(e)})
|
| 397 |
+
|
| 398 |
+
message = f"π Started trading sessions for {len(started_sessions)} pairs"
|
| 399 |
+
if started_sessions:
|
| 400 |
+
send_telegram(message)
|
| 401 |
+
log(message)
|
| 402 |
+
|
| 403 |
+
return {
|
| 404 |
+
"message": message,
|
| 405 |
+
"started_sessions": started_sessions,
|
| 406 |
+
"failed_sessions": failed_sessions,
|
| 407 |
+
"total_started": len(started_sessions),
|
| 408 |
+
"total_failed": len(failed_sessions)
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
@app.post("/stop_all")
|
| 412 |
+
async def stop_all_pairs():
|
| 413 |
+
"""Stop trading sessions for all pairs"""
|
| 414 |
+
stopped_sessions = []
|
| 415 |
+
|
| 416 |
+
for session_id, session in list(active_sessions.items()):
|
| 417 |
+
if session['status'] == 'running':
|
| 418 |
+
session['status'] = 'stopped'
|
| 419 |
+
session['end_time'] = datetime.now()
|
| 420 |
+
stopped_sessions.append(session_id)
|
| 421 |
+
|
| 422 |
+
# Stop the actual trading
|
| 423 |
+
if session['symbol'] in bot_instances:
|
| 424 |
+
bot = bot_instances[session['symbol']]
|
| 425 |
+
bot['monitor'].stop_monitoring()
|
| 426 |
+
await bot['ws_client'].close()
|
| 427 |
+
|
| 428 |
+
if stopped_sessions:
|
| 429 |
+
message = f"π Stopped all trading sessions ({len(stopped_sessions)} sessions)"
|
| 430 |
+
send_telegram(message)
|
| 431 |
+
log(message)
|
| 432 |
+
return {"message": message, "stopped_sessions": stopped_sessions}
|
| 433 |
+
else:
|
| 434 |
+
return {"message": "No active sessions to stop", "stopped_sessions": []}
|
| 435 |
+
|
| 436 |
+
@app.get("/analysis/status")
|
| 437 |
+
async def get_analysis_status():
|
| 438 |
+
"""Get current analysis status for all active sessions"""
|
| 439 |
+
analysis_status = {}
|
| 440 |
+
|
| 441 |
+
for session_id, session in active_sessions.items():
|
| 442 |
+
if session['status'] == 'running' and session['symbol'] in bot_instances:
|
| 443 |
+
bot = bot_instances[session['symbol']]
|
| 444 |
+
symbol = session['symbol']
|
| 445 |
+
|
| 446 |
+
# Get current market data
|
| 447 |
+
try:
|
| 448 |
+
current_price = bot['data_engine'].get_prices(symbol, limit=1)
|
| 449 |
+
current_price = current_price[-1] if current_price else None
|
| 450 |
+
except:
|
| 451 |
+
current_price = None
|
| 452 |
+
|
| 453 |
+
# Get technical indicators
|
| 454 |
+
try:
|
| 455 |
+
ema_fast = bot['data_engine'].calculate_ema(symbol, "1", 9)
|
| 456 |
+
ema_slow = bot['data_engine'].calculate_ema(symbol, "1", 21)
|
| 457 |
+
rsi = bot['data_engine'].calculate_rsi(symbol, "1", 14)
|
| 458 |
+
volume_spike = bot['data_engine'].detect_volume_spike(symbol, "1", 1.3)
|
| 459 |
+
orderbook_imbalance = bot['data_engine'].get_orderbook_imbalance(symbol)
|
| 460 |
+
except:
|
| 461 |
+
ema_fast = ema_slow = rsi = volume_spike = orderbook_imbalance = None
|
| 462 |
+
|
| 463 |
+
analysis_status[symbol] = {
|
| 464 |
+
"session_id": session_id,
|
| 465 |
+
"current_price": current_price,
|
| 466 |
+
"indicators": {
|
| 467 |
+
"ema_9": ema_fast,
|
| 468 |
+
"ema_21": ema_slow,
|
| 469 |
+
"rsi_14": rsi,
|
| 470 |
+
"volume_spike": volume_spike,
|
| 471 |
+
"orderbook_imbalance": orderbook_imbalance
|
| 472 |
+
},
|
| 473 |
+
"strategy_conditions": {
|
| 474 |
+
"trend_up": ema_fast > ema_slow if ema_fast and ema_slow else None,
|
| 475 |
+
"rsi_valid": 40 <= rsi <= 70 if rsi else None,
|
| 476 |
+
"volume_confirmed": volume_spike,
|
| 477 |
+
"orderbook_aligned": abs(orderbook_imbalance or 0) > 0.15
|
| 478 |
+
},
|
| 479 |
+
"last_update": datetime.now().isoformat()
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
return {
|
| 483 |
+
"analysis_status": analysis_status,
|
| 484 |
+
"active_sessions": len(analysis_status),
|
| 485 |
+
"last_updated": datetime.now().isoformat()
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
@app.post("/emergency_stop")
|
| 489 |
+
async def emergency_stop():
|
| 490 |
+
"""Emergency stop all trading"""
|
| 491 |
+
stopped_sessions = []
|
| 492 |
+
|
| 493 |
+
for session_id, session in active_sessions.items():
|
| 494 |
+
if session['status'] == 'running':
|
| 495 |
+
session['status'] = 'emergency_stop'
|
| 496 |
+
session['end_time'] = datetime.now()
|
| 497 |
+
stopped_sessions.append(session_id)
|
| 498 |
+
|
| 499 |
+
# Stop all monitors
|
| 500 |
+
for bot in bot_instances.values():
|
| 501 |
+
bot['monitor'].stop_monitoring()
|
| 502 |
+
await bot['ws_client'].close()
|
| 503 |
+
|
| 504 |
+
message = f"π¨ EMERGENCY STOP activated - All trading halted"
|
| 505 |
+
send_telegram(message)
|
| 506 |
+
log(message)
|
| 507 |
+
|
| 508 |
+
return {"message": message, "stopped_sessions": stopped_sessions}
|
| 509 |
+
|
| 510 |
+
if __name__ == "__main__":
|
| 511 |
+
log("π Starting FastAPI server for Bybit Scalping Bot")
|
| 512 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
app.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import subprocess
|
| 3 |
+
import os
|
| 4 |
+
import signal
|
| 5 |
+
import time
|
| 6 |
+
from threading import Thread
|
| 7 |
+
import requests
|
| 8 |
+
|
| 9 |
+
# Set page config
|
| 10 |
+
st.set_page_config(
|
| 11 |
+
page_title="Scalping Bot Control",
|
| 12 |
+
page_icon="π€",
|
| 13 |
+
layout="wide"
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
st.title("π€ Bybit Scalping Bot Control Panel")
|
| 17 |
+
|
| 18 |
+
# Sidebar
|
| 19 |
+
st.sidebar.header("βοΈ Configuration")
|
| 20 |
+
|
| 21 |
+
# Environment variables setup
|
| 22 |
+
st.sidebar.subheader("π API Configuration")
|
| 23 |
+
bybit_key = st.sidebar.text_input("Bybit API Key", type="password")
|
| 24 |
+
bybit_secret = st.sidebar.text_input("Bybit API Secret", type="password")
|
| 25 |
+
bybit_testnet = st.sidebar.checkbox("Use Testnet", value=False)
|
| 26 |
+
telegram_token = st.sidebar.text_input("Telegram Bot Token (optional)")
|
| 27 |
+
telegram_chat = st.sidebar.text_input("Telegram Chat ID (optional)")
|
| 28 |
+
|
| 29 |
+
if st.sidebar.button("πΎ Save Configuration"):
|
| 30 |
+
# Save to .env file
|
| 31 |
+
env_content = f"""BYBIT_API_KEY={bybit_key}
|
| 32 |
+
BYBIT_API_SECRET={bybit_secret}
|
| 33 |
+
BYBIT_TESTNET={str(bybit_testnet).lower()}
|
| 34 |
+
TELEGRAM_BOT_TOKEN={telegram_token}
|
| 35 |
+
TELEGRAM_CHAT_ID={telegram_chat}
|
| 36 |
+
"""
|
| 37 |
+
with open('.env', 'w') as f:
|
| 38 |
+
f.write(env_content)
|
| 39 |
+
st.sidebar.success("Configuration saved!")
|
| 40 |
+
|
| 41 |
+
# Main content
|
| 42 |
+
col1, col2, col3 = st.columns(3)
|
| 43 |
+
|
| 44 |
+
with col1:
|
| 45 |
+
st.subheader("π Start Trading")
|
| 46 |
+
symbol = st.selectbox("Symbol", ["BTCUSDT", "ETHUSDT", "SOLUSDT"])
|
| 47 |
+
duration = st.slider("Duration (hours)", 1, 24, 18)
|
| 48 |
+
max_trades = st.number_input("Max Trades (optional)", min_value=1, value=50)
|
| 49 |
+
|
| 50 |
+
if st.button("βΆοΈ Start Trading", key="start"):
|
| 51 |
+
try:
|
| 52 |
+
response = requests.post(f"http://localhost:8000/start/{symbol}",
|
| 53 |
+
params={"duration_hours": duration, "max_trades": max_trades})
|
| 54 |
+
if response.status_code == 200:
|
| 55 |
+
st.success(f"β
Started trading {symbol} for {duration} hours")
|
| 56 |
+
else:
|
| 57 |
+
st.error(f"β Failed to start: {response.text}")
|
| 58 |
+
except Exception as e:
|
| 59 |
+
st.error(f"β Error: {e}")
|
| 60 |
+
|
| 61 |
+
with col2:
|
| 62 |
+
st.subheader("π Stop Trading")
|
| 63 |
+
stop_symbol = st.selectbox("Symbol to Stop", ["BTCUSDT", "ETHUSDT", "SOLUSDT"], key="stop_select")
|
| 64 |
+
|
| 65 |
+
if st.button("βΉοΈ Stop Trading", key="stop"):
|
| 66 |
+
try:
|
| 67 |
+
response = requests.post(f"http://localhost:8000/stop/{stop_symbol}")
|
| 68 |
+
if response.status_code == 200:
|
| 69 |
+
st.success(f"β
Stopped trading {stop_symbol}")
|
| 70 |
+
else:
|
| 71 |
+
st.error(f"β Failed to stop: {response.text}")
|
| 72 |
+
except Exception as e:
|
| 73 |
+
st.error(f"β Error: {e}")
|
| 74 |
+
|
| 75 |
+
with col3:
|
| 76 |
+
st.subheader("π¨ Emergency")
|
| 77 |
+
if st.button("π¨ EMERGENCY STOP ALL", key="emergency"):
|
| 78 |
+
try:
|
| 79 |
+
response = requests.post("http://localhost:8000/emergency_stop")
|
| 80 |
+
if response.status_code == 200:
|
| 81 |
+
st.error("π¨ ALL TRADING STOPPED!")
|
| 82 |
+
else:
|
| 83 |
+
st.error(f"β Failed: {response.text}")
|
| 84 |
+
except Exception as e:
|
| 85 |
+
st.error(f"β Error: {e}")
|
| 86 |
+
|
| 87 |
+
# Status section
|
| 88 |
+
st.header("π Bot Status")
|
| 89 |
+
|
| 90 |
+
if st.button("π Refresh Status"):
|
| 91 |
+
try:
|
| 92 |
+
response = requests.get("http://localhost:8000/status")
|
| 93 |
+
if response.status_code == 200:
|
| 94 |
+
status = response.json()
|
| 95 |
+
|
| 96 |
+
# Overall status
|
| 97 |
+
if status["is_running"]:
|
| 98 |
+
st.success("β
Bot is RUNNING")
|
| 99 |
+
else:
|
| 100 |
+
st.warning("βΈοΈ Bot is STOPPED")
|
| 101 |
+
|
| 102 |
+
st.metric("Total P&L", f"${status['total_pnl']:.2f}")
|
| 103 |
+
st.metric("Trades Today", status['trades_today'])
|
| 104 |
+
|
| 105 |
+
# Active sessions
|
| 106 |
+
if status["active_sessions"]:
|
| 107 |
+
st.subheader("π― Active Sessions")
|
| 108 |
+
for session in status["active_sessions"]:
|
| 109 |
+
with st.expander(f"{session['symbol']} - {session['status'].upper()}"):
|
| 110 |
+
st.write(f"**Session ID:** {session['session_id']}")
|
| 111 |
+
st.write(f"**Started:** {session['start_time'][:19]}")
|
| 112 |
+
st.write(f"**P&L:** ${session['pnl']:.2f}")
|
| 113 |
+
st.write(f"**Trades:** {session['trades']}")
|
| 114 |
+
else:
|
| 115 |
+
st.info("No active sessions")
|
| 116 |
+
|
| 117 |
+
else:
|
| 118 |
+
st.error(f"β Failed to get status: {response.status_code}")
|
| 119 |
+
except Exception as e:
|
| 120 |
+
st.error(f"β Connection error: {e}")
|
| 121 |
+
|
| 122 |
+
# Analysis section
|
| 123 |
+
st.header("π Live Analysis")
|
| 124 |
+
|
| 125 |
+
if st.button("π Refresh Analysis"):
|
| 126 |
+
try:
|
| 127 |
+
response = requests.get("http://localhost:8000/analysis/status")
|
| 128 |
+
if response.status_code == 200:
|
| 129 |
+
analysis = response.json()
|
| 130 |
+
|
| 131 |
+
for symbol, data in analysis["analysis_status"].items():
|
| 132 |
+
with st.expander(f"π {symbol} Analysis"):
|
| 133 |
+
col1, col2 = st.columns(2)
|
| 134 |
+
|
| 135 |
+
with col1:
|
| 136 |
+
st.metric("Current Price", f"${data['current_price']:.2f}" if data['current_price'] else "N/A")
|
| 137 |
+
|
| 138 |
+
indicators = data['indicators']
|
| 139 |
+
st.write("**Indicators:**")
|
| 140 |
+
st.write(f"EMA 9: {indicators['ema_9']:.4f}" if indicators['ema_9'] else "EMA 9: N/A")
|
| 141 |
+
st.write(f"EMA 21: {indicators['ema_21']:.4f}" if indicators['ema_21'] else "EMA 21: N/A")
|
| 142 |
+
st.write(f"RSI 14: {indicators['rsi_14']:.1f}" if indicators['rsi_14'] else "RSI 14: N/A")
|
| 143 |
+
|
| 144 |
+
with col2:
|
| 145 |
+
conditions = data['strategy_conditions']
|
| 146 |
+
st.write("**Strategy Conditions:**")
|
| 147 |
+
st.write(f"π Trend Up: {'β
' if conditions['trend_up'] else 'β'}")
|
| 148 |
+
st.write(f"π RSI Valid: {'β
' if conditions['rsi_valid'] else 'β'}")
|
| 149 |
+
st.write(f"π₯ Volume Spike: {'β
' if conditions['volume_spike'] else 'β'}")
|
| 150 |
+
st.write(f"π― Orderbook OK: {'β
' if conditions['orderbook_aligned'] else 'β'}")
|
| 151 |
+
|
| 152 |
+
all_conditions = all([conditions['trend_up'], conditions['rsi_valid'],
|
| 153 |
+
conditions['volume_spike'], conditions['orderbook_aligned']])
|
| 154 |
+
|
| 155 |
+
if all_conditions:
|
| 156 |
+
st.success("π― TRADE SIGNAL READY!")
|
| 157 |
+
else:
|
| 158 |
+
st.info("β³ Waiting for conditions...")
|
| 159 |
+
else:
|
| 160 |
+
st.error(f"β Failed to get analysis: {response.status_code}")
|
| 161 |
+
except Exception as e:
|
| 162 |
+
st.error(f"β Connection error: {e}")
|
| 163 |
+
|
| 164 |
+
# Logs section
|
| 165 |
+
st.header("π Recent Logs")
|
| 166 |
+
|
| 167 |
+
log_type = st.selectbox("Log Type", ["Live Logs", "Analysis Logs"])
|
| 168 |
+
|
| 169 |
+
if st.button("π Refresh Logs"):
|
| 170 |
+
try:
|
| 171 |
+
if log_type == "Live Logs":
|
| 172 |
+
response = requests.get("http://localhost:8000/logs/live")
|
| 173 |
+
else:
|
| 174 |
+
response = requests.get("http://localhost:8000/logs/analysis")
|
| 175 |
+
|
| 176 |
+
if response.status_code == 200:
|
| 177 |
+
logs_data = response.json()
|
| 178 |
+
|
| 179 |
+
st.write(f"**Total {log_type}:** {logs_data['count']}")
|
| 180 |
+
|
| 181 |
+
logs_text = ""
|
| 182 |
+
for log_line in logs_data['logs'][-10:]: # Show last 10
|
| 183 |
+
logs_text += log_line
|
| 184 |
+
|
| 185 |
+
st.code(logs_text, language="text")
|
| 186 |
+
else:
|
| 187 |
+
st.error(f"β Failed to get logs: {response.status_code}")
|
| 188 |
+
except Exception as e:
|
| 189 |
+
st.error(f"β Connection error: {e}")
|
| 190 |
+
|
| 191 |
+
# Footer
|
| 192 |
+
st.markdown("---")
|
| 193 |
+
st.markdown("*π€ Bybit Scalping Bot - FastAPI Control Interface*")
|
| 194 |
+
st.markdown("*Made with β€οΈ for automated crypto trading*")
|
backtester/__pycache__/engine.cpython-312.pyc
ADDED
|
Binary file (18.6 kB). View file
|
|
|
backtester/engine.py
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import numpy as np
|
| 3 |
+
from typing import Dict, List, Optional, Any, Tuple
|
| 4 |
+
import logging
|
| 5 |
+
import yaml
|
| 6 |
+
from datetime import datetime, timedelta
|
| 7 |
+
import json
|
| 8 |
+
import os
|
| 9 |
+
|
| 10 |
+
from core.strategy import ScalpingStrategy
|
| 11 |
+
from core.data_engine import DataEngine
|
| 12 |
+
from core.risk import RiskManager
|
| 13 |
+
from services.logger import log
|
| 14 |
+
|
| 15 |
+
logger = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
+
class BacktestingEngine:
|
| 18 |
+
def __init__(self):
|
| 19 |
+
self.settings = yaml.safe_load(open("config/settings.yaml"))
|
| 20 |
+
self.pairs = yaml.safe_load(open("config/pairs.yaml"))["pairs"]
|
| 21 |
+
|
| 22 |
+
self.initial_balance = 1000
|
| 23 |
+
self.fee_rate = 0.001
|
| 24 |
+
self.slippage = 0.0005
|
| 25 |
+
|
| 26 |
+
self.results = {}
|
| 27 |
+
|
| 28 |
+
def load_historical_data(self, symbol: str, interval: str = "1",
|
| 29 |
+
days: int = 30) -> Optional[pd.DataFrame]:
|
| 30 |
+
try:
|
| 31 |
+
|
| 32 |
+
periods = days * 24 * 60
|
| 33 |
+
base_price = 50000 if symbol.startswith('BTC') else 3000 if symbol.startswith('ETH') else 100
|
| 34 |
+
|
| 35 |
+
np.random.seed(42)
|
| 36 |
+
|
| 37 |
+
timestamps = pd.date_range(
|
| 38 |
+
start=datetime.now() - timedelta(days=days),
|
| 39 |
+
end=datetime.now(),
|
| 40 |
+
freq='1min'
|
| 41 |
+
)[:periods]
|
| 42 |
+
|
| 43 |
+
returns = np.random.normal(0, 0.001, periods)
|
| 44 |
+
prices = base_price * np.exp(np.cumsum(returns))
|
| 45 |
+
|
| 46 |
+
highs = prices * (1 + np.abs(np.random.normal(0, 0.002, periods)))
|
| 47 |
+
lows = prices * (1 - np.abs(np.random.normal(0, 0.002, periods)))
|
| 48 |
+
opens = np.roll(prices, 1)
|
| 49 |
+
opens[0] = base_price
|
| 50 |
+
|
| 51 |
+
volumes = np.random.lognormal(10, 1, periods)
|
| 52 |
+
|
| 53 |
+
df = pd.DataFrame({
|
| 54 |
+
'timestamp': timestamps,
|
| 55 |
+
'open': opens,
|
| 56 |
+
'high': highs,
|
| 57 |
+
'low': lows,
|
| 58 |
+
'close': prices,
|
| 59 |
+
'volume': volumes
|
| 60 |
+
})
|
| 61 |
+
|
| 62 |
+
df.set_index('timestamp', inplace=True)
|
| 63 |
+
return df
|
| 64 |
+
|
| 65 |
+
except Exception as e:
|
| 66 |
+
logger.error(f"Error loading historical data for {symbol}: {e}")
|
| 67 |
+
return None
|
| 68 |
+
|
| 69 |
+
def run_backtest(self, symbol: str, strategy_params: Optional[Dict[str, Any]] = None,
|
| 70 |
+
start_date: Optional[datetime] = None, end_date: Optional[datetime] = None) -> Dict[str, Any]:
|
| 71 |
+
try:
|
| 72 |
+
log(f"π Starting backtest for {symbol}")
|
| 73 |
+
|
| 74 |
+
df = self.load_historical_data(symbol)
|
| 75 |
+
if df is None or df.empty:
|
| 76 |
+
return {'error': f'No data available for {symbol}'}
|
| 77 |
+
|
| 78 |
+
if start_date:
|
| 79 |
+
df = df[df.index >= start_date]
|
| 80 |
+
if end_date:
|
| 81 |
+
df = df[df.index <= end_date]
|
| 82 |
+
|
| 83 |
+
if len(df) < 100:
|
| 84 |
+
return {'error': f'Insufficient data for {symbol}: {len(df)} candles'}
|
| 85 |
+
|
| 86 |
+
data_engine = DataEngine()
|
| 87 |
+
strategy = ScalpingStrategy(data_engine)
|
| 88 |
+
|
| 89 |
+
if strategy_params:
|
| 90 |
+
for key, value in strategy_params.items():
|
| 91 |
+
if hasattr(strategy, key):
|
| 92 |
+
setattr(strategy, key, value)
|
| 93 |
+
|
| 94 |
+
mock_exchange = MockExchange(self.fee_rate, self.slippage)
|
| 95 |
+
|
| 96 |
+
risk_manager = RiskManager(mock_exchange)
|
| 97 |
+
risk_manager.max_daily_loss = float('inf')
|
| 98 |
+
|
| 99 |
+
trades, equity_curve = self._simulate_trading(
|
| 100 |
+
df, strategy, risk_manager, mock_exchange, symbol
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
metrics = self._calculate_metrics(trades, equity_curve, df)
|
| 104 |
+
|
| 105 |
+
result = {
|
| 106 |
+
'symbol': symbol,
|
| 107 |
+
'total_trades': len(trades),
|
| 108 |
+
'winning_trades': sum(1 for t in trades if t['pnl'] > 0),
|
| 109 |
+
'losing_trades': sum(1 for t in trades if t['pnl'] < 0),
|
| 110 |
+
'total_pnl': sum(t['pnl'] for t in trades),
|
| 111 |
+
'max_drawdown': metrics['max_drawdown'],
|
| 112 |
+
'win_rate': metrics['win_rate'],
|
| 113 |
+
'profit_factor': metrics['profit_factor'],
|
| 114 |
+
'sharpe_ratio': metrics['sharpe_ratio'],
|
| 115 |
+
'avg_trade_duration': metrics['avg_trade_duration'],
|
| 116 |
+
'trades': trades[:50],
|
| 117 |
+
'equity_curve': equity_curve[-100:]
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
self.results[symbol] = result
|
| 121 |
+
log(f"β
Backtest completed for {symbol}: {len(trades)} trades, PnL: {result['total_pnl']:.2f}")
|
| 122 |
+
|
| 123 |
+
return result
|
| 124 |
+
|
| 125 |
+
except Exception as e:
|
| 126 |
+
logger.error(f"Error in backtest for {symbol}: {e}")
|
| 127 |
+
return {'error': str(e)}
|
| 128 |
+
|
| 129 |
+
def _simulate_trading(self, df: pd.DataFrame, strategy: ScalpingStrategy,
|
| 130 |
+
risk_manager: RiskManager, exchange: 'MockExchange',
|
| 131 |
+
symbol: str) -> Tuple[List[Dict], List[float]]:
|
| 132 |
+
trades = []
|
| 133 |
+
equity_curve = [self.initial_balance]
|
| 134 |
+
open_position = None
|
| 135 |
+
|
| 136 |
+
for i, (timestamp, row) in enumerate(df.iterrows()):
|
| 137 |
+
current_price = row['close']
|
| 138 |
+
|
| 139 |
+
candle_data = {
|
| 140 |
+
'timestamp': timestamp.timestamp() * 1000,
|
| 141 |
+
'open': row['open'],
|
| 142 |
+
'high': row['high'],
|
| 143 |
+
'low': row['low'],
|
| 144 |
+
'close': row['close'],
|
| 145 |
+
'volume': row['volume']
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
historical_df = df.iloc[:i+1]
|
| 149 |
+
strategy_data_engine = DataEngine()
|
| 150 |
+
|
| 151 |
+
for j in range(max(0, i-200), i+1):
|
| 152 |
+
hist_candle = df.iloc[j]
|
| 153 |
+
hist_data = {
|
| 154 |
+
'timestamp': df.index[j].timestamp() * 1000,
|
| 155 |
+
'open': hist_candle['open'],
|
| 156 |
+
'high': hist_candle['high'],
|
| 157 |
+
'low': hist_candle['low'],
|
| 158 |
+
'close': hist_candle['close'],
|
| 159 |
+
'volume': hist_candle['volume']
|
| 160 |
+
}
|
| 161 |
+
strategy_data_engine.update_candle(symbol, "1", hist_data)
|
| 162 |
+
|
| 163 |
+
strategy.data_engine = strategy_data_engine
|
| 164 |
+
|
| 165 |
+
if open_position:
|
| 166 |
+
|
| 167 |
+
position_age = (timestamp - open_position['entry_time']).seconds / 60
|
| 168 |
+
|
| 169 |
+
exit_reason = None
|
| 170 |
+
if open_position['side'] == 'BUY':
|
| 171 |
+
if current_price >= open_position['tp_price']:
|
| 172 |
+
exit_reason = 'TP'
|
| 173 |
+
elif current_price <= open_position['sl_price']:
|
| 174 |
+
exit_reason = 'SL'
|
| 175 |
+
elif position_age > 15:
|
| 176 |
+
exit_reason = 'TIMEOUT'
|
| 177 |
+
else:
|
| 178 |
+
if current_price <= open_position['tp_price']:
|
| 179 |
+
exit_reason = 'TP'
|
| 180 |
+
elif current_price >= open_position['sl_price']:
|
| 181 |
+
exit_reason = 'SL'
|
| 182 |
+
elif position_age > 15:
|
| 183 |
+
exit_reason = 'TIMEOUT'
|
| 184 |
+
|
| 185 |
+
if exit_reason:
|
| 186 |
+
|
| 187 |
+
pnl = exchange.close_position(open_position, current_price)
|
| 188 |
+
equity_curve.append(equity_curve[-1] + pnl)
|
| 189 |
+
|
| 190 |
+
trade = {
|
| 191 |
+
'entry_time': open_position['entry_time'],
|
| 192 |
+
'exit_time': timestamp,
|
| 193 |
+
'side': open_position['side'],
|
| 194 |
+
'entry_price': open_position['entry_price'],
|
| 195 |
+
'exit_price': current_price,
|
| 196 |
+
'quantity': open_position['quantity'],
|
| 197 |
+
'pnl': pnl,
|
| 198 |
+
'reason': exit_reason,
|
| 199 |
+
'duration_minutes': position_age
|
| 200 |
+
}
|
| 201 |
+
trades.append(trade)
|
| 202 |
+
|
| 203 |
+
open_position = None
|
| 204 |
+
|
| 205 |
+
elif i > 50:
|
| 206 |
+
signal, confidence, price = strategy.generate_signal(symbol)
|
| 207 |
+
|
| 208 |
+
if signal in ['BUY', 'SELL'] and confidence > 0.6:
|
| 209 |
+
|
| 210 |
+
if risk_manager.validate_entry_signal(symbol, signal, confidence):
|
| 211 |
+
|
| 212 |
+
qty = risk_manager.calculate_position_size(symbol, price, signal)
|
| 213 |
+
|
| 214 |
+
if qty > 0:
|
| 215 |
+
|
| 216 |
+
open_position = {
|
| 217 |
+
'entry_time': timestamp,
|
| 218 |
+
'side': signal,
|
| 219 |
+
'entry_price': price,
|
| 220 |
+
'quantity': qty,
|
| 221 |
+
'tp_price': price * (1.025 if signal == 'BUY' else 0.975),
|
| 222 |
+
'sl_price': price * (0.99 if signal == 'BUY' else 1.01)
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
return trades, equity_curve
|
| 226 |
+
|
| 227 |
+
def _calculate_metrics(self, trades: List[Dict], equity_curve: List[float],
|
| 228 |
+
df: pd.DataFrame) -> Dict[str, float]:
|
| 229 |
+
try:
|
| 230 |
+
if not trades:
|
| 231 |
+
return {
|
| 232 |
+
'max_drawdown': 0.0,
|
| 233 |
+
'win_rate': 0.0,
|
| 234 |
+
'profit_factor': 0.0,
|
| 235 |
+
'sharpe_ratio': 0.0,
|
| 236 |
+
'avg_trade_duration': 0.0
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
peak = equity_curve[0]
|
| 240 |
+
max_drawdown = 0.0
|
| 241 |
+
|
| 242 |
+
for equity in equity_curve:
|
| 243 |
+
if equity > peak:
|
| 244 |
+
peak = equity
|
| 245 |
+
drawdown = (peak - equity) / peak
|
| 246 |
+
max_drawdown = max(max_drawdown, drawdown)
|
| 247 |
+
|
| 248 |
+
winning_trades = [t for t in trades if t['pnl'] > 0]
|
| 249 |
+
win_rate = len(winning_trades) / len(trades) if trades else 0.0
|
| 250 |
+
|
| 251 |
+
gross_profit = sum(t['pnl'] for t in winning_trades)
|
| 252 |
+
gross_loss = abs(sum(t['pnl'] for t in trades if t['pnl'] < 0))
|
| 253 |
+
profit_factor = gross_profit / gross_loss if gross_loss > 0 else float('inf')
|
| 254 |
+
|
| 255 |
+
returns = np.diff(equity_curve) / equity_curve[:-1]
|
| 256 |
+
if len(returns) > 1 and np.std(returns) > 0:
|
| 257 |
+
sharpe_ratio = np.mean(returns) / np.std(returns) * np.sqrt(365 * 24 * 60)
|
| 258 |
+
else:
|
| 259 |
+
sharpe_ratio = 0.0
|
| 260 |
+
|
| 261 |
+
durations = [t['duration_minutes'] for t in trades]
|
| 262 |
+
avg_trade_duration = np.mean(durations) if durations else 0.0
|
| 263 |
+
|
| 264 |
+
return {
|
| 265 |
+
'max_drawdown': max_drawdown,
|
| 266 |
+
'win_rate': win_rate,
|
| 267 |
+
'profit_factor': profit_factor,
|
| 268 |
+
'sharpe_ratio': sharpe_ratio,
|
| 269 |
+
'avg_trade_duration': avg_trade_duration
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
except Exception as e:
|
| 273 |
+
logger.error(f"Error calculating metrics: {e}")
|
| 274 |
+
return {
|
| 275 |
+
'max_drawdown': 0.0,
|
| 276 |
+
'win_rate': 0.0,
|
| 277 |
+
'profit_factor': 0.0,
|
| 278 |
+
'sharpe_ratio': 0.0,
|
| 279 |
+
'avg_trade_duration': 0.0
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
def optimize_parameters(self, symbol: str, param_ranges: Dict[str, List[float]]) -> Dict[str, Any]:
|
| 283 |
+
try:
|
| 284 |
+
log(f"π― Starting parameter optimization for {symbol}")
|
| 285 |
+
|
| 286 |
+
best_result = None
|
| 287 |
+
best_params = None
|
| 288 |
+
best_score = -float('inf')
|
| 289 |
+
|
| 290 |
+
from itertools import product
|
| 291 |
+
param_names = list(param_ranges.keys())
|
| 292 |
+
param_values = list(param_ranges.values())
|
| 293 |
+
|
| 294 |
+
total_combinations = np.prod([len(v) for v in param_values])
|
| 295 |
+
log(f"Testing {total_combinations} parameter combinations")
|
| 296 |
+
|
| 297 |
+
for i, param_combo in enumerate(product(*param_values)):
|
| 298 |
+
param_dict = dict(zip(param_names, param_combo))
|
| 299 |
+
|
| 300 |
+
result = self.run_backtest(symbol, strategy_params=param_dict)
|
| 301 |
+
|
| 302 |
+
if 'error' not in result:
|
| 303 |
+
|
| 304 |
+
score = result['sharpe_ratio'] - result['max_drawdown'] * 10
|
| 305 |
+
|
| 306 |
+
if score > best_score:
|
| 307 |
+
best_score = score
|
| 308 |
+
best_result = result
|
| 309 |
+
best_params = param_dict
|
| 310 |
+
|
| 311 |
+
if (i + 1) % 10 == 0:
|
| 312 |
+
log(f"Progress: {i + 1}/{total_combinations} combinations tested")
|
| 313 |
+
|
| 314 |
+
if best_result:
|
| 315 |
+
log(f"β
Optimization completed. Best params: {best_params}")
|
| 316 |
+
return {
|
| 317 |
+
'best_parameters': best_params,
|
| 318 |
+
'best_result': best_result,
|
| 319 |
+
'optimization_score': best_score
|
| 320 |
+
}
|
| 321 |
+
else:
|
| 322 |
+
return {'error': 'No valid results found during optimization'}
|
| 323 |
+
|
| 324 |
+
except Exception as e:
|
| 325 |
+
logger.error(f"Error in parameter optimization: {e}")
|
| 326 |
+
return {'error': str(e)}
|
| 327 |
+
|
| 328 |
+
def save_results(self, filename: str = "backtest_results.json"):
|
| 329 |
+
try:
|
| 330 |
+
os.makedirs("backtest_results", exist_ok=True)
|
| 331 |
+
filepath = f"backtest_results/{filename}"
|
| 332 |
+
|
| 333 |
+
with open(filepath, 'w') as f:
|
| 334 |
+
json.dump(self.results, f, indent=2, default=str)
|
| 335 |
+
|
| 336 |
+
log(f"πΎ Results saved to {filepath}")
|
| 337 |
+
|
| 338 |
+
except Exception as e:
|
| 339 |
+
logger.error(f"Error saving results: {e}")
|
| 340 |
+
|
| 341 |
+
def load_results(self, filename: str = "backtest_results.json") -> Dict[str, Any]:
|
| 342 |
+
try:
|
| 343 |
+
filepath = f"backtest_results/{filename}"
|
| 344 |
+
|
| 345 |
+
if os.path.exists(filepath):
|
| 346 |
+
with open(filepath, 'r') as f:
|
| 347 |
+
self.results = json.load(f)
|
| 348 |
+
log(f"π Results loaded from {filepath}")
|
| 349 |
+
return self.results
|
| 350 |
+
else:
|
| 351 |
+
return {}
|
| 352 |
+
|
| 353 |
+
except Exception as e:
|
| 354 |
+
logger.error(f"Error loading results: {e}")
|
| 355 |
+
return {}
|
| 356 |
+
|
| 357 |
+
def generate_report(self, symbol: str) -> str:
|
| 358 |
+
try:
|
| 359 |
+
if symbol not in self.results:
|
| 360 |
+
return f"No backtest results found for {symbol}"
|
| 361 |
+
|
| 362 |
+
result = self.results[symbol]
|
| 363 |
+
|
| 364 |
+
report = f
|
| 365 |
+
|
| 366 |
+
for i, trade in enumerate(result['trades'][-5:]):
|
| 367 |
+
report += f"{i+1}. {trade['side']} {trade['quantity']:.3f} @ {trade['entry_price']:.2f} -> {trade['exit_price']:.2f} (PnL: ${trade['pnl']:.2f})\n"
|
| 368 |
+
|
| 369 |
+
return report
|
| 370 |
+
|
| 371 |
+
except Exception as e:
|
| 372 |
+
logger.error(f"Error generating report: {e}")
|
| 373 |
+
return f"Error generating report: {e}"
|
| 374 |
+
|
| 375 |
+
class MockExchange:
|
| 376 |
+
|
| 377 |
+
def __init__(self, fee_rate: float = 0.001, slippage: float = 0.0005):
|
| 378 |
+
self.fee_rate = fee_rate
|
| 379 |
+
self.slippage = slippage
|
| 380 |
+
|
| 381 |
+
def get_balance(self):
|
| 382 |
+
return [{"coin": "USDT", "walletBalance": "10000"}]
|
| 383 |
+
|
| 384 |
+
def get_positions(self):
|
| 385 |
+
return []
|
| 386 |
+
|
| 387 |
+
def calculate_position_size(self, symbol, entry_price, side):
|
| 388 |
+
return 0.01
|
| 389 |
+
|
| 390 |
+
def validate_entry_signal(self, symbol, signal, confidence):
|
| 391 |
+
return True
|
| 392 |
+
|
| 393 |
+
def close_position(self, position, exit_price):
|
| 394 |
+
entry_price = position['entry_price']
|
| 395 |
+
quantity = position['quantity']
|
| 396 |
+
side = position['side']
|
| 397 |
+
|
| 398 |
+
if side == 'BUY':
|
| 399 |
+
exit_price *= (1 - self.slippage)
|
| 400 |
+
else:
|
| 401 |
+
exit_price *= (1 + self.slippage)
|
| 402 |
+
|
| 403 |
+
if side == 'BUY':
|
| 404 |
+
pnl = (exit_price - entry_price) / entry_price * quantity
|
| 405 |
+
else:
|
| 406 |
+
pnl = (entry_price - exit_price) / entry_price * quantity
|
| 407 |
+
|
| 408 |
+
fee = abs(pnl) * self.fee_rate
|
| 409 |
+
pnl -= fee
|
| 410 |
+
|
| 411 |
+
return pnl
|
config/pairs.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pairs:
|
| 2 |
+
- BTCUSDT
|
| 3 |
+
- ETHUSDT
|
| 4 |
+
- SOLUSDT
|
config/settings.yaml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
trading:
|
| 2 |
+
leverage: 20
|
| 3 |
+
tp_percent: 0.025
|
| 4 |
+
sl_percent: 0.01
|
| 5 |
+
trailing_tp: true
|
| 6 |
+
|
| 7 |
+
strategy:
|
| 8 |
+
interval: "1"
|
| 9 |
+
rsi_period: 14
|
| 10 |
+
ema_fast: 9
|
| 11 |
+
ema_slow: 21
|
| 12 |
+
|
| 13 |
+
risk:
|
| 14 |
+
risk_per_trade: 0.02
|
| 15 |
+
|
| 16 |
+
telegram:
|
| 17 |
+
enabled: true
|
core/__pycache__/data_engine.cpython-312.pyc
ADDED
|
Binary file (13.5 kB). View file
|
|
|
core/__pycache__/exchange.cpython-312.pyc
ADDED
|
Binary file (8.37 kB). View file
|
|
|
core/__pycache__/indicators.cpython-312.pyc
ADDED
|
Binary file (15.6 kB). View file
|
|
|
core/__pycache__/risk.cpython-312.pyc
ADDED
|
Binary file (15.6 kB). View file
|
|
|
core/__pycache__/strategy.cpython-312.pyc
ADDED
|
Binary file (11.2 kB). View file
|
|
|
core/__pycache__/trade_monitor.cpython-312.pyc
ADDED
|
Binary file (12.1 kB). View file
|
|
|
core/__pycache__/websockets.cpython-312.pyc
ADDED
|
Binary file (17 kB). View file
|
|
|
core/data_engine.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import numpy as np
|
| 3 |
+
from collections import deque
|
| 4 |
+
import logging
|
| 5 |
+
import time
|
| 6 |
+
import yaml
|
| 7 |
+
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
class DataEngine:
|
| 11 |
+
def __init__(self):
|
| 12 |
+
self.settings = yaml.safe_load(open("config/settings.yaml"))
|
| 13 |
+
|
| 14 |
+
self.price_buffers = {}
|
| 15 |
+
self.volume_buffers = {}
|
| 16 |
+
|
| 17 |
+
self.candle_buffers = {}
|
| 18 |
+
|
| 19 |
+
self.indicators_cache = {}
|
| 20 |
+
|
| 21 |
+
self.max_price_buffer = 1000
|
| 22 |
+
self.max_candle_buffer = 200
|
| 23 |
+
|
| 24 |
+
pairs = yaml.safe_load(open("config/pairs.yaml"))["pairs"]
|
| 25 |
+
for pair in pairs:
|
| 26 |
+
self._init_buffers(pair)
|
| 27 |
+
|
| 28 |
+
def _init_buffers(self, symbol):
|
| 29 |
+
self.price_buffers[symbol] = deque(maxlen=self.max_price_buffer)
|
| 30 |
+
self.volume_buffers[symbol] = deque(maxlen=self.max_price_buffer)
|
| 31 |
+
|
| 32 |
+
intervals = ["1", "5", "15"]
|
| 33 |
+
for interval in intervals:
|
| 34 |
+
key = f"{symbol}_{interval}"
|
| 35 |
+
self.candle_buffers[key] = deque(maxlen=self.max_candle_buffer)
|
| 36 |
+
self.indicators_cache[key] = {}
|
| 37 |
+
|
| 38 |
+
def update_price(self, symbol, price, volume=0):
|
| 39 |
+
self.price_buffers[symbol].append(float(price))
|
| 40 |
+
if volume > 0:
|
| 41 |
+
self.volume_buffers[symbol].append(float(volume))
|
| 42 |
+
|
| 43 |
+
def update_candle(self, symbol, interval, candle_data):
|
| 44 |
+
key = f"{symbol}_{interval}"
|
| 45 |
+
|
| 46 |
+
if isinstance(candle_data, dict):
|
| 47 |
+
candle = {
|
| 48 |
+
'timestamp': int(candle_data.get('timestamp', candle_data.get('start', time.time() * 1000))),
|
| 49 |
+
'open': float(candle_data['open']),
|
| 50 |
+
'high': float(candle_data['high']),
|
| 51 |
+
'low': float(candle_data['low']),
|
| 52 |
+
'close': float(candle_data['close']),
|
| 53 |
+
'volume': float(candle_data.get('volume', 0))
|
| 54 |
+
}
|
| 55 |
+
elif isinstance(candle_data, list):
|
| 56 |
+
|
| 57 |
+
candle = {
|
| 58 |
+
'timestamp': int(candle_data[0]),
|
| 59 |
+
'open': float(candle_data[1]),
|
| 60 |
+
'high': float(candle_data[2]),
|
| 61 |
+
'low': float(candle_data[3]),
|
| 62 |
+
'close': float(candle_data[4]),
|
| 63 |
+
'volume': float(candle_data[5])
|
| 64 |
+
}
|
| 65 |
+
else:
|
| 66 |
+
logger.error(f"Invalid candle data format: {candle_data}")
|
| 67 |
+
return
|
| 68 |
+
|
| 69 |
+
self.candle_buffers[key].append(candle)
|
| 70 |
+
|
| 71 |
+
self.update_price(symbol, candle['close'], candle['volume'])
|
| 72 |
+
|
| 73 |
+
self.indicators_cache[key] = {}
|
| 74 |
+
|
| 75 |
+
def get_prices(self, symbol, limit=100):
|
| 76 |
+
return list(self.price_buffers.get(symbol, deque()))[-limit:]
|
| 77 |
+
|
| 78 |
+
def get_candles(self, symbol, interval="1", limit=100):
|
| 79 |
+
key = f"{symbol}_{interval}"
|
| 80 |
+
candles = list(self.candle_buffers.get(key, deque()))[-limit:]
|
| 81 |
+
|
| 82 |
+
if not candles:
|
| 83 |
+
return pd.DataFrame()
|
| 84 |
+
|
| 85 |
+
df = pd.DataFrame(candles)
|
| 86 |
+
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
|
| 87 |
+
df.set_index('timestamp', inplace=True)
|
| 88 |
+
|
| 89 |
+
return df
|
| 90 |
+
|
| 91 |
+
def calculate_ema(self, symbol, interval="1", period=9):
|
| 92 |
+
key = f"{symbol}_{interval}"
|
| 93 |
+
cache_key = f"ema_{period}"
|
| 94 |
+
|
| 95 |
+
if cache_key in self.indicators_cache[key]:
|
| 96 |
+
return self.indicators_cache[key][cache_key]
|
| 97 |
+
|
| 98 |
+
df = self.get_candles(symbol, interval, limit=period * 2)
|
| 99 |
+
if df.empty or len(df) < period:
|
| 100 |
+
return None
|
| 101 |
+
|
| 102 |
+
ema = df['close'].ewm(span=period, adjust=False).mean()
|
| 103 |
+
self.indicators_cache[key][cache_key] = ema.iloc[-1] if not ema.empty else None
|
| 104 |
+
|
| 105 |
+
return ema.iloc[-1]
|
| 106 |
+
|
| 107 |
+
def calculate_rsi(self, symbol, interval="1", period=14):
|
| 108 |
+
key = f"{symbol}_{interval}"
|
| 109 |
+
cache_key = f"rsi_{period}"
|
| 110 |
+
|
| 111 |
+
if cache_key in self.indicators_cache[key]:
|
| 112 |
+
return self.indicators_cache[key][cache_key]
|
| 113 |
+
|
| 114 |
+
df = self.get_candles(symbol, interval, limit=period * 3)
|
| 115 |
+
if df.empty or len(df) < period + 1:
|
| 116 |
+
return None
|
| 117 |
+
|
| 118 |
+
delta = df['close'].diff()
|
| 119 |
+
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
|
| 120 |
+
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
|
| 121 |
+
|
| 122 |
+
rs = gain / loss
|
| 123 |
+
rsi = 100 - (100 / (1 + rs))
|
| 124 |
+
|
| 125 |
+
self.indicators_cache[key][cache_key] = rsi.iloc[-1] if not rsi.empty else None
|
| 126 |
+
|
| 127 |
+
return rsi.iloc[-1]
|
| 128 |
+
|
| 129 |
+
def calculate_adx(self, symbol, interval="1", period=14):
|
| 130 |
+
key = f"{symbol}_{interval}"
|
| 131 |
+
cache_key = f"adx_{period}"
|
| 132 |
+
|
| 133 |
+
if cache_key in self.indicators_cache[key]:
|
| 134 |
+
return self.indicators_cache[key][cache_key]
|
| 135 |
+
|
| 136 |
+
df = self.get_candles(symbol, interval, limit=period * 3)
|
| 137 |
+
if df.empty or len(df) < period + 1:
|
| 138 |
+
return None
|
| 139 |
+
|
| 140 |
+
df['hl'] = df['high'] - df['low']
|
| 141 |
+
df['hc'] = np.abs(df['high'] - df['close'].shift(1))
|
| 142 |
+
df['lc'] = np.abs(df['low'] - df['close'].shift(1))
|
| 143 |
+
df['tr'] = df[['hl', 'hc', 'lc']].max(axis=1)
|
| 144 |
+
|
| 145 |
+
df['dm_plus'] = np.where((df['high'] - df['high'].shift(1)) > (df['low'].shift(1) - df['low']),
|
| 146 |
+
np.maximum(df['high'] - df['high'].shift(1), 0), 0)
|
| 147 |
+
df['dm_minus'] = np.where((df['low'].shift(1) - df['low']) > (df['high'] - df['high'].shift(1)),
|
| 148 |
+
np.maximum(df['low'].shift(1) - df['low'], 0), 0)
|
| 149 |
+
|
| 150 |
+
df['di_plus'] = 100 * (df['dm_plus'].rolling(window=period).mean() / df['tr'].rolling(window=period).mean())
|
| 151 |
+
df['di_minus'] = 100 * (df['dm_minus'].rolling(window=period).mean() / df['tr'].rolling(window=period).mean())
|
| 152 |
+
|
| 153 |
+
df['dx'] = 100 * np.abs(df['di_plus'] - df['di_minus']) / (df['di_plus'] + df['di_minus'])
|
| 154 |
+
adx = df['dx'].rolling(window=period).mean()
|
| 155 |
+
|
| 156 |
+
self.indicators_cache[key][cache_key] = adx.iloc[-1] if not adx.empty else None
|
| 157 |
+
|
| 158 |
+
return adx.iloc[-1]
|
| 159 |
+
|
| 160 |
+
def get_orderbook_imbalance(self, symbol, orderbook_data=None):
|
| 161 |
+
if orderbook_data is None:
|
| 162 |
+
|
| 163 |
+
return 0.0
|
| 164 |
+
|
| 165 |
+
bids = orderbook_data.get('b', [])
|
| 166 |
+
asks = orderbook_data.get('a', [])
|
| 167 |
+
|
| 168 |
+
bid_volume = sum(float(bid[1]) for bid in bids[:10])
|
| 169 |
+
ask_volume = sum(float(ask[1]) for ask in asks[:10])
|
| 170 |
+
|
| 171 |
+
total_volume = bid_volume + ask_volume
|
| 172 |
+
if total_volume == 0:
|
| 173 |
+
return 0.0
|
| 174 |
+
|
| 175 |
+
imbalance = (bid_volume - ask_volume) / total_volume
|
| 176 |
+
|
| 177 |
+
return imbalance
|
| 178 |
+
|
| 179 |
+
def get_spread(self, symbol, orderbook_data=None):
|
| 180 |
+
if orderbook_data is None:
|
| 181 |
+
return 0.0
|
| 182 |
+
|
| 183 |
+
bids = orderbook_data.get('b', [])
|
| 184 |
+
asks = orderbook_data.get('a', [])
|
| 185 |
+
|
| 186 |
+
if not bids or not asks:
|
| 187 |
+
return 0.0
|
| 188 |
+
|
| 189 |
+
best_bid = float(bids[0][0])
|
| 190 |
+
best_ask = float(asks[0][0])
|
| 191 |
+
|
| 192 |
+
spread = (best_ask - best_bid) / best_bid
|
| 193 |
+
return spread
|
| 194 |
+
|
| 195 |
+
def detect_volume_spike(self, symbol, interval="1", threshold=2.0):
|
| 196 |
+
df = self.get_candles(symbol, interval, limit=20)
|
| 197 |
+
if df.empty or len(df) < 5:
|
| 198 |
+
return False
|
| 199 |
+
|
| 200 |
+
current_volume = df['volume'].iloc[-1]
|
| 201 |
+
avg_volume = df['volume'].iloc[:-1].mean()
|
| 202 |
+
|
| 203 |
+
if avg_volume == 0:
|
| 204 |
+
return False
|
| 205 |
+
|
| 206 |
+
return (current_volume / avg_volume) > threshold
|
| 207 |
+
|
| 208 |
+
def get_price_change_rate(self, symbol, periods=5):
|
| 209 |
+
prices = self.get_prices(symbol, limit=periods + 1)
|
| 210 |
+
if len(prices) < periods + 1:
|
| 211 |
+
return 0.0
|
| 212 |
+
|
| 213 |
+
old_price = prices[0]
|
| 214 |
+
new_price = prices[-1]
|
| 215 |
+
|
| 216 |
+
if old_price == 0:
|
| 217 |
+
return 0.0
|
| 218 |
+
|
| 219 |
+
return (new_price - old_price) / old_price
|
| 220 |
+
|
| 221 |
+
def clear_cache(self, symbol=None, interval=None):
|
| 222 |
+
if symbol and interval:
|
| 223 |
+
key = f"{symbol}_{interval}"
|
| 224 |
+
if key in self.indicators_cache:
|
| 225 |
+
self.indicators_cache[key] = {}
|
| 226 |
+
elif symbol:
|
| 227 |
+
|
| 228 |
+
for key in list(self.indicators_cache.keys()):
|
| 229 |
+
if key.startswith(f"{symbol}_"):
|
| 230 |
+
self.indicators_cache[key] = {}
|
| 231 |
+
else:
|
| 232 |
+
|
| 233 |
+
self.indicators_cache = {}
|
| 234 |
+
|
| 235 |
+
def get_buffer_status(self):
|
| 236 |
+
status = {
|
| 237 |
+
'price_buffers': {symbol: len(buffer) for symbol, buffer in self.price_buffers.items()},
|
| 238 |
+
'candle_buffers': {key: len(buffer) for key, buffer in self.candle_buffers.items()},
|
| 239 |
+
'indicators_cache': {key: list(cache.keys()) for key, cache in self.indicators_cache.items()}
|
| 240 |
+
}
|
| 241 |
+
return status
|
core/exchange.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pybit.unified_trading import HTTP
|
| 2 |
+
from dotenv import load_dotenv
|
| 3 |
+
import os
|
| 4 |
+
import yaml
|
| 5 |
+
import time
|
| 6 |
+
import logging
|
| 7 |
+
|
| 8 |
+
load_dotenv()
|
| 9 |
+
logger = logging.getLogger(__name__)
|
| 10 |
+
|
| 11 |
+
class BybitExchange:
|
| 12 |
+
def __init__(self):
|
| 13 |
+
self.settings = yaml.safe_load(open("config/settings.yaml"))
|
| 14 |
+
|
| 15 |
+
API_KEY = os.getenv("BYBIT_API_KEY")
|
| 16 |
+
API_SECRET = os.getenv("BYBIT_API_SECRET")
|
| 17 |
+
TESTNET = os.getenv("BYBIT_TESTNET", "true").lower() == "true"
|
| 18 |
+
|
| 19 |
+
if not API_KEY or not API_SECRET:
|
| 20 |
+
raise Exception("API keys missing in .env")
|
| 21 |
+
|
| 22 |
+
self.session = HTTP(
|
| 23 |
+
api_key=API_KEY,
|
| 24 |
+
api_secret=API_SECRET,
|
| 25 |
+
testnet=TESTNET
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
self.max_retries = 3
|
| 29 |
+
self.retry_delay = 1
|
| 30 |
+
|
| 31 |
+
def _retry_request(self, func, *args, **kwargs):
|
| 32 |
+
for attempt in range(self.max_retries):
|
| 33 |
+
try:
|
| 34 |
+
response = func(*args, **kwargs)
|
| 35 |
+
if "retCode" in response and response["retCode"] == 0:
|
| 36 |
+
return response
|
| 37 |
+
else:
|
| 38 |
+
logger.warning(f"API call failed: {response}")
|
| 39 |
+
if attempt < self.max_retries - 1:
|
| 40 |
+
time.sleep(self.retry_delay * (attempt + 1))
|
| 41 |
+
continue
|
| 42 |
+
raise Exception(f"API call failed after {self.max_retries} attempts: {response}")
|
| 43 |
+
except Exception as e:
|
| 44 |
+
logger.error(f"API request error: {e}")
|
| 45 |
+
if attempt < self.max_retries - 1:
|
| 46 |
+
time.sleep(self.retry_delay * (attempt + 1))
|
| 47 |
+
continue
|
| 48 |
+
raise e
|
| 49 |
+
|
| 50 |
+
def get_balance(self):
|
| 51 |
+
response = self._retry_request(self.session.get_wallet_balance, accountType="UNIFIED")
|
| 52 |
+
return response["result"]["list"][0]["coin"]
|
| 53 |
+
|
| 54 |
+
def get_positions(self, symbol=None):
|
| 55 |
+
params = {"category": "linear"}
|
| 56 |
+
if symbol:
|
| 57 |
+
params["symbol"] = symbol
|
| 58 |
+
|
| 59 |
+
response = self._retry_request(self.session.get_positions, **params)
|
| 60 |
+
return response["result"]["list"]
|
| 61 |
+
|
| 62 |
+
def set_leverage(self, symbol, leverage=None):
|
| 63 |
+
lev = leverage or self.settings["trading"]["leverage"]
|
| 64 |
+
response = self._retry_request(
|
| 65 |
+
self.session.set_leverage,
|
| 66 |
+
category="linear",
|
| 67 |
+
symbol=symbol,
|
| 68 |
+
buyLeverage=str(lev),
|
| 69 |
+
sellLeverage=str(lev)
|
| 70 |
+
)
|
| 71 |
+
return response
|
| 72 |
+
|
| 73 |
+
def market_order(self, symbol, side, qty, reduce_only=False):
|
| 74 |
+
params = {
|
| 75 |
+
"category": "linear",
|
| 76 |
+
"symbol": symbol,
|
| 77 |
+
"side": side.upper(),
|
| 78 |
+
"orderType": "Market",
|
| 79 |
+
"qty": str(qty)
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
if reduce_only:
|
| 83 |
+
params["reduceOnly"] = True
|
| 84 |
+
|
| 85 |
+
response = self._retry_request(self.session.place_order, **params)
|
| 86 |
+
return response["result"]
|
| 87 |
+
|
| 88 |
+
def limit_order(self, symbol, side, qty, price, reduce_only=False):
|
| 89 |
+
params = {
|
| 90 |
+
"category": "linear",
|
| 91 |
+
"symbol": symbol,
|
| 92 |
+
"side": side.upper(),
|
| 93 |
+
"orderType": "Limit",
|
| 94 |
+
"qty": str(qty),
|
| 95 |
+
"price": str(price)
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
if reduce_only:
|
| 99 |
+
params["reduceOnly"] = True
|
| 100 |
+
|
| 101 |
+
response = self._retry_request(self.session.place_order, **params)
|
| 102 |
+
return response["result"]
|
| 103 |
+
|
| 104 |
+
def set_tp_sl(self, symbol, side, entry_price, tp_price, sl_price):
|
| 105 |
+
response = self._retry_request(
|
| 106 |
+
self.session.set_trading_stop,
|
| 107 |
+
category="linear",
|
| 108 |
+
symbol=symbol,
|
| 109 |
+
takeProfit=str(tp_price),
|
| 110 |
+
stopLoss=str(sl_price),
|
| 111 |
+
tpTriggerBy="LastPrice",
|
| 112 |
+
slTriggerBy="LastPrice"
|
| 113 |
+
)
|
| 114 |
+
return response
|
| 115 |
+
|
| 116 |
+
def cancel_order(self, symbol, order_id=None):
|
| 117 |
+
params = {"category": "linear", "symbol": symbol}
|
| 118 |
+
if order_id:
|
| 119 |
+
params["orderId"] = order_id
|
| 120 |
+
|
| 121 |
+
response = self._retry_request(self.session.cancel_order, **params)
|
| 122 |
+
return response["result"]
|
| 123 |
+
|
| 124 |
+
def cancel_all_orders(self, symbol=None):
|
| 125 |
+
params = {"category": "linear"}
|
| 126 |
+
if symbol:
|
| 127 |
+
params["symbol"] = symbol
|
| 128 |
+
|
| 129 |
+
response = self._retry_request(self.session.cancel_all_orders, **params)
|
| 130 |
+
return response["result"]
|
| 131 |
+
|
| 132 |
+
def close_position(self, symbol):
|
| 133 |
+
positions = self.get_positions(symbol)
|
| 134 |
+
if not positions:
|
| 135 |
+
return None
|
| 136 |
+
|
| 137 |
+
position = positions[0]
|
| 138 |
+
size = float(position["size"])
|
| 139 |
+
|
| 140 |
+
if size > 0:
|
| 141 |
+
|
| 142 |
+
return self.market_order(symbol, "Sell", size, reduce_only=True)
|
| 143 |
+
elif size < 0:
|
| 144 |
+
|
| 145 |
+
return self.market_order(symbol, "Buy", abs(size), reduce_only=True)
|
| 146 |
+
|
| 147 |
+
return None
|
| 148 |
+
|
| 149 |
+
def get_active_orders(self, symbol=None):
|
| 150 |
+
params = {"category": "linear"}
|
| 151 |
+
if symbol:
|
| 152 |
+
params["symbol"] = symbol
|
| 153 |
+
|
| 154 |
+
response = self._retry_request(self.session.get_open_orders, **params)
|
| 155 |
+
return response["result"]["list"]
|
| 156 |
+
|
| 157 |
+
def get_kline_data(self, symbol, interval="1", limit=200):
|
| 158 |
+
response = self._retry_request(
|
| 159 |
+
self.session.get_kline,
|
| 160 |
+
category="linear",
|
| 161 |
+
symbol=symbol,
|
| 162 |
+
interval=interval,
|
| 163 |
+
limit=limit
|
| 164 |
+
)
|
| 165 |
+
return response["result"]["list"]
|
| 166 |
+
|
| 167 |
+
def get_orderbook(self, symbol, limit=25):
|
| 168 |
+
response = self._retry_request(
|
| 169 |
+
self.session.get_orderbook,
|
| 170 |
+
category="linear",
|
| 171 |
+
symbol=symbol,
|
| 172 |
+
limit=limit
|
| 173 |
+
)
|
| 174 |
+
return response["result"]
|
| 175 |
+
|
| 176 |
+
def get_ticker(self, symbol):
|
| 177 |
+
response = self._retry_request(
|
| 178 |
+
self.session.get_tickers,
|
| 179 |
+
category="linear",
|
| 180 |
+
symbol=symbol
|
| 181 |
+
)
|
| 182 |
+
return response["result"]["list"][0]
|
| 183 |
+
|
| 184 |
+
def get_server_time(self):
|
| 185 |
+
response = self._retry_request(self.session.get_server_time)
|
| 186 |
+
return response["result"]["timeSecond"]
|
core/indicators.py
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import numpy as np
|
| 3 |
+
from typing import Optional, List, Dict, Any
|
| 4 |
+
import logging
|
| 5 |
+
|
| 6 |
+
logger = logging.getLogger(__name__)
|
| 7 |
+
|
| 8 |
+
class TechnicalIndicators:
|
| 9 |
+
def __init__(self):
|
| 10 |
+
pass
|
| 11 |
+
|
| 12 |
+
@staticmethod
|
| 13 |
+
def sma(data: pd.Series, period: int) -> pd.Series:
|
| 14 |
+
return data.rolling(window=period).mean()
|
| 15 |
+
|
| 16 |
+
@staticmethod
|
| 17 |
+
def ema(data: pd.Series, period: int) -> pd.Series:
|
| 18 |
+
return data.ewm(span=period, adjust=False).mean()
|
| 19 |
+
|
| 20 |
+
@staticmethod
|
| 21 |
+
def rsi(data: pd.Series, period: int = 14) -> pd.Series:
|
| 22 |
+
delta = data.diff()
|
| 23 |
+
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
|
| 24 |
+
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
|
| 25 |
+
|
| 26 |
+
rs = gain / loss
|
| 27 |
+
rsi = 100 - (100 / (1 + rs))
|
| 28 |
+
|
| 29 |
+
return rsi
|
| 30 |
+
|
| 31 |
+
@staticmethod
|
| 32 |
+
def macd(data: pd.Series, fast_period: int = 12, slow_period: int = 26, signal_period: int = 9) -> Dict[str, pd.Series]:
|
| 33 |
+
fast_ema = TechnicalIndicators.ema(data, fast_period)
|
| 34 |
+
slow_ema = TechnicalIndicators.ema(data, slow_period)
|
| 35 |
+
|
| 36 |
+
macd_line = fast_ema - slow_ema
|
| 37 |
+
signal_line = TechnicalIndicators.ema(macd_line, signal_period)
|
| 38 |
+
histogram = macd_line - signal_line
|
| 39 |
+
|
| 40 |
+
return {
|
| 41 |
+
'macd': macd_line,
|
| 42 |
+
'signal': signal_line,
|
| 43 |
+
'histogram': histogram
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
@staticmethod
|
| 47 |
+
def bollinger_bands(data: pd.Series, period: int = 20, std_dev: float = 2.0) -> Dict[str, pd.Series]:
|
| 48 |
+
sma = TechnicalIndicators.sma(data, period)
|
| 49 |
+
std = data.rolling(window=period).std()
|
| 50 |
+
|
| 51 |
+
upper_band = sma + (std * std_dev)
|
| 52 |
+
lower_band = sma - (std * std_dev)
|
| 53 |
+
|
| 54 |
+
return {
|
| 55 |
+
'upper': upper_band,
|
| 56 |
+
'middle': sma,
|
| 57 |
+
'lower': lower_band
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
@staticmethod
|
| 61 |
+
def adx(high: pd.Series, low: pd.Series, close: pd.Series, period: int = 14) -> pd.Series:
|
| 62 |
+
|
| 63 |
+
hl = high - low
|
| 64 |
+
hc = np.abs(high - close.shift(1))
|
| 65 |
+
lc = np.abs(low - close.shift(1))
|
| 66 |
+
tr = pd.concat([hl, hc, lc], axis=1).max(axis=1)
|
| 67 |
+
|
| 68 |
+
dm_plus = np.where((high - high.shift(1)) > (low.shift(1) - low),
|
| 69 |
+
np.maximum(high - high.shift(1), 0), 0)
|
| 70 |
+
dm_minus = np.where((low.shift(1) - low) > (high - high.shift(1)),
|
| 71 |
+
np.maximum(low.shift(1) - low), 0)
|
| 72 |
+
|
| 73 |
+
tr_smooth = tr.rolling(window=period).mean()
|
| 74 |
+
dm_plus_smooth = pd.Series(dm_plus).rolling(window=period).mean()
|
| 75 |
+
dm_minus_smooth = pd.Series(dm_minus).rolling(window=period).mean()
|
| 76 |
+
|
| 77 |
+
di_plus = 100 * (dm_plus_smooth / tr_smooth)
|
| 78 |
+
di_minus = 100 * (dm_minus_smooth / tr_smooth)
|
| 79 |
+
|
| 80 |
+
dx = 100 * np.abs(di_plus - di_minus) / (di_plus + di_minus)
|
| 81 |
+
adx = dx.rolling(window=period).mean()
|
| 82 |
+
|
| 83 |
+
return adx
|
| 84 |
+
|
| 85 |
+
@staticmethod
|
| 86 |
+
def stochastic_oscillator(high: pd.Series, low: pd.Series, close: pd.Series,
|
| 87 |
+
k_period: int = 14, d_period: int = 3) -> Dict[str, pd.Series]:
|
| 88 |
+
lowest_low = low.rolling(window=k_period).min()
|
| 89 |
+
highest_high = high.rolling(window=k_period).max()
|
| 90 |
+
|
| 91 |
+
k_percent = 100 * ((close - lowest_low) / (highest_high - lowest_low))
|
| 92 |
+
d_percent = k_percent.rolling(window=d_period).mean()
|
| 93 |
+
|
| 94 |
+
return {
|
| 95 |
+
'k': k_percent,
|
| 96 |
+
'd': d_percent
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
@staticmethod
|
| 100 |
+
def williams_r(high: pd.Series, low: pd.Series, close: pd.Series, period: int = 14) -> pd.Series:
|
| 101 |
+
highest_high = high.rolling(window=period).max()
|
| 102 |
+
lowest_low = low.rolling(window=period).min()
|
| 103 |
+
|
| 104 |
+
williams_r = -100 * ((highest_high - close) / (highest_high - lowest_low))
|
| 105 |
+
|
| 106 |
+
return williams_r
|
| 107 |
+
|
| 108 |
+
@staticmethod
|
| 109 |
+
def cci(high: pd.Series, low: pd.Series, close: pd.Series, period: int = 20) -> pd.Series:
|
| 110 |
+
typical_price = (high + low + close) / 3
|
| 111 |
+
sma_tp = TechnicalIndicators.sma(typical_price, period)
|
| 112 |
+
mad = lambda x: np.mean(np.abs(x - x.mean()))
|
| 113 |
+
mean_deviation = typical_price.rolling(window=period).apply(mad, raw=False)
|
| 114 |
+
|
| 115 |
+
cci = (typical_price - sma_tp) / (0.015 * mean_deviation)
|
| 116 |
+
|
| 117 |
+
return cci
|
| 118 |
+
|
| 119 |
+
@staticmethod
|
| 120 |
+
def atr(high: pd.Series, low: pd.Series, close: pd.Series, period: int = 14) -> pd.Series:
|
| 121 |
+
hl = high - low
|
| 122 |
+
hc = np.abs(high - close.shift(1))
|
| 123 |
+
lc = np.abs(low - close.shift(1))
|
| 124 |
+
tr = pd.concat([hl, hc, lc], axis=1).max(axis=1)
|
| 125 |
+
|
| 126 |
+
atr = tr.rolling(window=period).mean()
|
| 127 |
+
|
| 128 |
+
return atr
|
| 129 |
+
|
| 130 |
+
@staticmethod
|
| 131 |
+
def ichimoku_cloud(high: pd.Series, low: pd.Series, conversion_period: int = 9,
|
| 132 |
+
base_period: int = 26, span_b_period: int = 52,
|
| 133 |
+
lagging_period: int = 26) -> Dict[str, pd.Series]:
|
| 134 |
+
|
| 135 |
+
conversion_line = (high.rolling(window=conversion_period).max() +
|
| 136 |
+
low.rolling(window=conversion_period).min()) / 2
|
| 137 |
+
|
| 138 |
+
base_line = (high.rolling(window=base_period).max() +
|
| 139 |
+
low.rolling(window=base_period).min()) / 2
|
| 140 |
+
|
| 141 |
+
leading_span_a = ((conversion_line + base_line) / 2).shift(lagging_period)
|
| 142 |
+
|
| 143 |
+
leading_span_b = ((high.rolling(window=span_b_period).max() +
|
| 144 |
+
low.rolling(window=span_b_period).min()) / 2).shift(lagging_period)
|
| 145 |
+
|
| 146 |
+
lagging_span = close.shift(-lagging_period)
|
| 147 |
+
|
| 148 |
+
return {
|
| 149 |
+
'conversion_line': conversion_line,
|
| 150 |
+
'base_line': base_line,
|
| 151 |
+
'leading_span_a': leading_span_a,
|
| 152 |
+
'leading_span_b': leading_span_b,
|
| 153 |
+
'lagging_span': lagging_span
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
@staticmethod
|
| 157 |
+
def fibonacci_retracements(high: pd.Series, low: pd.Series) -> Dict[str, float]:
|
| 158 |
+
max_high = high.max()
|
| 159 |
+
min_low = low.min()
|
| 160 |
+
diff = max_high - min_low
|
| 161 |
+
|
| 162 |
+
levels = {
|
| 163 |
+
'0.0': max_high,
|
| 164 |
+
'0.236': max_high - 0.236 * diff,
|
| 165 |
+
'0.382': max_high - 0.382 * diff,
|
| 166 |
+
'0.5': max_high - 0.5 * diff,
|
| 167 |
+
'0.618': max_high - 0.618 * diff,
|
| 168 |
+
'0.786': max_high - 0.786 * diff,
|
| 169 |
+
'1.0': min_low
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
return levels
|
| 173 |
+
|
| 174 |
+
@staticmethod
|
| 175 |
+
def detect_crossover(fast_series: pd.Series, slow_series: pd.Series) -> Dict[str, bool]:
|
| 176 |
+
|
| 177 |
+
crossed_above = (fast_series.shift(1) <= slow_series.shift(1)) & (fast_series > slow_series)
|
| 178 |
+
|
| 179 |
+
crossed_below = (fast_series.shift(1) >= slow_series.shift(1)) & (fast_series < slow_series)
|
| 180 |
+
|
| 181 |
+
return {
|
| 182 |
+
'crossed_above': crossed_above.iloc[-1] if not crossed_above.empty else False,
|
| 183 |
+
'crossed_below': crossed_below.iloc[-1] if not crossed_below.empty else False
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
@staticmethod
|
| 187 |
+
def volume_profile(volume: pd.Series, price: pd.Series, bins: int = 50) -> Dict[str, Any]:
|
| 188 |
+
|
| 189 |
+
price_min, price_max = price.min(), price.max()
|
| 190 |
+
price_bins = np.linspace(price_min, price_max, bins)
|
| 191 |
+
|
| 192 |
+
volume_profile = []
|
| 193 |
+
for i in range(len(price_bins) - 1):
|
| 194 |
+
mask = (price >= price_bins[i]) & (price < price_bins[i + 1])
|
| 195 |
+
bin_volume = volume[mask].sum()
|
| 196 |
+
volume_profile.append({
|
| 197 |
+
'price_level': (price_bins[i] + price_bins[i + 1]) / 2,
|
| 198 |
+
'volume': bin_volume
|
| 199 |
+
})
|
| 200 |
+
|
| 201 |
+
poc = max(volume_profile, key=lambda x: x['volume'])['price_level']
|
| 202 |
+
|
| 203 |
+
total_volume = sum(x['volume'] for x in volume_profile)
|
| 204 |
+
sorted_profile = sorted(volume_profile, key=lambda x: x['volume'], reverse=True)
|
| 205 |
+
|
| 206 |
+
cumulative_volume = 0
|
| 207 |
+
value_area_high = None
|
| 208 |
+
value_area_low = None
|
| 209 |
+
|
| 210 |
+
for level in sorted_profile:
|
| 211 |
+
cumulative_volume += level['volume']
|
| 212 |
+
if cumulative_volume >= total_volume * 0.7:
|
| 213 |
+
break
|
| 214 |
+
|
| 215 |
+
in_value_area = sorted([x for x in volume_profile if x['volume'] > 0],
|
| 216 |
+
key=lambda x: x['volume'], reverse=True)[:len([x for x in sorted_profile if cumulative_volume >= total_volume * 0.7])]
|
| 217 |
+
|
| 218 |
+
if in_value_area:
|
| 219 |
+
prices_in_va = [x['price_level'] for x in in_value_area]
|
| 220 |
+
value_area_high = max(prices_in_va)
|
| 221 |
+
value_area_low = min(prices_in_va)
|
| 222 |
+
|
| 223 |
+
return {
|
| 224 |
+
'volume_profile': volume_profile,
|
| 225 |
+
'poc': poc,
|
| 226 |
+
'value_area_high': value_area_high,
|
| 227 |
+
'value_area_low': value_area_low
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
@staticmethod
|
| 231 |
+
def order_flow_imbalance(orderbook: Dict) -> Dict[str, float]:
|
| 232 |
+
if not orderbook or 'b' not in orderbook or 'a' not in orderbook:
|
| 233 |
+
return {'imbalance': 0.0, 'bid_pressure': 0.0, 'ask_pressure': 0.0}
|
| 234 |
+
|
| 235 |
+
bids = orderbook['b'][:10]
|
| 236 |
+
asks = orderbook['a'][:10]
|
| 237 |
+
|
| 238 |
+
bid_volume = sum(float(bid[1]) for bid in bids)
|
| 239 |
+
ask_volume = sum(float(ask[1]) for ask in asks)
|
| 240 |
+
|
| 241 |
+
total_volume = bid_volume + ask_volume
|
| 242 |
+
|
| 243 |
+
if total_volume == 0:
|
| 244 |
+
return {'imbalance': 0.0, 'bid_pressure': 0.0, 'ask_pressure': 0.0}
|
| 245 |
+
|
| 246 |
+
imbalance = (bid_volume - ask_volume) / total_volume
|
| 247 |
+
bid_pressure = bid_volume / total_volume
|
| 248 |
+
ask_pressure = ask_volume / total_volume
|
| 249 |
+
|
| 250 |
+
return {
|
| 251 |
+
'imbalance': imbalance,
|
| 252 |
+
'bid_pressure': bid_pressure,
|
| 253 |
+
'ask_pressure': ask_pressure
|
| 254 |
+
}
|
core/risk.py
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from typing import Dict, List, Optional, Tuple, Any
|
| 3 |
+
import yaml
|
| 4 |
+
import time
|
| 5 |
+
from datetime import datetime, timedelta
|
| 6 |
+
|
| 7 |
+
logger = logging.getLogger(__name__)
|
| 8 |
+
|
| 9 |
+
class RiskManager:
|
| 10 |
+
def __init__(self, exchange_client):
|
| 11 |
+
self.exchange = exchange_client
|
| 12 |
+
self.settings = yaml.safe_load(open("config/settings.yaml"))
|
| 13 |
+
self.leverage = self.settings["trading"]["leverage"]
|
| 14 |
+
self.tp_percent = self.settings["trading"]["tp_percent"]
|
| 15 |
+
self.sl_percent = self.settings["trading"]["sl_percent"]
|
| 16 |
+
self.risk_per_trade = self.settings["risk"]["risk_per_trade"]
|
| 17 |
+
self.max_daily_loss = 100
|
| 18 |
+
self.daily_pnl = 0.0
|
| 19 |
+
self.daily_trades = 0
|
| 20 |
+
self.max_daily_trades = 50
|
| 21 |
+
self.max_open_positions = 1
|
| 22 |
+
self.min_order_size = 0.001
|
| 23 |
+
self.open_positions = {}
|
| 24 |
+
self.emergency_stop = False
|
| 25 |
+
self.last_reset_time = datetime.now()
|
| 26 |
+
self.trade_history = []
|
| 27 |
+
self.win_count = 0
|
| 28 |
+
self.loss_count = 0
|
| 29 |
+
|
| 30 |
+
def calculate_position_size(self, symbol: str, entry_price: float, side: str) -> float:
|
| 31 |
+
try:
|
| 32 |
+
balance_data = self.exchange.get_balance()
|
| 33 |
+
if not balance_data:
|
| 34 |
+
logger.error("Failed to get account balance")
|
| 35 |
+
return 0.0
|
| 36 |
+
usdt_balance = 0.0
|
| 37 |
+
for coin in balance_data:
|
| 38 |
+
if coin.get("coin") == "USDT":
|
| 39 |
+
usdt_balance = float(coin.get("walletBalance", 0))
|
| 40 |
+
break
|
| 41 |
+
if usdt_balance <= 0:
|
| 42 |
+
logger.warning("Insufficient USDT balance")
|
| 43 |
+
return 0.0
|
| 44 |
+
risk_amount = usdt_balance * self.risk_per_trade
|
| 45 |
+
sl_distance = entry_price * self.sl_percent
|
| 46 |
+
position_size = risk_amount / (sl_distance * self.leverage)
|
| 47 |
+
position_size = max(position_size, self.min_order_size)
|
| 48 |
+
if self.daily_pnl < -self.max_daily_loss:
|
| 49 |
+
logger.warning(f"Daily loss limit reached: {self.daily_pnl}")
|
| 50 |
+
return 0.0
|
| 51 |
+
if self.daily_trades >= self.max_daily_trades:
|
| 52 |
+
logger.warning("Max daily trades reached")
|
| 53 |
+
return 0.0
|
| 54 |
+
if symbol in self.open_positions:
|
| 55 |
+
logger.warning(f"Position already open for {symbol}")
|
| 56 |
+
return 0.0
|
| 57 |
+
position_size = self._validate_position_size(symbol, position_size, entry_price)
|
| 58 |
+
logger.info(f"Calculated position size: {position_size:.4f} for {symbol}")
|
| 59 |
+
return position_size
|
| 60 |
+
except Exception as e:
|
| 61 |
+
logger.error(f"Error calculating position size: {e}")
|
| 62 |
+
return 0.0
|
| 63 |
+
|
| 64 |
+
def _validate_position_size(self, symbol: str, size: float, price: float) -> float:
|
| 65 |
+
try:
|
| 66 |
+
min_size = 0.001
|
| 67 |
+
max_size = 100.0
|
| 68 |
+
validated_size = max(min_size, min(size, max_size))
|
| 69 |
+
if symbol.startswith('BTC'):
|
| 70 |
+
validated_size = round(validated_size, 3)
|
| 71 |
+
elif symbol.startswith('ETH'):
|
| 72 |
+
validated_size = round(validated_size, 2)
|
| 73 |
+
else:
|
| 74 |
+
validated_size = round(validated_size, 1)
|
| 75 |
+
return validated_size
|
| 76 |
+
except Exception as e:
|
| 77 |
+
logger.error(f"Error validating position size: {e}")
|
| 78 |
+
return 0.0
|
| 79 |
+
|
| 80 |
+
def validate_entry_signal(self, symbol: str, signal: str, confidence: float) -> bool:
|
| 81 |
+
try:
|
| 82 |
+
if self.emergency_stop:
|
| 83 |
+
logger.warning("Emergency stop activated")
|
| 84 |
+
return False
|
| 85 |
+
min_confidence = 0.6
|
| 86 |
+
if confidence < min_confidence:
|
| 87 |
+
logger.info(f"Signal confidence too low: {confidence}")
|
| 88 |
+
return False
|
| 89 |
+
if symbol in self.open_positions:
|
| 90 |
+
logger.warning(f"Position already exists for {symbol}")
|
| 91 |
+
return False
|
| 92 |
+
if not self._check_market_conditions(symbol):
|
| 93 |
+
return False
|
| 94 |
+
if self._is_high_volatility(symbol):
|
| 95 |
+
logger.warning(f"High volatility detected for {symbol}")
|
| 96 |
+
return False
|
| 97 |
+
return True
|
| 98 |
+
except Exception as e:
|
| 99 |
+
logger.error(f"Error validating entry signal: {e}")
|
| 100 |
+
return False
|
| 101 |
+
|
| 102 |
+
def _check_market_conditions(self, symbol: str) -> bool:
|
| 103 |
+
try:
|
| 104 |
+
ticker = self.exchange.get_ticker(symbol)
|
| 105 |
+
if not ticker:
|
| 106 |
+
return False
|
| 107 |
+
volume_24h = float(ticker.get("volume24h", 0))
|
| 108 |
+
if volume_24h < 100000:
|
| 109 |
+
logger.warning(f"Low volume for {symbol}: {volume_24h}")
|
| 110 |
+
return False
|
| 111 |
+
bid_price = float(ticker.get("bid1Price", 0))
|
| 112 |
+
ask_price = float(ticker.get("ask1Price", 0))
|
| 113 |
+
if bid_price == 0 or ask_price == 0:
|
| 114 |
+
return False
|
| 115 |
+
spread = (ask_price - bid_price) / bid_price
|
| 116 |
+
max_spread = 0.001
|
| 117 |
+
if spread > max_spread:
|
| 118 |
+
logger.warning(f"Spread too wide for {symbol}: {spread}")
|
| 119 |
+
return False
|
| 120 |
+
return True
|
| 121 |
+
except Exception as e:
|
| 122 |
+
logger.error(f"Error checking market conditions: {e}")
|
| 123 |
+
return False
|
| 124 |
+
|
| 125 |
+
def _is_high_volatility(self, symbol: str) -> bool:
|
| 126 |
+
try:
|
| 127 |
+
prices = self.exchange.get_kline_data(symbol, interval="1", limit=10)
|
| 128 |
+
if not prices or len(prices) < 5:
|
| 129 |
+
return True
|
| 130 |
+
high = max(float(candle[2]) for candle in prices)
|
| 131 |
+
low = min(float(candle[3]) for candle in prices)
|
| 132 |
+
current_price = float(prices[-1][4])
|
| 133 |
+
volatility = (high - low) / current_price
|
| 134 |
+
high_vol_threshold = 0.02
|
| 135 |
+
return volatility > high_vol_threshold
|
| 136 |
+
except Exception as e:
|
| 137 |
+
logger.error(f"Error checking volatility: {e}")
|
| 138 |
+
return True
|
| 139 |
+
|
| 140 |
+
def update_position(self, symbol: str, position_data: Dict[str, Any]):
|
| 141 |
+
try:
|
| 142 |
+
self.open_positions[symbol] = {
|
| 143 |
+
'entry_time': datetime.now(),
|
| 144 |
+
'entry_price': float(position_data.get('avgPrice', 0)),
|
| 145 |
+
'size': float(position_data.get('qty', 0)),
|
| 146 |
+
'side': position_data.get('side', ''),
|
| 147 |
+
'leverage': self.leverage
|
| 148 |
+
}
|
| 149 |
+
logger.info(f"Position updated for {symbol}: {self.open_positions[symbol]}")
|
| 150 |
+
except Exception as e:
|
| 151 |
+
logger.error(f"Error updating position for {symbol}: {e}")
|
| 152 |
+
|
| 153 |
+
def close_position(self, symbol: str, reason: str = "manual"):
|
| 154 |
+
try:
|
| 155 |
+
if symbol not in self.open_positions:
|
| 156 |
+
logger.warning(f"No position found for {symbol}")
|
| 157 |
+
return False
|
| 158 |
+
position = self.open_positions[symbol]
|
| 159 |
+
exit_price = self.exchange.get_ticker(symbol)
|
| 160 |
+
if exit_price:
|
| 161 |
+
exit_price = float(exit_price.get("lastPrice", 0))
|
| 162 |
+
entry_price = position['entry_price']
|
| 163 |
+
if position['side'] == 'Buy':
|
| 164 |
+
pnl = (exit_price - entry_price) / entry_price * position['size'] * self.leverage
|
| 165 |
+
else:
|
| 166 |
+
pnl = (entry_price - exit_price) / entry_price * position['size'] * self.leverage
|
| 167 |
+
self.daily_pnl += pnl
|
| 168 |
+
if pnl > 0:
|
| 169 |
+
self.win_count += 1
|
| 170 |
+
else:
|
| 171 |
+
self.loss_count += 1
|
| 172 |
+
self._record_trade(symbol, position, exit_price, pnl, reason)
|
| 173 |
+
del self.open_positions[symbol]
|
| 174 |
+
self.daily_trades += 1
|
| 175 |
+
logger.info(f"Position closed for {symbol}, reason: {reason}")
|
| 176 |
+
return True
|
| 177 |
+
except Exception as e:
|
| 178 |
+
logger.error(f"Error closing position for {symbol}: {e}")
|
| 179 |
+
return False
|
| 180 |
+
|
| 181 |
+
def _record_trade(self, symbol: str, position: Dict, exit_price: float, pnl: float, reason: str):
|
| 182 |
+
try:
|
| 183 |
+
trade = {
|
| 184 |
+
'symbol': symbol,
|
| 185 |
+
'entry_time': position['entry_time'],
|
| 186 |
+
'exit_time': datetime.now(),
|
| 187 |
+
'entry_price': position['entry_price'],
|
| 188 |
+
'exit_price': exit_price,
|
| 189 |
+
'size': position['size'],
|
| 190 |
+
'side': position['side'],
|
| 191 |
+
'pnl': pnl,
|
| 192 |
+
'reason': reason,
|
| 193 |
+
'leverage': position['leverage']
|
| 194 |
+
}
|
| 195 |
+
self.trade_history.append(trade)
|
| 196 |
+
if len(self.trade_history) > 1000:
|
| 197 |
+
self.trade_history = self.trade_history[-1000:]
|
| 198 |
+
except Exception as e:
|
| 199 |
+
logger.error(f"Error recording trade: {e}")
|
| 200 |
+
|
| 201 |
+
def check_tp_sl_hit(self, symbol: str) -> Optional[str]:
|
| 202 |
+
try:
|
| 203 |
+
if symbol not in self.open_positions:
|
| 204 |
+
return None
|
| 205 |
+
position = self.open_positions[symbol]
|
| 206 |
+
current_price = self.exchange.get_ticker(symbol)
|
| 207 |
+
if not current_price:
|
| 208 |
+
return None
|
| 209 |
+
current_price = float(current_price.get("lastPrice", 0))
|
| 210 |
+
entry_price = position['entry_price']
|
| 211 |
+
if position['side'] == 'Buy':
|
| 212 |
+
tp_price = entry_price * (1 + self.tp_percent)
|
| 213 |
+
sl_price = entry_price * (1 - self.sl_percent)
|
| 214 |
+
if current_price >= tp_price:
|
| 215 |
+
return "TP"
|
| 216 |
+
elif current_price <= sl_price:
|
| 217 |
+
return "SL"
|
| 218 |
+
else:
|
| 219 |
+
tp_price = entry_price * (1 - self.tp_percent)
|
| 220 |
+
sl_price = entry_price * (1 + self.sl_percent)
|
| 221 |
+
if current_price <= tp_price:
|
| 222 |
+
return "TP"
|
| 223 |
+
elif current_price >= sl_price:
|
| 224 |
+
return "SL"
|
| 225 |
+
return None
|
| 226 |
+
except Exception as e:
|
| 227 |
+
logger.error(f"Error checking TP/SL for {symbol}: {e}")
|
| 228 |
+
return None
|
| 229 |
+
|
| 230 |
+
def emergency_stop_all(self):
|
| 231 |
+
try:
|
| 232 |
+
self.emergency_stop = True
|
| 233 |
+
for symbol in list(self.open_positions.keys()):
|
| 234 |
+
self.exchange.close_position(symbol)
|
| 235 |
+
self.close_position(symbol, "emergency_stop")
|
| 236 |
+
logger.critical("Emergency stop activated - all positions closed")
|
| 237 |
+
except Exception as e:
|
| 238 |
+
logger.error(f"Error in emergency stop: {e}")
|
| 239 |
+
|
| 240 |
+
def reset_daily_stats(self):
|
| 241 |
+
try:
|
| 242 |
+
now = datetime.now()
|
| 243 |
+
if now.date() > self.last_reset_time.date():
|
| 244 |
+
self.daily_pnl = 0.0
|
| 245 |
+
self.daily_trades = 0
|
| 246 |
+
self.last_reset_time = now
|
| 247 |
+
logger.info("Daily statistics reset")
|
| 248 |
+
except Exception as e:
|
| 249 |
+
logger.error(f"Error resetting daily stats: {e}")
|
| 250 |
+
|
| 251 |
+
def get_risk_status(self) -> Dict[str, Any]:
|
| 252 |
+
try:
|
| 253 |
+
total_trades = self.win_count + self.loss_count
|
| 254 |
+
win_rate = self.win_count / total_trades if total_trades > 0 else 0.0
|
| 255 |
+
status = {
|
| 256 |
+
'emergency_stop': self.emergency_stop,
|
| 257 |
+
'open_positions': len(self.open_positions),
|
| 258 |
+
'daily_pnl': self.daily_pnl,
|
| 259 |
+
'daily_trades': self.daily_trades,
|
| 260 |
+
'win_rate': win_rate,
|
| 261 |
+
'total_trades': total_trades,
|
| 262 |
+
'positions': list(self.open_positions.keys())
|
| 263 |
+
}
|
| 264 |
+
return status
|
| 265 |
+
except Exception as e:
|
| 266 |
+
logger.error(f"Error getting risk status: {e}")
|
| 267 |
+
return {'error': str(e)}
|
core/strategy.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import numpy as np
|
| 3 |
+
import logging
|
| 4 |
+
from typing import Dict, List, Optional, Tuple, Any
|
| 5 |
+
from core.indicators import TechnicalIndicators
|
| 6 |
+
from core.data_engine import DataEngine
|
| 7 |
+
import yaml
|
| 8 |
+
|
| 9 |
+
logger = logging.getLogger(__name__)
|
| 10 |
+
|
| 11 |
+
class ScalpingStrategy:
|
| 12 |
+
def __init__(self, data_engine: DataEngine):
|
| 13 |
+
self.data_engine = data_engine
|
| 14 |
+
self.settings = yaml.safe_load(open("config/settings.yaml"))
|
| 15 |
+
self.ema_fast_period = self.settings["strategy"]["ema_fast"]
|
| 16 |
+
self.ema_slow_period = self.settings["strategy"]["ema_slow"]
|
| 17 |
+
self.rsi_period = self.settings["strategy"]["rsi_period"]
|
| 18 |
+
self.strategy_weights = {
|
| 19 |
+
'ema_momentum': 0.4,
|
| 20 |
+
'breakout': 0.35,
|
| 21 |
+
'pullback': 0.25
|
| 22 |
+
}
|
| 23 |
+
self.min_confidence = 0.6
|
| 24 |
+
|
| 25 |
+
def generate_signal(self, symbol: str, interval: str = "1") -> Tuple[str, float, float]:
|
| 26 |
+
try:
|
| 27 |
+
current_price = self.data_engine.get_prices(symbol, limit=1)
|
| 28 |
+
if not current_price:
|
| 29 |
+
return "NEUTRAL", 0.0, 0.0
|
| 30 |
+
current_price = current_price[-1]
|
| 31 |
+
ema_signal, ema_conf = self._ema_momentum_strategy(symbol, interval)
|
| 32 |
+
breakout_signal, breakout_conf = self._breakout_strategy(symbol, interval)
|
| 33 |
+
pullback_signal, pullback_conf = self._pullback_strategy(symbol, interval)
|
| 34 |
+
buy_signals = []
|
| 35 |
+
sell_signals = []
|
| 36 |
+
if ema_signal == "BUY":
|
| 37 |
+
buy_signals.append(ema_conf * self.strategy_weights['ema_momentum'])
|
| 38 |
+
elif ema_signal == "SELL":
|
| 39 |
+
sell_signals.append(ema_conf * self.strategy_weights['ema_momentum'])
|
| 40 |
+
if breakout_signal == "BUY":
|
| 41 |
+
buy_signals.append(breakout_conf * self.strategy_weights['breakout'])
|
| 42 |
+
elif breakout_signal == "SELL":
|
| 43 |
+
sell_signals.append(breakout_conf * self.strategy_weights['breakout'])
|
| 44 |
+
if pullback_signal == "BUY":
|
| 45 |
+
buy_signals.append(pullback_conf * self.strategy_weights['pullback'])
|
| 46 |
+
elif pullback_signal == "SELL":
|
| 47 |
+
sell_signals.append(pullback_conf * self.strategy_weights['pullback'])
|
| 48 |
+
buy_strength = sum(buy_signals) if buy_signals else 0.0
|
| 49 |
+
sell_strength = sum(sell_signals) if sell_signals else 0.0
|
| 50 |
+
if buy_strength > self.min_confidence and buy_strength > sell_strength:
|
| 51 |
+
final_confidence = min(buy_strength, 1.0)
|
| 52 |
+
return "BUY", final_confidence, current_price
|
| 53 |
+
elif sell_strength > self.min_confidence and sell_strength > buy_strength:
|
| 54 |
+
final_confidence = min(sell_strength, 1.0)
|
| 55 |
+
return "SELL", final_confidence, current_price
|
| 56 |
+
else:
|
| 57 |
+
return "NEUTRAL", 0.0, current_price
|
| 58 |
+
except Exception as e:
|
| 59 |
+
logger.error(f"Error generating signal for {symbol}: {e}")
|
| 60 |
+
return "NEUTRAL", 0.0, 0.0
|
| 61 |
+
|
| 62 |
+
def _ema_momentum_strategy(self, symbol: str, interval: str) -> Tuple[str, float]:
|
| 63 |
+
try:
|
| 64 |
+
df = self.data_engine.get_candles(symbol, interval, limit=50)
|
| 65 |
+
if df.empty or len(df) < self.ema_slow_period + 5:
|
| 66 |
+
return "NEUTRAL", 0.0
|
| 67 |
+
ema_fast = self.data_engine.calculate_ema(symbol, interval, self.ema_fast_period)
|
| 68 |
+
ema_slow = self.data_engine.calculate_ema(symbol, interval, self.ema_slow_period)
|
| 69 |
+
rsi = self.data_engine.calculate_rsi(symbol, interval, self.rsi_period)
|
| 70 |
+
if ema_fast is None or ema_slow is None or rsi is None:
|
| 71 |
+
return "NEUTRAL", 0.0
|
| 72 |
+
ema_fast_prev = df['close'].ewm(span=self.ema_fast_period, adjust=False).mean().iloc[-2]
|
| 73 |
+
ema_slow_prev = df['close'].ewm(span=self.ema_slow_period, adjust=False).mean().iloc[-2]
|
| 74 |
+
crossover_up = ema_fast_prev <= ema_slow_prev and ema_fast > ema_slow
|
| 75 |
+
crossover_down = ema_fast_prev >= ema_slow_prev and ema_fast < ema_slow
|
| 76 |
+
rsi_oversold = rsi < 35
|
| 77 |
+
rsi_overbought = rsi > 65
|
| 78 |
+
orderbook_imbalance = self.data_engine.get_orderbook_imbalance(symbol)
|
| 79 |
+
confidence = 0.0
|
| 80 |
+
signal = "NEUTRAL"
|
| 81 |
+
if crossover_up and rsi_oversold and orderbook_imbalance > 0.1:
|
| 82 |
+
confidence = 0.8
|
| 83 |
+
signal = "BUY"
|
| 84 |
+
elif crossover_down and rsi_overbought and orderbook_imbalance < -0.1:
|
| 85 |
+
confidence = 0.8
|
| 86 |
+
signal = "SELL"
|
| 87 |
+
return signal, confidence
|
| 88 |
+
except Exception as e:
|
| 89 |
+
logger.error(f"EMA momentum strategy error for {symbol}: {e}")
|
| 90 |
+
return "NEUTRAL", 0.0
|
| 91 |
+
|
| 92 |
+
def _breakout_strategy(self, symbol: str, interval: str) -> Tuple[str, float]:
|
| 93 |
+
try:
|
| 94 |
+
df = self.data_engine.get_candles(symbol, interval, limit=20)
|
| 95 |
+
if df.empty or len(df) < 10:
|
| 96 |
+
return "NEUTRAL", 0.0
|
| 97 |
+
volume_spike = self.data_engine.detect_volume_spike(symbol, interval, threshold=1.8)
|
| 98 |
+
price_change_rate = self.data_engine.get_price_change_rate(symbol, periods=3)
|
| 99 |
+
spread = self.data_engine.get_spread(symbol)
|
| 100 |
+
orderbook_imbalance = self.data_engine.get_orderbook_imbalance(symbol)
|
| 101 |
+
recent_high = df['high'].tail(5).max()
|
| 102 |
+
recent_low = df['low'].tail(5).min()
|
| 103 |
+
current_price = df['close'].iloc[-1]
|
| 104 |
+
breakout_up = current_price > recent_high * 1.001
|
| 105 |
+
breakout_down = current_price < recent_low * 0.999
|
| 106 |
+
confidence = 0.0
|
| 107 |
+
signal = "NEUTRAL"
|
| 108 |
+
if breakout_up and volume_spike and price_change_rate > 0.002 and orderbook_imbalance > 0.15:
|
| 109 |
+
confidence = 0.75
|
| 110 |
+
signal = "BUY"
|
| 111 |
+
elif breakout_down and volume_spike and price_change_rate < -0.002 and orderbook_imbalance < -0.15:
|
| 112 |
+
confidence = 0.75
|
| 113 |
+
signal = "SELL"
|
| 114 |
+
return signal, confidence
|
| 115 |
+
except Exception as e:
|
| 116 |
+
logger.error(f"Breakout strategy error for {symbol}: {e}")
|
| 117 |
+
return "NEUTRAL", 0.0
|
| 118 |
+
|
| 119 |
+
def _pullback_strategy(self, symbol: str, interval: str) -> Tuple[str, float]:
|
| 120 |
+
try:
|
| 121 |
+
df = self.data_engine.get_candles(symbol, interval, limit=30)
|
| 122 |
+
if df.empty or len(df) < 20:
|
| 123 |
+
return "NEUTRAL", 0.0
|
| 124 |
+
ema_21 = df['close'].ewm(span=21, adjust=False).mean()
|
| 125 |
+
ema_slope = ema_21.diff().iloc[-1]
|
| 126 |
+
uptrend = ema_slope > 0
|
| 127 |
+
downtrend = ema_slope < 0
|
| 128 |
+
ema_9 = df['close'].ewm(span=9, adjust=False).mean().iloc[-1]
|
| 129 |
+
current_price = df['close'].iloc[-1]
|
| 130 |
+
price_to_ema_ratio = current_price / ema_9
|
| 131 |
+
recent_volume = df['volume'].tail(5).mean()
|
| 132 |
+
previous_volume = df['volume'].tail(10).head(5).mean()
|
| 133 |
+
volume_trend = recent_volume / previous_volume if previous_volume > 0 else 1.0
|
| 134 |
+
pullback_up = uptrend and price_to_ema_ratio < 0.995
|
| 135 |
+
pullback_down = downtrend and price_to_ema_ratio > 1.005
|
| 136 |
+
volume_decrease_on_pullback = volume_trend < 0.8
|
| 137 |
+
volume_increase_on_continuation = volume_trend > 1.2
|
| 138 |
+
confidence = 0.0
|
| 139 |
+
signal = "NEUTRAL"
|
| 140 |
+
if pullback_up and volume_decrease_on_pullback:
|
| 141 |
+
if volume_increase_on_continuation and current_price > ema_9:
|
| 142 |
+
confidence = 0.7
|
| 143 |
+
signal = "BUY"
|
| 144 |
+
elif pullback_down and volume_decrease_on_pullback:
|
| 145 |
+
if volume_increase_on_continuation and current_price < ema_9:
|
| 146 |
+
confidence = 0.7
|
| 147 |
+
signal = "SELL"
|
| 148 |
+
return signal, confidence
|
| 149 |
+
except Exception as e:
|
| 150 |
+
logger.error(f"Pullback strategy error for {symbol}: {e}")
|
| 151 |
+
return "NEUTRAL", 0.0
|
| 152 |
+
|
| 153 |
+
def get_strategy_status(self, symbol: str) -> Dict[str, Any]:
|
| 154 |
+
try:
|
| 155 |
+
status = {
|
| 156 |
+
'symbol': symbol,
|
| 157 |
+
'timestamp': pd.Timestamp.now(),
|
| 158 |
+
'strategies': {}
|
| 159 |
+
}
|
| 160 |
+
ema_signal, ema_conf = self._ema_momentum_strategy(symbol)
|
| 161 |
+
breakout_signal, breakout_conf = self._breakout_strategy(symbol)
|
| 162 |
+
pullback_signal, pullback_conf = self._pullback_strategy(symbol)
|
| 163 |
+
status['strategies'] = {
|
| 164 |
+
'ema_momentum': {
|
| 165 |
+
'signal': ema_signal,
|
| 166 |
+
'confidence': ema_conf
|
| 167 |
+
},
|
| 168 |
+
'breakout': {
|
| 169 |
+
'signal': breakout_signal,
|
| 170 |
+
'confidence': breakout_conf
|
| 171 |
+
},
|
| 172 |
+
'pullback': {
|
| 173 |
+
'signal': pullback_signal,
|
| 174 |
+
'confidence': pullback_conf
|
| 175 |
+
}
|
| 176 |
+
}
|
| 177 |
+
current_price = self.data_engine.get_prices(symbol, limit=1)
|
| 178 |
+
status['current_price'] = current_price[-1] if current_price else None
|
| 179 |
+
ema_fast = self.data_engine.calculate_ema(symbol, "1", self.ema_fast_period)
|
| 180 |
+
ema_slow = self.data_engine.calculate_ema(symbol, "1", self.ema_slow_period)
|
| 181 |
+
rsi = self.data_engine.calculate_rsi(symbol, "1", self.rsi_period)
|
| 182 |
+
status['indicators'] = {
|
| 183 |
+
'ema_fast': ema_fast,
|
| 184 |
+
'ema_slow': ema_slow,
|
| 185 |
+
'rsi': rsi
|
| 186 |
+
}
|
| 187 |
+
return status
|
| 188 |
+
except Exception as e:
|
| 189 |
+
logger.error(f"Error getting strategy status for {symbol}: {e}")
|
| 190 |
+
return {'error': str(e)}
|
core/trade_monitor.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import logging
|
| 3 |
+
import time
|
| 4 |
+
from typing import Dict, List, Optional, Any
|
| 5 |
+
from datetime import datetime, timedelta
|
| 6 |
+
import yaml
|
| 7 |
+
|
| 8 |
+
from core.exchange import BybitExchange
|
| 9 |
+
from core.strategy import ScalpingStrategy
|
| 10 |
+
from core.risk import RiskManager
|
| 11 |
+
from core.data_engine import DataEngine
|
| 12 |
+
from services.logger import log, log_trade, log_error, log_performance
|
| 13 |
+
|
| 14 |
+
logger = logging.getLogger(__name__)
|
| 15 |
+
|
| 16 |
+
class TradeMonitor:
|
| 17 |
+
def __init__(self, exchange: BybitExchange, strategy: ScalpingStrategy,
|
| 18 |
+
risk_manager: RiskManager, data_engine: DataEngine):
|
| 19 |
+
self.exchange = exchange
|
| 20 |
+
self.strategy = strategy
|
| 21 |
+
self.risk_manager = risk_manager
|
| 22 |
+
self.data_engine = data_engine
|
| 23 |
+
|
| 24 |
+
self.settings = yaml.safe_load(open("config/settings.yaml"))
|
| 25 |
+
self.pairs = yaml.safe_load(open("config/pairs.yaml"))["pairs"]
|
| 26 |
+
|
| 27 |
+
self.is_running = False
|
| 28 |
+
self.monitoring_interval = 2
|
| 29 |
+
self.last_signal_check = {}
|
| 30 |
+
self.signal_cooldown = 30
|
| 31 |
+
|
| 32 |
+
self.performance_check_interval = 300
|
| 33 |
+
self.last_performance_check = datetime.now()
|
| 34 |
+
|
| 35 |
+
async def start_monitoring(self):
|
| 36 |
+
self.is_running = True
|
| 37 |
+
log("π Starting trade monitoring loop")
|
| 38 |
+
|
| 39 |
+
try:
|
| 40 |
+
while self.is_running:
|
| 41 |
+
await self._monitoring_cycle()
|
| 42 |
+
await asyncio.sleep(self.monitoring_interval)
|
| 43 |
+
|
| 44 |
+
except Exception as e:
|
| 45 |
+
log_error("monitoring_loop", f"Monitoring loop crashed: {e}")
|
| 46 |
+
self.is_running = False
|
| 47 |
+
|
| 48 |
+
def stop_monitoring(self):
|
| 49 |
+
self.is_running = False
|
| 50 |
+
log("π Trade monitoring stopped")
|
| 51 |
+
|
| 52 |
+
async def _monitoring_cycle(self):
|
| 53 |
+
try:
|
| 54 |
+
|
| 55 |
+
self.risk_manager.reset_daily_stats()
|
| 56 |
+
|
| 57 |
+
await self._check_open_positions()
|
| 58 |
+
|
| 59 |
+
await self._check_signals()
|
| 60 |
+
|
| 61 |
+
await self._periodic_performance_check()
|
| 62 |
+
|
| 63 |
+
self._health_check()
|
| 64 |
+
|
| 65 |
+
except Exception as e:
|
| 66 |
+
log_error("monitoring_cycle", f"Error in monitoring cycle: {e}")
|
| 67 |
+
|
| 68 |
+
async def _check_open_positions(self):
|
| 69 |
+
try:
|
| 70 |
+
positions = self.exchange.get_positions()
|
| 71 |
+
|
| 72 |
+
for position in positions:
|
| 73 |
+
symbol = position.get("symbol")
|
| 74 |
+
if not symbol:
|
| 75 |
+
continue
|
| 76 |
+
|
| 77 |
+
size = float(position.get("size", 0))
|
| 78 |
+
|
| 79 |
+
if abs(size) < 0.001:
|
| 80 |
+
continue
|
| 81 |
+
|
| 82 |
+
exit_reason = self.risk_manager.check_tp_sl_hit(symbol)
|
| 83 |
+
|
| 84 |
+
if exit_reason:
|
| 85 |
+
log(f"π― {exit_reason} hit for {symbol}")
|
| 86 |
+
|
| 87 |
+
close_result = self.exchange.close_position(symbol)
|
| 88 |
+
|
| 89 |
+
if close_result:
|
| 90 |
+
|
| 91 |
+
self.risk_manager.close_position(symbol, exit_reason)
|
| 92 |
+
|
| 93 |
+
exit_price = float(close_result.get("avgPrice", 0))
|
| 94 |
+
log_trade(symbol, "CLOSE", abs(size), exit_price,
|
| 95 |
+
reason=exit_reason)
|
| 96 |
+
|
| 97 |
+
self.last_signal_check[symbol] = datetime.now() - timedelta(seconds=self.signal_cooldown)
|
| 98 |
+
|
| 99 |
+
else:
|
| 100 |
+
log_error("position_close", f"Failed to close position for {symbol}")
|
| 101 |
+
|
| 102 |
+
except Exception as e:
|
| 103 |
+
log_error("position_check", f"Error checking positions: {e}")
|
| 104 |
+
|
| 105 |
+
async def _check_signals(self):
|
| 106 |
+
try:
|
| 107 |
+
current_time = datetime.now()
|
| 108 |
+
|
| 109 |
+
for symbol in self.pairs:
|
| 110 |
+
|
| 111 |
+
last_check = self.last_signal_check.get(symbol)
|
| 112 |
+
if last_check and (current_time - last_check).seconds < self.signal_cooldown:
|
| 113 |
+
continue
|
| 114 |
+
|
| 115 |
+
signal, confidence, price = self.strategy.generate_signal(symbol)
|
| 116 |
+
|
| 117 |
+
if signal in ["BUY", "SELL"]:
|
| 118 |
+
|
| 119 |
+
if self.risk_manager.validate_entry_signal(symbol, signal, confidence):
|
| 120 |
+
await self._execute_signal(symbol, signal, confidence, price)
|
| 121 |
+
|
| 122 |
+
self.last_signal_check[symbol] = current_time
|
| 123 |
+
|
| 124 |
+
except Exception as e:
|
| 125 |
+
log_error("signal_check", f"Error checking signals: {e}")
|
| 126 |
+
|
| 127 |
+
async def _execute_signal(self, symbol: str, signal: str, confidence: float, price: float):
|
| 128 |
+
try:
|
| 129 |
+
log(f"π Executing {signal} signal for {symbol} at {price} (conf: {confidence:.2f})")
|
| 130 |
+
|
| 131 |
+
qty = self.risk_manager.calculate_position_size(symbol, price, signal)
|
| 132 |
+
|
| 133 |
+
if qty <= 0:
|
| 134 |
+
log(f"β Invalid position size for {symbol}: {qty}")
|
| 135 |
+
return
|
| 136 |
+
|
| 137 |
+
self.exchange.set_leverage(symbol)
|
| 138 |
+
|
| 139 |
+
order = self.exchange.market_order(symbol, signal, qty)
|
| 140 |
+
|
| 141 |
+
if order:
|
| 142 |
+
entry_price = float(order.get("avgPrice", price))
|
| 143 |
+
|
| 144 |
+
tp_percent = self.settings["trading"]["tp_percent"]
|
| 145 |
+
sl_percent = self.settings["trading"]["sl_percent"]
|
| 146 |
+
|
| 147 |
+
if signal == "BUY":
|
| 148 |
+
tp_price = entry_price * (1 + tp_percent)
|
| 149 |
+
sl_price = entry_price * (1 - sl_percent)
|
| 150 |
+
else:
|
| 151 |
+
tp_price = entry_price * (1 - tp_percent)
|
| 152 |
+
sl_price = entry_price * (1 + sl_percent)
|
| 153 |
+
|
| 154 |
+
tp_sl_result = self.exchange.set_tp_sl(symbol, signal, entry_price, tp_price, sl_price)
|
| 155 |
+
|
| 156 |
+
if tp_sl_result:
|
| 157 |
+
log(f"β
TP/SL set for {symbol}: TP={tp_price:.4f}, SL={sl_price:.4f}")
|
| 158 |
+
|
| 159 |
+
self.risk_manager.update_position(symbol, order)
|
| 160 |
+
|
| 161 |
+
log_trade(symbol, signal, qty, entry_price, "MARKET")
|
| 162 |
+
|
| 163 |
+
else:
|
| 164 |
+
log_error("tp_sl_setup", f"Failed to set TP/SL for {symbol}")
|
| 165 |
+
|
| 166 |
+
self.exchange.close_position(symbol)
|
| 167 |
+
|
| 168 |
+
else:
|
| 169 |
+
log_error("order_execution", f"Failed to execute order for {symbol}")
|
| 170 |
+
|
| 171 |
+
except Exception as e:
|
| 172 |
+
log_error("signal_execution", f"Error executing signal for {symbol}: {e}")
|
| 173 |
+
|
| 174 |
+
async def _periodic_performance_check(self):
|
| 175 |
+
try:
|
| 176 |
+
current_time = datetime.now()
|
| 177 |
+
|
| 178 |
+
if (current_time - self.last_performance_check).seconds >= self.performance_check_interval:
|
| 179 |
+
|
| 180 |
+
risk_status = self.risk_manager.get_risk_status()
|
| 181 |
+
|
| 182 |
+
from services.logger import logger_instance
|
| 183 |
+
trade_stats = logger_instance.get_trade_statistics()
|
| 184 |
+
|
| 185 |
+
performance_data = {
|
| 186 |
+
**risk_status,
|
| 187 |
+
**trade_stats
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
log_performance("ALL", "5min", performance_data)
|
| 191 |
+
|
| 192 |
+
self.last_performance_check = current_time
|
| 193 |
+
|
| 194 |
+
if risk_status.get('daily_pnl', 0) < -self.risk_manager.max_daily_loss:
|
| 195 |
+
log("π¨ Daily loss limit reached - activating emergency stop")
|
| 196 |
+
self.risk_manager.emergency_stop_all()
|
| 197 |
+
|
| 198 |
+
except Exception as e:
|
| 199 |
+
log_error("performance_check", f"Error in performance check: {e}")
|
| 200 |
+
|
| 201 |
+
def _health_check(self):
|
| 202 |
+
try:
|
| 203 |
+
|
| 204 |
+
server_time = self.exchange.get_server_time()
|
| 205 |
+
if not server_time:
|
| 206 |
+
log_error("health_check", "Exchange connection unhealthy")
|
| 207 |
+
|
| 208 |
+
buffer_status = self.data_engine.get_buffer_status()
|
| 209 |
+
|
| 210 |
+
for symbol, buffers in buffer_status.get('price_buffers', {}).items():
|
| 211 |
+
if buffers < 10:
|
| 212 |
+
log(f"β οΈ Low price buffer for {symbol}: {buffers} points")
|
| 213 |
+
|
| 214 |
+
except Exception as e:
|
| 215 |
+
log_error("health_check", f"Health check error: {e}")
|
| 216 |
+
|
| 217 |
+
def get_monitoring_status(self) -> Dict[str, Any]:
|
| 218 |
+
try:
|
| 219 |
+
return {
|
| 220 |
+
'is_running': self.is_running,
|
| 221 |
+
'monitoring_interval': self.monitoring_interval,
|
| 222 |
+
'active_symbols': len(self.pairs),
|
| 223 |
+
'open_positions': len(self.risk_manager.open_positions),
|
| 224 |
+
'last_performance_check': self.last_performance_check.isoformat(),
|
| 225 |
+
'signal_cooldowns': {
|
| 226 |
+
symbol: self.last_signal_check.get(symbol).isoformat()
|
| 227 |
+
for symbol in self.last_signal_check.keys()
|
| 228 |
+
if self.last_signal_check.get(symbol)
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
except Exception as e:
|
| 232 |
+
return {'error': str(e)}
|
core/websockets.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
import json
|
| 3 |
+
import websockets
|
| 4 |
+
import logging
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
import os
|
| 7 |
+
import yaml
|
| 8 |
+
import time
|
| 9 |
+
from collections import defaultdict
|
| 10 |
+
|
| 11 |
+
load_dotenv()
|
| 12 |
+
logger = logging.getLogger(__name__)
|
| 13 |
+
|
| 14 |
+
class BybitWebSocket:
|
| 15 |
+
def __init__(self, callback=None):
|
| 16 |
+
self.settings = yaml.safe_load(open("config/settings.yaml"))
|
| 17 |
+
TESTNET = os.getenv("BYBIT_TESTNET", "true").lower() == "true"
|
| 18 |
+
|
| 19 |
+
self.ws_url = "wss://stream-testnet.bybit.com/v5/public/linear" if TESTNET else "wss://stream.bybit.com/v5/public/linear"
|
| 20 |
+
self.private_ws_url = "wss://stream-testnet.bybit.com/v5/private" if TESTNET else "wss://stream.bybit.com/v5/private"
|
| 21 |
+
|
| 22 |
+
self.callback = callback
|
| 23 |
+
self.subscriptions = set()
|
| 24 |
+
self.data_buffers = defaultdict(list)
|
| 25 |
+
|
| 26 |
+
self.websocket = None
|
| 27 |
+
self.private_websocket = None
|
| 28 |
+
self.is_connected = False
|
| 29 |
+
self.is_private_connected = False
|
| 30 |
+
self.reconnect_delay = 1
|
| 31 |
+
self.max_reconnect_delay = 60
|
| 32 |
+
self.heartbeat_interval = 20
|
| 33 |
+
|
| 34 |
+
self.latest_prices = {}
|
| 35 |
+
self.orderbooks = {}
|
| 36 |
+
self.trades = defaultdict(list)
|
| 37 |
+
self.tickers = {}
|
| 38 |
+
|
| 39 |
+
async def connect(self):
|
| 40 |
+
while True:
|
| 41 |
+
try:
|
| 42 |
+
logger.info(f"Connecting to Bybit WebSocket: {self.ws_url}")
|
| 43 |
+
self.websocket = await websockets.connect(self.ws_url)
|
| 44 |
+
self.is_connected = True
|
| 45 |
+
logger.info("WebSocket connected successfully")
|
| 46 |
+
|
| 47 |
+
asyncio.create_task(self._heartbeat())
|
| 48 |
+
|
| 49 |
+
if self.subscriptions:
|
| 50 |
+
await self._resubscribe()
|
| 51 |
+
|
| 52 |
+
await self._message_handler()
|
| 53 |
+
|
| 54 |
+
except Exception as e:
|
| 55 |
+
logger.error(f"WebSocket connection error: {e}")
|
| 56 |
+
self.is_connected = False
|
| 57 |
+
await self._reconnect()
|
| 58 |
+
|
| 59 |
+
async def connect_private(self):
|
| 60 |
+
API_KEY = os.getenv("BYBIT_API_KEY")
|
| 61 |
+
API_SECRET = os.getenv("BYBIT_API_SECRET")
|
| 62 |
+
|
| 63 |
+
if not API_KEY or not API_SECRET:
|
| 64 |
+
logger.warning("Private WebSocket not available - API keys missing")
|
| 65 |
+
return
|
| 66 |
+
|
| 67 |
+
while True:
|
| 68 |
+
try:
|
| 69 |
+
logger.info(f"Connecting to private WebSocket: {self.private_ws_url}")
|
| 70 |
+
self.private_websocket = await websockets.connect(self.private_ws_url)
|
| 71 |
+
self.is_private_connected = True
|
| 72 |
+
logger.info("Private WebSocket connected successfully")
|
| 73 |
+
|
| 74 |
+
await self._authenticate_private()
|
| 75 |
+
|
| 76 |
+
await self._subscribe_private()
|
| 77 |
+
|
| 78 |
+
await self._private_message_handler()
|
| 79 |
+
|
| 80 |
+
except Exception as e:
|
| 81 |
+
logger.error(f"Private WebSocket connection error: {e}")
|
| 82 |
+
self.is_private_connected = False
|
| 83 |
+
await asyncio.sleep(self.reconnect_delay)
|
| 84 |
+
continue
|
| 85 |
+
|
| 86 |
+
async def _authenticate_private(self):
|
| 87 |
+
|
| 88 |
+
pass
|
| 89 |
+
|
| 90 |
+
async def _subscribe_private(self):
|
| 91 |
+
|
| 92 |
+
subscriptions = [
|
| 93 |
+
{"op": "subscribe", "args": ["position.linear"]},
|
| 94 |
+
{"op": "subscribe", "args": ["order.linear"]},
|
| 95 |
+
{"op": "subscribe", "args": ["wallet"]}
|
| 96 |
+
]
|
| 97 |
+
|
| 98 |
+
for sub in subscriptions:
|
| 99 |
+
await self.private_websocket.send(json.dumps(sub))
|
| 100 |
+
logger.info(f"Subscribed to private channel: {sub}")
|
| 101 |
+
|
| 102 |
+
async def subscribe_ticker(self, symbols):
|
| 103 |
+
if isinstance(symbols, str):
|
| 104 |
+
symbols = [symbols]
|
| 105 |
+
|
| 106 |
+
args = [f"tickers.{symbol}" for symbol in symbols]
|
| 107 |
+
subscription = {"op": "subscribe", "args": args}
|
| 108 |
+
|
| 109 |
+
await self.websocket.send(json.dumps(subscription))
|
| 110 |
+
self.subscriptions.add(("ticker", tuple(symbols)))
|
| 111 |
+
logger.info(f"Subscribed to tickers: {symbols}")
|
| 112 |
+
|
| 113 |
+
async def subscribe_kline(self, symbols, intervals=None):
|
| 114 |
+
if isinstance(symbols, str):
|
| 115 |
+
symbols = [symbols]
|
| 116 |
+
if intervals is None:
|
| 117 |
+
intervals = ["1", "5"]
|
| 118 |
+
|
| 119 |
+
args = []
|
| 120 |
+
for symbol in symbols:
|
| 121 |
+
for interval in intervals:
|
| 122 |
+
args.append(f"kline.{interval}.{symbol}")
|
| 123 |
+
|
| 124 |
+
subscription = {"op": "subscribe", "args": args}
|
| 125 |
+
await self.websocket.send(json.dumps(subscription))
|
| 126 |
+
self.subscriptions.add(("kline", tuple(symbols), tuple(intervals)))
|
| 127 |
+
logger.info(f"Subscribed to klines: {symbols} - {intervals}")
|
| 128 |
+
|
| 129 |
+
async def subscribe_orderbook(self, symbols, depth=25):
|
| 130 |
+
if isinstance(symbols, str):
|
| 131 |
+
symbols = [symbols]
|
| 132 |
+
|
| 133 |
+
args = [f"orderbook.{depth}.{symbol}" for symbol in symbols]
|
| 134 |
+
subscription = {"op": "subscribe", "args": args}
|
| 135 |
+
|
| 136 |
+
await self.websocket.send(json.dumps(subscription))
|
| 137 |
+
self.subscriptions.add(("orderbook", tuple(symbols), depth))
|
| 138 |
+
logger.info(f"Subscribed to orderbook: {symbols} depth={depth}")
|
| 139 |
+
|
| 140 |
+
async def subscribe_trades(self, symbols):
|
| 141 |
+
if isinstance(symbols, str):
|
| 142 |
+
symbols = [symbols]
|
| 143 |
+
|
| 144 |
+
args = [f"publicTrade.{symbol}" for symbol in symbols]
|
| 145 |
+
subscription = {"op": "subscribe", "args": args}
|
| 146 |
+
|
| 147 |
+
await self.websocket.send(json.dumps(subscription))
|
| 148 |
+
self.subscriptions.add(("trades", tuple(symbols)))
|
| 149 |
+
logger.info(f"Subscribed to trades: {symbols}")
|
| 150 |
+
|
| 151 |
+
async def _resubscribe(self):
|
| 152 |
+
for sub in self.subscriptions:
|
| 153 |
+
sub_type = sub[0]
|
| 154 |
+
if sub_type == "ticker":
|
| 155 |
+
await self.subscribe_ticker(list(sub[1]))
|
| 156 |
+
elif sub_type == "kline":
|
| 157 |
+
await self.subscribe_kline(list(sub[1]), list(sub[2]))
|
| 158 |
+
elif sub_type == "orderbook":
|
| 159 |
+
await self.subscribe_orderbook(list(sub[1]), sub[2])
|
| 160 |
+
elif sub_type == "trades":
|
| 161 |
+
await self.subscribe_trades(list(sub[1]))
|
| 162 |
+
|
| 163 |
+
async def _heartbeat(self):
|
| 164 |
+
while self.is_connected:
|
| 165 |
+
try:
|
| 166 |
+
ping_msg = {"op": "ping"}
|
| 167 |
+
await self.websocket.send(json.dumps(ping_msg))
|
| 168 |
+
await asyncio.sleep(self.heartbeat_interval)
|
| 169 |
+
except Exception as e:
|
| 170 |
+
logger.error(f"Heartbeat error: {e}")
|
| 171 |
+
break
|
| 172 |
+
|
| 173 |
+
async def _message_handler(self):
|
| 174 |
+
try:
|
| 175 |
+
async for message in self.websocket:
|
| 176 |
+
try:
|
| 177 |
+
data = json.loads(message)
|
| 178 |
+
|
| 179 |
+
if "topic" in data:
|
| 180 |
+
await self._process_message(data)
|
| 181 |
+
elif data.get("op") == "pong":
|
| 182 |
+
|
| 183 |
+
pass
|
| 184 |
+
elif "success" in data:
|
| 185 |
+
if data["success"]:
|
| 186 |
+
logger.info("Subscription successful")
|
| 187 |
+
else:
|
| 188 |
+
logger.error(f"Subscription failed: {data}")
|
| 189 |
+
|
| 190 |
+
except json.JSONDecodeError as e:
|
| 191 |
+
logger.error(f"Invalid JSON received: {e}")
|
| 192 |
+
except Exception as e:
|
| 193 |
+
logger.error(f"Message processing error: {e}")
|
| 194 |
+
|
| 195 |
+
except websockets.exceptions.ConnectionClosed:
|
| 196 |
+
logger.warning("WebSocket connection closed")
|
| 197 |
+
self.is_connected = False
|
| 198 |
+
|
| 199 |
+
async def _private_message_handler(self):
|
| 200 |
+
try:
|
| 201 |
+
async for message in self.private_websocket:
|
| 202 |
+
try:
|
| 203 |
+
data = json.loads(message)
|
| 204 |
+
await self._process_private_message(data)
|
| 205 |
+
except Exception as e:
|
| 206 |
+
logger.error(f"Private message processing error: {e}")
|
| 207 |
+
except websockets.exceptions.ConnectionClosed:
|
| 208 |
+
logger.warning("Private WebSocket connection closed")
|
| 209 |
+
self.is_private_connected = False
|
| 210 |
+
|
| 211 |
+
async def _process_message(self, data):
|
| 212 |
+
topic = data["topic"]
|
| 213 |
+
payload = data["data"]
|
| 214 |
+
|
| 215 |
+
if topic.startswith("tickers."):
|
| 216 |
+
symbol = topic.split(".")[1]
|
| 217 |
+
self.tickers[symbol] = payload
|
| 218 |
+
self.latest_prices[symbol] = float(payload["lastPrice"])
|
| 219 |
+
|
| 220 |
+
elif topic.startswith("orderbook."):
|
| 221 |
+
symbol = topic.split(".")[2]
|
| 222 |
+
self.orderbooks[symbol] = payload
|
| 223 |
+
|
| 224 |
+
elif topic.startswith("publicTrade."):
|
| 225 |
+
symbol = topic.split(".")[1]
|
| 226 |
+
self.trades[symbol].extend(payload)
|
| 227 |
+
|
| 228 |
+
self.trades[symbol] = self.trades[symbol][-100:]
|
| 229 |
+
|
| 230 |
+
elif topic.startswith("kline."):
|
| 231 |
+
parts = topic.split(".")
|
| 232 |
+
interval = parts[1]
|
| 233 |
+
symbol = parts[2]
|
| 234 |
+
|
| 235 |
+
key = f"{symbol}_{interval}"
|
| 236 |
+
self.data_buffers[key].extend(payload)
|
| 237 |
+
|
| 238 |
+
self.data_buffers[key] = self.data_buffers[key][-200:]
|
| 239 |
+
|
| 240 |
+
if self.callback:
|
| 241 |
+
await self.callback(data)
|
| 242 |
+
|
| 243 |
+
async def _process_private_message(self, data):
|
| 244 |
+
|
| 245 |
+
if self.callback:
|
| 246 |
+
await self.callback(data)
|
| 247 |
+
|
| 248 |
+
async def _reconnect(self):
|
| 249 |
+
self.reconnect_delay = min(self.reconnect_delay * 2, self.max_reconnect_delay)
|
| 250 |
+
logger.info(f"Reconnecting in {self.reconnect_delay} seconds...")
|
| 251 |
+
await asyncio.sleep(self.reconnect_delay)
|
| 252 |
+
|
| 253 |
+
def get_latest_price(self, symbol):
|
| 254 |
+
return self.latest_prices.get(symbol)
|
| 255 |
+
|
| 256 |
+
def get_orderbook(self, symbol):
|
| 257 |
+
return self.orderbooks.get(symbol)
|
| 258 |
+
|
| 259 |
+
def get_recent_trades(self, symbol, limit=10):
|
| 260 |
+
return self.trades.get(symbol, [])[-limit:]
|
| 261 |
+
|
| 262 |
+
def get_kline_buffer(self, symbol, interval="1", limit=100):
|
| 263 |
+
key = f"{symbol}_{interval}"
|
| 264 |
+
return self.data_buffers.get(key, [])[-limit:]
|
| 265 |
+
|
| 266 |
+
async def close(self):
|
| 267 |
+
if self.websocket:
|
| 268 |
+
await self.websocket.close()
|
| 269 |
+
if self.private_websocket:
|
| 270 |
+
await self.private_websocket.close()
|
| 271 |
+
|
| 272 |
+
self.is_connected = False
|
| 273 |
+
self.is_private_connected = False
|
| 274 |
+
logger.info("WebSocket connections closed")
|
logs/errors.json
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"timestamp": "2026-01-08T08:37:38.988990",
|
| 4 |
+
"type": "position_check",
|
| 5 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:38).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 6 |
+
"details": {}
|
| 7 |
+
},
|
| 8 |
+
{
|
| 9 |
+
"timestamp": "2026-01-08T08:37:45.564716",
|
| 10 |
+
"type": "position_check",
|
| 11 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:45).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 12 |
+
"details": {}
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"timestamp": "2026-01-08T08:37:52.709415",
|
| 16 |
+
"type": "position_check",
|
| 17 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:52).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 18 |
+
"details": {}
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"timestamp": "2026-01-08T08:37:59.344576",
|
| 22 |
+
"type": "position_check",
|
| 23 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:59).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 24 |
+
"details": {}
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"timestamp": "2026-01-08T08:38:05.892083",
|
| 28 |
+
"type": "position_check",
|
| 29 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:05).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 30 |
+
"details": {}
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"timestamp": "2026-01-08T08:38:12.476775",
|
| 34 |
+
"type": "position_check",
|
| 35 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:12).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 36 |
+
"details": {}
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"timestamp": "2026-01-08T08:38:19.571827",
|
| 40 |
+
"type": "position_check",
|
| 41 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:19).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 42 |
+
"details": {}
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
"timestamp": "2026-01-08T08:38:26.173805",
|
| 46 |
+
"type": "position_check",
|
| 47 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:26).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 48 |
+
"details": {}
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"timestamp": "2026-01-08T08:38:32.720932",
|
| 52 |
+
"type": "position_check",
|
| 53 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:32).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 54 |
+
"details": {}
|
| 55 |
+
},
|
| 56 |
+
{
|
| 57 |
+
"timestamp": "2026-01-08T08:38:39.330812",
|
| 58 |
+
"type": "position_check",
|
| 59 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:39).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 60 |
+
"details": {}
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"timestamp": "2026-01-08T08:38:45.991815",
|
| 64 |
+
"type": "position_check",
|
| 65 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:45).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 66 |
+
"details": {}
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"timestamp": "2026-01-08T08:38:52.621013",
|
| 70 |
+
"type": "position_check",
|
| 71 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:52).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 72 |
+
"details": {}
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"timestamp": "2026-01-08T08:38:59.185389",
|
| 76 |
+
"type": "position_check",
|
| 77 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:59).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 78 |
+
"details": {}
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"timestamp": "2026-01-08T08:39:05.774305",
|
| 82 |
+
"type": "position_check",
|
| 83 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:05).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 84 |
+
"details": {}
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"timestamp": "2026-01-08T08:39:12.350832",
|
| 88 |
+
"type": "position_check",
|
| 89 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:12).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 90 |
+
"details": {}
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"timestamp": "2026-01-08T08:39:18.855515",
|
| 94 |
+
"type": "position_check",
|
| 95 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:18).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 96 |
+
"details": {}
|
| 97 |
+
},
|
| 98 |
+
{
|
| 99 |
+
"timestamp": "2026-01-08T08:39:25.602026",
|
| 100 |
+
"type": "position_check",
|
| 101 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:25).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 102 |
+
"details": {}
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"timestamp": "2026-01-08T08:39:32.308813",
|
| 106 |
+
"type": "position_check",
|
| 107 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:32).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 108 |
+
"details": {}
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"timestamp": "2026-01-08T08:39:38.808382",
|
| 112 |
+
"type": "position_check",
|
| 113 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:38).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 114 |
+
"details": {}
|
| 115 |
+
},
|
| 116 |
+
{
|
| 117 |
+
"timestamp": "2026-01-08T08:39:45.490647",
|
| 118 |
+
"type": "position_check",
|
| 119 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:45).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 120 |
+
"details": {}
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"timestamp": "2026-01-08T08:39:52.341720",
|
| 124 |
+
"type": "position_check",
|
| 125 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:52).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 126 |
+
"details": {}
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"timestamp": "2026-01-08T08:39:59.324708",
|
| 130 |
+
"type": "position_check",
|
| 131 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:59).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 132 |
+
"details": {}
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"timestamp": "2026-01-08T08:40:05.833493",
|
| 136 |
+
"type": "position_check",
|
| 137 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:05).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 138 |
+
"details": {}
|
| 139 |
+
},
|
| 140 |
+
{
|
| 141 |
+
"timestamp": "2026-01-08T08:40:12.406886",
|
| 142 |
+
"type": "position_check",
|
| 143 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:12).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 144 |
+
"details": {}
|
| 145 |
+
},
|
| 146 |
+
{
|
| 147 |
+
"timestamp": "2026-01-08T08:40:18.962761",
|
| 148 |
+
"type": "position_check",
|
| 149 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:18).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 150 |
+
"details": {}
|
| 151 |
+
},
|
| 152 |
+
{
|
| 153 |
+
"timestamp": "2026-01-08T08:40:25.469095",
|
| 154 |
+
"type": "position_check",
|
| 155 |
+
"message": "Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:25).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 156 |
+
"details": {}
|
| 157 |
+
},
|
| 158 |
+
{
|
| 159 |
+
"timestamp": "2026-01-08T09:18:16.855872",
|
| 160 |
+
"type": "position_check",
|
| 161 |
+
"message": "Error checking positions: HTTPSConnectionPool(host='api.bybit.com', port=443): Max retries exceeded with url: /v5/position/list?category=linear (Caused by NameResolutionError(\"<urllib3.connection.HTTPSConnection object at 0x7fa8c7dadbe0>: Failed to resolve 'api.bybit.com' ([Errno -3] Temporary failure in name resolution)\"))",
|
| 162 |
+
"details": {}
|
| 163 |
+
},
|
| 164 |
+
{
|
| 165 |
+
"timestamp": "2026-01-08T09:18:27.774079",
|
| 166 |
+
"type": "position_check",
|
| 167 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:27).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 168 |
+
"details": {}
|
| 169 |
+
},
|
| 170 |
+
{
|
| 171 |
+
"timestamp": "2026-01-08T09:18:34.383041",
|
| 172 |
+
"type": "position_check",
|
| 173 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:34).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 174 |
+
"details": {}
|
| 175 |
+
},
|
| 176 |
+
{
|
| 177 |
+
"timestamp": "2026-01-08T09:18:40.634319",
|
| 178 |
+
"type": "position_check",
|
| 179 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:40).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 180 |
+
"details": {}
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"timestamp": "2026-01-08T09:18:46.921983",
|
| 184 |
+
"type": "position_check",
|
| 185 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:46).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 186 |
+
"details": {}
|
| 187 |
+
},
|
| 188 |
+
{
|
| 189 |
+
"timestamp": "2026-01-08T09:18:53.245499",
|
| 190 |
+
"type": "position_check",
|
| 191 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:53).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 192 |
+
"details": {}
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"timestamp": "2026-01-08T09:18:59.867702",
|
| 196 |
+
"type": "position_check",
|
| 197 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:59).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 198 |
+
"details": {}
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"timestamp": "2026-01-08T09:19:06.148693",
|
| 202 |
+
"type": "position_check",
|
| 203 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:06).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 204 |
+
"details": {}
|
| 205 |
+
},
|
| 206 |
+
{
|
| 207 |
+
"timestamp": "2026-01-08T09:19:12.462576",
|
| 208 |
+
"type": "position_check",
|
| 209 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:12).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 210 |
+
"details": {}
|
| 211 |
+
},
|
| 212 |
+
{
|
| 213 |
+
"timestamp": "2026-01-08T09:19:18.734882",
|
| 214 |
+
"type": "position_check",
|
| 215 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:18).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 216 |
+
"details": {}
|
| 217 |
+
},
|
| 218 |
+
{
|
| 219 |
+
"timestamp": "2026-01-08T09:19:25.030983",
|
| 220 |
+
"type": "position_check",
|
| 221 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:25).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 222 |
+
"details": {}
|
| 223 |
+
},
|
| 224 |
+
{
|
| 225 |
+
"timestamp": "2026-01-08T09:19:31.982889",
|
| 226 |
+
"type": "position_check",
|
| 227 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:31).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 228 |
+
"details": {}
|
| 229 |
+
},
|
| 230 |
+
{
|
| 231 |
+
"timestamp": "2026-01-08T09:19:38.288181",
|
| 232 |
+
"type": "position_check",
|
| 233 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:38).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 234 |
+
"details": {}
|
| 235 |
+
},
|
| 236 |
+
{
|
| 237 |
+
"timestamp": "2026-01-08T09:19:44.958179",
|
| 238 |
+
"type": "position_check",
|
| 239 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:44).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 240 |
+
"details": {}
|
| 241 |
+
},
|
| 242 |
+
{
|
| 243 |
+
"timestamp": "2026-01-08T09:19:51.266450",
|
| 244 |
+
"type": "position_check",
|
| 245 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:51).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 246 |
+
"details": {}
|
| 247 |
+
},
|
| 248 |
+
{
|
| 249 |
+
"timestamp": "2026-01-08T09:19:57.588008",
|
| 250 |
+
"type": "position_check",
|
| 251 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:57).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 252 |
+
"details": {}
|
| 253 |
+
},
|
| 254 |
+
{
|
| 255 |
+
"timestamp": "2026-01-08T09:20:03.853308",
|
| 256 |
+
"type": "position_check",
|
| 257 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:03).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 258 |
+
"details": {}
|
| 259 |
+
},
|
| 260 |
+
{
|
| 261 |
+
"timestamp": "2026-01-08T09:20:10.119147",
|
| 262 |
+
"type": "position_check",
|
| 263 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:10).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 264 |
+
"details": {}
|
| 265 |
+
},
|
| 266 |
+
{
|
| 267 |
+
"timestamp": "2026-01-08T09:20:16.416108",
|
| 268 |
+
"type": "position_check",
|
| 269 |
+
"message": "Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:16).\nRequest \u2192 GET https://api.bybit.com/v5/position/list: category=linear.",
|
| 270 |
+
"details": {}
|
| 271 |
+
}
|
| 272 |
+
]
|
logs/errors.log
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
2026-01-08 08:34:07,556 - ERROR - WEBSOCKET_START: Failed to start WebSocket: 'NoneType' object has no attribute 'send'
|
| 2 |
+
2026-01-08 08:34:12,589 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:12).
|
| 3 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 4 |
+
2026-01-08 08:34:19,229 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:19).
|
| 5 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 6 |
+
2026-01-08 08:34:25,791 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:25).
|
| 7 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 8 |
+
2026-01-08 08:34:32,336 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:32).
|
| 9 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 10 |
+
2026-01-08 08:34:38,925 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:38).
|
| 11 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 12 |
+
2026-01-08 08:34:45,531 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:45).
|
| 13 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 14 |
+
2026-01-08 08:34:52,116 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:52).
|
| 15 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 16 |
+
2026-01-08 08:34:58,727 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:58).
|
| 17 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 18 |
+
2026-01-08 08:35:05,343 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:05).
|
| 19 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 20 |
+
2026-01-08 08:35:12,672 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:12).
|
| 21 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 22 |
+
2026-01-08 08:35:19,332 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:19).
|
| 23 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 24 |
+
2026-01-08 08:35:28,860 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:28).
|
| 25 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 26 |
+
2026-01-08 08:35:35,409 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:35).
|
| 27 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 28 |
+
2026-01-08 08:35:41,937 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:41).
|
| 29 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 30 |
+
2026-01-08 08:35:48,456 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:48).
|
| 31 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 32 |
+
2026-01-08 08:35:55,276 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:55).
|
| 33 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 34 |
+
2026-01-08 08:36:01,846 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:01).
|
| 35 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 36 |
+
2026-01-08 08:36:08,368 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:08).
|
| 37 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 38 |
+
2026-01-08 08:36:15,048 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:15).
|
| 39 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 40 |
+
2026-01-08 08:36:21,919 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:21).
|
| 41 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 42 |
+
2026-01-08 08:36:28,439 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:28).
|
| 43 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 44 |
+
2026-01-08 08:36:34,976 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:34).
|
| 45 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 46 |
+
2026-01-08 08:36:41,540 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:41).
|
| 47 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 48 |
+
2026-01-08 08:36:48,068 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:48).
|
| 49 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 50 |
+
2026-01-08 08:36:54,593 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:54).
|
| 51 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 52 |
+
2026-01-08 08:37:01,148 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:01).
|
| 53 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 54 |
+
2026-01-08 08:37:07,681 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:07).
|
| 55 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 56 |
+
2026-01-08 08:37:14,180 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:14).
|
| 57 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 58 |
+
2026-01-08 08:37:20,706 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:20).
|
| 59 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 60 |
+
2026-01-08 08:37:27,277 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:27).
|
| 61 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 62 |
+
2026-01-08 08:37:33,795 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:33).
|
| 63 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 64 |
+
2026-01-08 08:37:38,993 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:38).
|
| 65 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 66 |
+
2026-01-08 08:37:40,377 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:40).
|
| 67 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 68 |
+
2026-01-08 08:37:45,565 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:45).
|
| 69 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 70 |
+
2026-01-08 08:37:46,986 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:46).
|
| 71 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 72 |
+
2026-01-08 08:37:52,709 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:52).
|
| 73 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 74 |
+
2026-01-08 08:37:53,526 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:53).
|
| 75 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 76 |
+
2026-01-08 08:37:59,349 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:59).
|
| 77 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 78 |
+
2026-01-08 08:38:00,095 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:00).
|
| 79 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 80 |
+
2026-01-08 08:38:05,892 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:05).
|
| 81 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 82 |
+
2026-01-08 08:38:06,687 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:06).
|
| 83 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 84 |
+
2026-01-08 08:38:12,478 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:12).
|
| 85 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 86 |
+
2026-01-08 08:38:13,349 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:13).
|
| 87 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 88 |
+
2026-01-08 08:38:19,573 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:19).
|
| 89 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 90 |
+
2026-01-08 08:38:20,395 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:20).
|
| 91 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 92 |
+
2026-01-08 08:38:26,178 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:26).
|
| 93 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 94 |
+
2026-01-08 08:38:26,976 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:26).
|
| 95 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 96 |
+
2026-01-08 08:38:32,722 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:32).
|
| 97 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 98 |
+
2026-01-08 08:38:33,526 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:33).
|
| 99 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 100 |
+
2026-01-08 08:38:39,332 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:39).
|
| 101 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 102 |
+
2026-01-08 08:38:40,099 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:40).
|
| 103 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 104 |
+
2026-01-08 08:38:45,994 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:45).
|
| 105 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 106 |
+
2026-01-08 08:38:46,691 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:46).
|
| 107 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 108 |
+
2026-01-08 08:38:52,626 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:52).
|
| 109 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 110 |
+
2026-01-08 08:38:53,552 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:53).
|
| 111 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 112 |
+
2026-01-08 08:38:59,186 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:59).
|
| 113 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 114 |
+
2026-01-08 08:39:00,123 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:00).
|
| 115 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 116 |
+
2026-01-08 08:39:05,847 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:05).
|
| 117 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 118 |
+
2026-01-08 08:39:06,742 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:06).
|
| 119 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 120 |
+
2026-01-08 08:39:12,351 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:12).
|
| 121 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 122 |
+
2026-01-08 08:39:13,238 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:13).
|
| 123 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 124 |
+
2026-01-08 08:39:18,856 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:18).
|
| 125 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 126 |
+
2026-01-08 08:39:19,812 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:19).
|
| 127 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 128 |
+
2026-01-08 08:39:25,602 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:25).
|
| 129 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 130 |
+
2026-01-08 08:39:26,333 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:26).
|
| 131 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 132 |
+
2026-01-08 08:39:32,309 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:32).
|
| 133 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 134 |
+
2026-01-08 08:39:32,992 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:32).
|
| 135 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 136 |
+
2026-01-08 08:39:38,808 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:38).
|
| 137 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 138 |
+
2026-01-08 08:39:39,878 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:39).
|
| 139 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 140 |
+
2026-01-08 08:39:45,492 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:45).
|
| 141 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 142 |
+
2026-01-08 08:39:46,402 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:46).
|
| 143 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 144 |
+
2026-01-08 08:39:52,342 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:52).
|
| 145 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 146 |
+
2026-01-08 08:39:52,896 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:52).
|
| 147 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 148 |
+
2026-01-08 08:39:59,328 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:59).
|
| 149 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 150 |
+
2026-01-08 08:39:59,702 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:59).
|
| 151 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 152 |
+
2026-01-08 08:40:05,835 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:05).
|
| 153 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 154 |
+
2026-01-08 08:40:06,262 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:06).
|
| 155 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 156 |
+
2026-01-08 08:40:12,407 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:12).
|
| 157 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 158 |
+
2026-01-08 08:40:12,801 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:12).
|
| 159 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 160 |
+
2026-01-08 08:40:18,963 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:18).
|
| 161 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 162 |
+
2026-01-08 08:40:19,325 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:19).
|
| 163 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 164 |
+
2026-01-08 08:40:25,470 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:25).
|
| 165 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 166 |
+
2026-01-08 08:40:25,835 - ERROR - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:25).
|
| 167 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 168 |
+
2026-01-08 09:18:16,857 - ERROR - POSITION_CHECK: Error checking positions: HTTPSConnectionPool(host='api.bybit.com', port=443): Max retries exceeded with url: /v5/position/list?category=linear (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at 0x7fa8c7dadbe0>: Failed to resolve 'api.bybit.com' ([Errno -3] Temporary failure in name resolution)"))
|
| 169 |
+
2026-01-08 09:18:17,152 - ERROR - POSITION_CHECK: Error checking positions: HTTPSConnectionPool(host='api.bybit.com', port=443): Max retries exceeded with url: /v5/position/list?category=linear (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at 0x7f87b3572900>: Failed to resolve 'api.bybit.com' ([Errno -3] Temporary failure in name resolution)"))
|
| 170 |
+
2026-01-08 09:18:27,355 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:27).
|
| 171 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 172 |
+
2026-01-08 09:18:27,775 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:27).
|
| 173 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 174 |
+
2026-01-08 09:18:33,629 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:33).
|
| 175 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 176 |
+
2026-01-08 09:18:34,384 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:34).
|
| 177 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 178 |
+
2026-01-08 09:18:39,888 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:39).
|
| 179 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 180 |
+
2026-01-08 09:18:40,635 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:40).
|
| 181 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 182 |
+
2026-01-08 09:18:46,160 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:46).
|
| 183 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 184 |
+
2026-01-08 09:18:46,923 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:46).
|
| 185 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 186 |
+
2026-01-08 09:18:52,447 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:52).
|
| 187 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 188 |
+
2026-01-08 09:18:53,247 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:53).
|
| 189 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 190 |
+
2026-01-08 09:18:58,713 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:58).
|
| 191 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 192 |
+
2026-01-08 09:18:59,869 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:59).
|
| 193 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 194 |
+
2026-01-08 09:19:04,983 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:04).
|
| 195 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 196 |
+
2026-01-08 09:19:06,149 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:06).
|
| 197 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 198 |
+
2026-01-08 09:19:11,307 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:11).
|
| 199 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 200 |
+
2026-01-08 09:19:12,464 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:12).
|
| 201 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 202 |
+
2026-01-08 09:19:17,567 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:17).
|
| 203 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 204 |
+
2026-01-08 09:19:18,735 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:18).
|
| 205 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 206 |
+
2026-01-08 09:19:23,898 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:23).
|
| 207 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 208 |
+
2026-01-08 09:19:25,033 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:25).
|
| 209 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 210 |
+
2026-01-08 09:19:30,217 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:30).
|
| 211 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 212 |
+
2026-01-08 09:19:31,983 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:31).
|
| 213 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 214 |
+
2026-01-08 09:19:36,549 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:36).
|
| 215 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 216 |
+
2026-01-08 09:19:38,289 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:38).
|
| 217 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 218 |
+
2026-01-08 09:19:42,883 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:42).
|
| 219 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 220 |
+
2026-01-08 09:19:44,959 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:44).
|
| 221 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 222 |
+
2026-01-08 09:19:49,179 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:49).
|
| 223 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 224 |
+
2026-01-08 09:19:51,268 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:51).
|
| 225 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 226 |
+
2026-01-08 09:19:56,099 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:56).
|
| 227 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 228 |
+
2026-01-08 09:19:57,589 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:57).
|
| 229 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 230 |
+
2026-01-08 09:20:02,381 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:02).
|
| 231 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 232 |
+
2026-01-08 09:20:03,854 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:03).
|
| 233 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 234 |
+
2026-01-08 09:20:08,654 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:08).
|
| 235 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 236 |
+
2026-01-08 09:20:10,120 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:10).
|
| 237 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 238 |
+
2026-01-08 09:20:15,315 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:15).
|
| 239 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 240 |
+
2026-01-08 09:20:16,417 - ERROR - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:16).
|
| 241 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
logs/performance.jsonl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"timestamp": "2026-01-08T08:39:06.743107", "symbol": "ALL", "period": "5min", "metrics": {"emergency_stop": false, "open_positions": 0, "daily_pnl": 0.0, "daily_trades": 0, "win_rate": 0.0, "total_trades": 0, "positions": [], "total_pnl": 0.0}}
|
| 2 |
+
{"timestamp": "2026-01-08T09:18:16.924852", "symbol": "ALL", "period": "5min", "metrics": {"emergency_stop": false, "open_positions": 0, "daily_pnl": 0.0, "daily_trades": 0, "win_rate": 0.0, "total_trades": 0, "positions": [], "total_pnl": 0.0}}
|
| 3 |
+
{"timestamp": "2026-01-08T09:18:17.152621", "symbol": "ALL", "period": "5min", "metrics": {"emergency_stop": false, "open_positions": 0, "daily_pnl": 0.0, "daily_trades": 0, "win_rate": 0.0, "total_trades": 0, "positions": [], "total_pnl": 0.0}}
|
logs/scalper.log
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
2026-01-08 08:34:03,664 - scalper - INFO - log:205 - π Starting Bybit Scalping Bot v1.0.0
|
| 2 |
+
2026-01-08 08:34:03,664 - scalper - INFO - log:205 - π Trading pairs: BTCUSDT, ETHUSDT, SOLUSDT
|
| 3 |
+
2026-01-08 08:34:03,664 - scalper - INFO - log:205 - βοΈ Leverage: 20x
|
| 4 |
+
2026-01-08 08:34:03,665 - scalper - INFO - log:205 - π― Take Profit: 2.5%
|
| 5 |
+
2026-01-08 08:34:03,665 - scalper - INFO - log:205 - π‘οΈ Stop Loss: 1.0%
|
| 6 |
+
2026-01-08 08:34:05,536 - scalper - INFO - log:205 - β
Exchange client initialized
|
| 7 |
+
2026-01-08 08:34:05,542 - scalper - INFO - log:205 - β
Data engine initialized
|
| 8 |
+
2026-01-08 08:34:05,547 - scalper - INFO - log:205 - β
Trading strategy initialized
|
| 9 |
+
2026-01-08 08:34:05,550 - scalper - INFO - log:205 - β
Risk manager initialized
|
| 10 |
+
2026-01-08 08:34:05,553 - scalper - INFO - log:205 - β
Trade monitor initialized
|
| 11 |
+
2026-01-08 08:34:05,554 - scalper - INFO - log:205 - β
WebSocket client initialized
|
| 12 |
+
2026-01-08 08:34:07,556 - scalper - ERROR - log_error:125 - WEBSOCKET_START: Failed to start WebSocket: 'NoneType' object has no attribute 'send'
|
| 13 |
+
2026-01-08 08:34:07,556 - scalper - INFO - log:205 - βΆοΈ Bot is now active and monitoring markets
|
| 14 |
+
2026-01-08 08:34:07,556 - scalper - INFO - log:205 - π Starting trade monitoring loop
|
| 15 |
+
2026-01-08 08:34:12,214 - scalper - INFO - log:205 - π Shutdown signal received
|
| 16 |
+
2026-01-08 08:34:12,589 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:12).
|
| 17 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 18 |
+
2026-01-08 08:34:12,972 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 19 |
+
2026-01-08 08:34:12,973 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 20 |
+
2026-01-08 08:34:12,973 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 21 |
+
2026-01-08 08:34:19,229 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:19).
|
| 22 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 23 |
+
2026-01-08 08:34:19,633 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 24 |
+
2026-01-08 08:34:19,633 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 25 |
+
2026-01-08 08:34:19,633 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 26 |
+
2026-01-08 08:34:25,791 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:25).
|
| 27 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 28 |
+
2026-01-08 08:34:26,166 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 29 |
+
2026-01-08 08:34:26,168 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 30 |
+
2026-01-08 08:34:26,169 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 31 |
+
2026-01-08 08:34:32,336 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:32).
|
| 32 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 33 |
+
2026-01-08 08:34:32,720 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 34 |
+
2026-01-08 08:34:32,721 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 35 |
+
2026-01-08 08:34:32,721 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 36 |
+
2026-01-08 08:34:38,925 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:38).
|
| 37 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 38 |
+
2026-01-08 08:34:39,304 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 39 |
+
2026-01-08 08:34:39,304 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 40 |
+
2026-01-08 08:34:39,305 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 41 |
+
2026-01-08 08:34:45,531 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:45).
|
| 42 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 43 |
+
2026-01-08 08:34:45,939 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 44 |
+
2026-01-08 08:34:45,942 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 45 |
+
2026-01-08 08:34:45,944 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 46 |
+
2026-01-08 08:34:52,116 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:52).
|
| 47 |
+
Request οΏ½οΏ½ GET https://api.bybit.com/v5/position/list: category=linear.
|
| 48 |
+
2026-01-08 08:34:52,509 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 49 |
+
2026-01-08 08:34:52,510 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 50 |
+
2026-01-08 08:34:52,510 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 51 |
+
2026-01-08 08:34:58,727 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:34:58).
|
| 52 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 53 |
+
2026-01-08 08:34:59,145 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 54 |
+
2026-01-08 08:34:59,146 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 55 |
+
2026-01-08 08:34:59,146 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 56 |
+
2026-01-08 08:35:05,343 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:05).
|
| 57 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 58 |
+
2026-01-08 08:35:06,035 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 59 |
+
2026-01-08 08:35:06,037 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 60 |
+
2026-01-08 08:35:06,038 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 61 |
+
2026-01-08 08:35:12,672 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:12).
|
| 62 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 63 |
+
2026-01-08 08:35:13,089 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 64 |
+
2026-01-08 08:35:13,089 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 65 |
+
2026-01-08 08:35:13,090 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 66 |
+
2026-01-08 08:35:19,332 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:19).
|
| 67 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 68 |
+
2026-01-08 08:35:19,743 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 69 |
+
2026-01-08 08:35:19,743 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 70 |
+
2026-01-08 08:35:19,744 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 71 |
+
2026-01-08 08:35:28,860 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:28).
|
| 72 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 73 |
+
2026-01-08 08:35:29,262 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 74 |
+
2026-01-08 08:35:29,262 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 75 |
+
2026-01-08 08:35:29,262 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 76 |
+
2026-01-08 08:35:35,409 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:35).
|
| 77 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 78 |
+
2026-01-08 08:35:35,769 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 79 |
+
2026-01-08 08:35:35,770 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 80 |
+
2026-01-08 08:35:35,770 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 81 |
+
2026-01-08 08:35:41,937 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:41).
|
| 82 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 83 |
+
2026-01-08 08:35:42,304 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 84 |
+
2026-01-08 08:35:42,304 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 85 |
+
2026-01-08 08:35:42,304 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 86 |
+
2026-01-08 08:35:48,456 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:48).
|
| 87 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 88 |
+
2026-01-08 08:35:49,140 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 89 |
+
2026-01-08 08:35:49,140 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 90 |
+
2026-01-08 08:35:49,140 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 91 |
+
2026-01-08 08:35:55,276 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:35:55).
|
| 92 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 93 |
+
2026-01-08 08:35:55,667 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 94 |
+
2026-01-08 08:35:55,667 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 95 |
+
2026-01-08 08:35:55,667 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 96 |
+
2026-01-08 08:36:01,846 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:01).
|
| 97 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 98 |
+
2026-01-08 08:36:02,251 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 99 |
+
2026-01-08 08:36:02,252 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 100 |
+
2026-01-08 08:36:02,252 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 101 |
+
2026-01-08 08:36:08,368 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:08).
|
| 102 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 103 |
+
2026-01-08 08:36:08,763 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 104 |
+
2026-01-08 08:36:08,763 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 105 |
+
2026-01-08 08:36:08,763 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 106 |
+
2026-01-08 08:36:15,048 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:15).
|
| 107 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 108 |
+
2026-01-08 08:36:15,426 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 109 |
+
2026-01-08 08:36:15,427 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 110 |
+
2026-01-08 08:36:15,427 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 111 |
+
2026-01-08 08:36:21,919 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:21).
|
| 112 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 113 |
+
2026-01-08 08:36:22,283 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 114 |
+
2026-01-08 08:36:22,283 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 115 |
+
2026-01-08 08:36:22,283 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 116 |
+
2026-01-08 08:36:28,439 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:28).
|
| 117 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 118 |
+
2026-01-08 08:36:28,820 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 119 |
+
2026-01-08 08:36:28,820 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 120 |
+
2026-01-08 08:36:28,820 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 121 |
+
2026-01-08 08:36:34,976 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:34).
|
| 122 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 123 |
+
2026-01-08 08:36:35,344 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 124 |
+
2026-01-08 08:36:35,344 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 125 |
+
2026-01-08 08:36:35,344 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 126 |
+
2026-01-08 08:36:41,540 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:41).
|
| 127 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 128 |
+
2026-01-08 08:36:41,923 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 129 |
+
2026-01-08 08:36:41,924 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 130 |
+
2026-01-08 08:36:41,924 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 131 |
+
2026-01-08 08:36:48,068 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:48).
|
| 132 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 133 |
+
2026-01-08 08:36:48,446 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 134 |
+
2026-01-08 08:36:48,446 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 135 |
+
2026-01-08 08:36:48,446 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 136 |
+
2026-01-08 08:36:54,593 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:36:54).
|
| 137 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 138 |
+
2026-01-08 08:36:54,964 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 139 |
+
2026-01-08 08:36:54,965 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 140 |
+
2026-01-08 08:36:54,965 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 141 |
+
2026-01-08 08:37:01,148 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:01).
|
| 142 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 143 |
+
2026-01-08 08:37:01,533 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 144 |
+
2026-01-08 08:37:01,534 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 145 |
+
2026-01-08 08:37:01,534 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 146 |
+
2026-01-08 08:37:07,681 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:07).
|
| 147 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 148 |
+
2026-01-08 08:37:08,058 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 149 |
+
2026-01-08 08:37:08,059 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 150 |
+
2026-01-08 08:37:08,059 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 151 |
+
2026-01-08 08:37:14,180 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:14).
|
| 152 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 153 |
+
2026-01-08 08:37:14,563 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 154 |
+
2026-01-08 08:37:14,563 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 155 |
+
2026-01-08 08:37:14,563 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 156 |
+
2026-01-08 08:37:20,706 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:20).
|
| 157 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 158 |
+
2026-01-08 08:37:21,084 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 159 |
+
2026-01-08 08:37:21,084 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 160 |
+
2026-01-08 08:37:21,084 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 161 |
+
2026-01-08 08:37:27,277 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:27).
|
| 162 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 163 |
+
2026-01-08 08:37:27,643 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 164 |
+
2026-01-08 08:37:27,644 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 165 |
+
2026-01-08 08:37:27,644 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 166 |
+
2026-01-08 08:37:31,042 - scalper - INFO - log:205 - π Starting Bybit Scalping Bot v1.0.0
|
| 167 |
+
2026-01-08 08:37:31,043 - scalper - INFO - log:205 - π Trading pairs: BTCUSDT, ETHUSDT, SOLUSDT
|
| 168 |
+
2026-01-08 08:37:31,043 - scalper - INFO - log:205 - βοΈ Leverage: 20x
|
| 169 |
+
2026-01-08 08:37:31,043 - scalper - INFO - log:205 - π― Take Profit: 2.5%
|
| 170 |
+
2026-01-08 08:37:31,043 - scalper - INFO - log:205 - π‘οΈ Stop Loss: 1.0%
|
| 171 |
+
2026-01-08 08:37:32,181 - scalper - INFO - log:205 - β
Exchange client initialized
|
| 172 |
+
2026-01-08 08:37:32,187 - scalper - INFO - log:205 - β
Data engine initialized
|
| 173 |
+
2026-01-08 08:37:32,191 - scalper - INFO - log:205 - β
Trading strategy initialized
|
| 174 |
+
2026-01-08 08:37:32,194 - scalper - INFO - log:205 - β
Risk manager initialized
|
| 175 |
+
2026-01-08 08:37:32,196 - scalper - INFO - log:205 - β
Trade monitor initialized
|
| 176 |
+
2026-01-08 08:37:32,199 - scalper - INFO - log:205 - β
WebSocket client initialized
|
| 177 |
+
2026-01-08 08:37:33,795 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:33).
|
| 178 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 179 |
+
2026-01-08 08:37:34,168 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 180 |
+
2026-01-08 08:37:34,168 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 181 |
+
2026-01-08 08:37:34,168 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 182 |
+
2026-01-08 08:37:34,201 - scalper - INFO - log:205 - β
WebSocket subscriptions active
|
| 183 |
+
2026-01-08 08:37:34,201 - scalper - INFO - log:205 - βΆοΈ Bot is now active and monitoring markets
|
| 184 |
+
2026-01-08 08:37:34,202 - scalper - INFO - log:205 - π Starting trade monitoring loop
|
| 185 |
+
2026-01-08 08:37:38,993 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:38).
|
| 186 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 187 |
+
2026-01-08 08:37:39,367 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 188 |
+
2026-01-08 08:37:39,367 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 189 |
+
2026-01-08 08:37:39,367 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 190 |
+
2026-01-08 08:37:40,377 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:40).
|
| 191 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 192 |
+
2026-01-08 08:37:40,747 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 193 |
+
2026-01-08 08:37:40,748 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 194 |
+
2026-01-08 08:37:40,748 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 195 |
+
2026-01-08 08:37:44,764 - scalper - INFO - log:205 - π Shutdown signal received
|
| 196 |
+
2026-01-08 08:37:45,565 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:45).
|
| 197 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 198 |
+
2026-01-08 08:37:46,986 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:46).
|
| 199 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 200 |
+
2026-01-08 08:37:47,355 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 201 |
+
2026-01-08 08:37:47,355 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 202 |
+
2026-01-08 08:37:47,356 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 203 |
+
2026-01-08 08:37:52,709 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:52).
|
| 204 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 205 |
+
2026-01-08 08:37:53,526 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:53).
|
| 206 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 207 |
+
2026-01-08 08:37:53,939 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 208 |
+
2026-01-08 08:37:53,940 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 209 |
+
2026-01-08 08:37:53,941 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 210 |
+
2026-01-08 08:37:59,349 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:37:59).
|
| 211 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 212 |
+
2026-01-08 08:38:00,095 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:00).
|
| 213 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 214 |
+
2026-01-08 08:38:00,463 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 215 |
+
2026-01-08 08:38:00,463 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 216 |
+
2026-01-08 08:38:00,464 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 217 |
+
2026-01-08 08:38:05,892 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:05).
|
| 218 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 219 |
+
2026-01-08 08:38:06,687 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:06).
|
| 220 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 221 |
+
2026-01-08 08:38:07,103 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 222 |
+
2026-01-08 08:38:07,103 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 223 |
+
2026-01-08 08:38:07,104 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 224 |
+
2026-01-08 08:38:12,478 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:12).
|
| 225 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 226 |
+
2026-01-08 08:38:13,349 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:13).
|
| 227 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 228 |
+
2026-01-08 08:38:13,741 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 229 |
+
2026-01-08 08:38:13,742 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 230 |
+
2026-01-08 08:38:13,742 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 231 |
+
2026-01-08 08:38:19,573 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:19).
|
| 232 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 233 |
+
2026-01-08 08:38:20,395 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:20).
|
| 234 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 235 |
+
2026-01-08 08:38:20,789 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 236 |
+
2026-01-08 08:38:20,790 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 237 |
+
2026-01-08 08:38:20,791 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 238 |
+
2026-01-08 08:38:26,178 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:26).
|
| 239 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 240 |
+
2026-01-08 08:38:26,976 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:26).
|
| 241 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 242 |
+
2026-01-08 08:38:27,386 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 243 |
+
2026-01-08 08:38:27,386 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 244 |
+
2026-01-08 08:38:27,386 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 245 |
+
2026-01-08 08:38:32,722 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:32).
|
| 246 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 247 |
+
2026-01-08 08:38:33,526 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:33).
|
| 248 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 249 |
+
2026-01-08 08:38:33,896 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 250 |
+
2026-01-08 08:38:33,896 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 251 |
+
2026-01-08 08:38:33,896 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 252 |
+
2026-01-08 08:38:39,332 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:39).
|
| 253 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 254 |
+
2026-01-08 08:38:40,099 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:40).
|
| 255 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 256 |
+
2026-01-08 08:38:40,484 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 257 |
+
2026-01-08 08:38:40,489 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 258 |
+
2026-01-08 08:38:40,489 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 259 |
+
2026-01-08 08:38:45,994 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:45).
|
| 260 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 261 |
+
2026-01-08 08:38:46,691 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:46).
|
| 262 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 263 |
+
2026-01-08 08:38:47,063 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 264 |
+
2026-01-08 08:38:47,064 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 265 |
+
2026-01-08 08:38:47,064 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 266 |
+
2026-01-08 08:38:52,626 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:52).
|
| 267 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 268 |
+
2026-01-08 08:38:53,552 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:53).
|
| 269 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 270 |
+
2026-01-08 08:38:53,979 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 271 |
+
2026-01-08 08:38:53,985 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 272 |
+
2026-01-08 08:38:53,987 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 273 |
+
2026-01-08 08:38:59,186 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:38:59).
|
| 274 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 275 |
+
2026-01-08 08:39:00,123 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:00).
|
| 276 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 277 |
+
2026-01-08 08:39:00,571 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 278 |
+
2026-01-08 08:39:00,582 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 279 |
+
2026-01-08 08:39:00,582 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 280 |
+
2026-01-08 08:39:05,847 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:05).
|
| 281 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 282 |
+
2026-01-08 08:39:06,742 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:06).
|
| 283 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 284 |
+
2026-01-08 08:39:06,743 - scalper - INFO - log_performance:140 - PERFORMANCE - ALL (5min): emergency_stop: 0.00, open_positions: 0.00, daily_pnl: 0.00, daily_trades: 0.00, win_rate: 0.00, total_trades: 0.00, positions: [], total_pnl: 0.00
|
| 285 |
+
2026-01-08 08:39:07,112 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 286 |
+
2026-01-08 08:39:07,112 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 287 |
+
2026-01-08 08:39:07,112 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 288 |
+
2026-01-08 08:39:12,351 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:12).
|
| 289 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 290 |
+
2026-01-08 08:39:13,238 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:13).
|
| 291 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 292 |
+
2026-01-08 08:39:13,615 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 293 |
+
2026-01-08 08:39:13,615 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 294 |
+
2026-01-08 08:39:13,615 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 295 |
+
2026-01-08 08:39:18,856 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:18).
|
| 296 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 297 |
+
2026-01-08 08:39:19,812 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:19).
|
| 298 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 299 |
+
2026-01-08 08:39:20,180 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 300 |
+
2026-01-08 08:39:20,181 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 301 |
+
2026-01-08 08:39:20,181 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 302 |
+
2026-01-08 08:39:25,602 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:25).
|
| 303 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 304 |
+
2026-01-08 08:39:26,333 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:26).
|
| 305 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 306 |
+
2026-01-08 08:39:26,716 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 307 |
+
2026-01-08 08:39:26,716 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 308 |
+
2026-01-08 08:39:26,716 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 309 |
+
2026-01-08 08:39:32,309 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:32).
|
| 310 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 311 |
+
2026-01-08 08:39:32,992 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:32).
|
| 312 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 313 |
+
2026-01-08 08:39:33,690 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 314 |
+
2026-01-08 08:39:33,690 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 315 |
+
2026-01-08 08:39:33,690 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 316 |
+
2026-01-08 08:39:38,808 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:38).
|
| 317 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 318 |
+
2026-01-08 08:39:39,878 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:39).
|
| 319 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 320 |
+
2026-01-08 08:39:40,254 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 321 |
+
2026-01-08 08:39:40,254 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 322 |
+
2026-01-08 08:39:40,255 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 323 |
+
2026-01-08 08:39:45,492 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:45).
|
| 324 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 325 |
+
2026-01-08 08:39:46,402 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:46).
|
| 326 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 327 |
+
2026-01-08 08:39:46,780 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 328 |
+
2026-01-08 08:39:46,780 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 329 |
+
2026-01-08 08:39:46,780 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 330 |
+
2026-01-08 08:39:52,342 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:52).
|
| 331 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 332 |
+
2026-01-08 08:39:52,896 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:52).
|
| 333 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 334 |
+
2026-01-08 08:39:53,272 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 335 |
+
2026-01-08 08:39:53,272 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 336 |
+
2026-01-08 08:39:53,272 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 337 |
+
2026-01-08 08:39:59,328 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:59).
|
| 338 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 339 |
+
2026-01-08 08:39:59,702 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:39:59).
|
| 340 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 341 |
+
2026-01-08 08:40:00,070 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 342 |
+
2026-01-08 08:40:00,071 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 343 |
+
2026-01-08 08:40:00,071 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 344 |
+
2026-01-08 08:40:05,835 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:05).
|
| 345 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 346 |
+
2026-01-08 08:40:06,262 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:06).
|
| 347 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 348 |
+
2026-01-08 08:40:06,649 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 349 |
+
2026-01-08 08:40:06,650 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 350 |
+
2026-01-08 08:40:06,650 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 351 |
+
2026-01-08 08:40:12,407 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:12).
|
| 352 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 353 |
+
2026-01-08 08:40:12,801 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:12).
|
| 354 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 355 |
+
2026-01-08 08:40:13,171 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 356 |
+
2026-01-08 08:40:13,172 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 357 |
+
2026-01-08 08:40:13,172 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 358 |
+
2026-01-08 08:40:18,963 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:18).
|
| 359 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 360 |
+
2026-01-08 08:40:19,325 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:19).
|
| 361 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 362 |
+
2026-01-08 08:40:19,710 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 363 |
+
2026-01-08 08:40:19,710 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 364 |
+
2026-01-08 08:40:19,711 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 365 |
+
2026-01-08 08:40:25,470 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:25).
|
| 366 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 367 |
+
2026-01-08 08:40:25,835 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Missing some parameters that must be filled in, symbol or settleCoin (ErrCode: 10001) (ErrTime: 14:40:25).
|
| 368 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 369 |
+
2026-01-08 08:40:26,222 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 370 |
+
2026-01-08 08:40:26,222 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 371 |
+
2026-01-08 08:40:26,222 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 372 |
+
2026-01-08 09:18:16,857 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: HTTPSConnectionPool(host='api.bybit.com', port=443): Max retries exceeded with url: /v5/position/list?category=linear (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at 0x7fa8c7dadbe0>: Failed to resolve 'api.bybit.com' ([Errno -3] Temporary failure in name resolution)"))
|
| 373 |
+
2026-01-08 09:18:16,925 - scalper - INFO - log_performance:140 - PERFORMANCE - ALL (5min): emergency_stop: 0.00, open_positions: 0.00, daily_pnl: 0.00, daily_trades: 0.00, win_rate: 0.00, total_trades: 0.00, positions: [], total_pnl: 0.00
|
| 374 |
+
2026-01-08 09:18:17,152 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: HTTPSConnectionPool(host='api.bybit.com', port=443): Max retries exceeded with url: /v5/position/list?category=linear (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at 0x7f87b3572900>: Failed to resolve 'api.bybit.com' ([Errno -3] Temporary failure in name resolution)"))
|
| 375 |
+
2026-01-08 09:18:17,152 - scalper - INFO - log_performance:140 - PERFORMANCE - ALL (5min): emergency_stop: 0.00, open_positions: 0.00, daily_pnl: 0.00, daily_trades: 0.00, win_rate: 0.00, total_trades: 0.00, positions: [], total_pnl: 0.00
|
| 376 |
+
2026-01-08 09:18:20,913 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 377 |
+
2026-01-08 09:18:20,913 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 378 |
+
2026-01-08 09:18:20,913 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 379 |
+
2026-01-08 09:18:27,355 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:27).
|
| 380 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 381 |
+
2026-01-08 09:18:27,661 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 382 |
+
2026-01-08 09:18:27,661 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 383 |
+
2026-01-08 09:18:27,661 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 384 |
+
2026-01-08 09:18:27,775 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:27).
|
| 385 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 386 |
+
2026-01-08 09:18:33,629 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:33).
|
| 387 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 388 |
+
2026-01-08 09:18:33,942 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 389 |
+
2026-01-08 09:18:33,942 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 390 |
+
2026-01-08 09:18:33,942 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 391 |
+
2026-01-08 09:18:34,384 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:34).
|
| 392 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 393 |
+
2026-01-08 09:18:39,888 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:39).
|
| 394 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 395 |
+
2026-01-08 09:18:40,197 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 396 |
+
2026-01-08 09:18:40,198 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 397 |
+
2026-01-08 09:18:40,198 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 398 |
+
2026-01-08 09:18:40,635 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:40).
|
| 399 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 400 |
+
2026-01-08 09:18:46,160 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:46).
|
| 401 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 402 |
+
2026-01-08 09:18:46,478 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 403 |
+
2026-01-08 09:18:46,478 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 404 |
+
2026-01-08 09:18:46,479 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 405 |
+
2026-01-08 09:18:46,923 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:46).
|
| 406 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 407 |
+
2026-01-08 09:18:52,447 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:52).
|
| 408 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 409 |
+
2026-01-08 09:18:52,763 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 410 |
+
2026-01-08 09:18:52,763 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 411 |
+
2026-01-08 09:18:52,764 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 412 |
+
2026-01-08 09:18:53,247 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:53).
|
| 413 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 414 |
+
2026-01-08 09:18:58,713 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:58).
|
| 415 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 416 |
+
2026-01-08 09:18:59,036 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 417 |
+
2026-01-08 09:18:59,036 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 418 |
+
2026-01-08 09:18:59,037 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 419 |
+
2026-01-08 09:18:59,869 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:18:59).
|
| 420 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 421 |
+
2026-01-08 09:19:04,983 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:04).
|
| 422 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 423 |
+
2026-01-08 09:19:05,321 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 424 |
+
2026-01-08 09:19:05,322 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 425 |
+
2026-01-08 09:19:05,322 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 426 |
+
2026-01-08 09:19:06,149 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:06).
|
| 427 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 428 |
+
2026-01-08 09:19:11,307 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:11).
|
| 429 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 430 |
+
2026-01-08 09:19:11,621 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 431 |
+
2026-01-08 09:19:11,621 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 432 |
+
2026-01-08 09:19:11,621 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 433 |
+
2026-01-08 09:19:12,464 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:12).
|
| 434 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 435 |
+
2026-01-08 09:19:17,567 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:17).
|
| 436 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 437 |
+
2026-01-08 09:19:17,875 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 438 |
+
2026-01-08 09:19:17,875 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 439 |
+
2026-01-08 09:19:17,875 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 440 |
+
2026-01-08 09:19:18,735 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:18).
|
| 441 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 442 |
+
2026-01-08 09:19:23,898 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:23).
|
| 443 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 444 |
+
2026-01-08 09:19:24,249 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 445 |
+
2026-01-08 09:19:24,249 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 446 |
+
2026-01-08 09:19:24,250 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 447 |
+
2026-01-08 09:19:25,033 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:25).
|
| 448 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 449 |
+
2026-01-08 09:19:30,217 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:30).
|
| 450 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 451 |
+
2026-01-08 09:19:30,551 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 452 |
+
2026-01-08 09:19:30,551 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 453 |
+
2026-01-08 09:19:30,552 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 454 |
+
2026-01-08 09:19:31,983 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:31).
|
| 455 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 456 |
+
2026-01-08 09:19:36,549 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:36).
|
| 457 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 458 |
+
2026-01-08 09:19:36,882 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 459 |
+
2026-01-08 09:19:36,882 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 460 |
+
2026-01-08 09:19:36,882 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 461 |
+
2026-01-08 09:19:38,289 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:38).
|
| 462 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 463 |
+
2026-01-08 09:19:42,883 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:42).
|
| 464 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 465 |
+
2026-01-08 09:19:43,200 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 466 |
+
2026-01-08 09:19:43,201 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 467 |
+
2026-01-08 09:19:43,201 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 468 |
+
2026-01-08 09:19:44,959 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:44).
|
| 469 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 470 |
+
2026-01-08 09:19:49,179 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:49).
|
| 471 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 472 |
+
2026-01-08 09:19:49,815 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 473 |
+
2026-01-08 09:19:49,815 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 474 |
+
2026-01-08 09:19:49,815 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 475 |
+
2026-01-08 09:19:51,268 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:51).
|
| 476 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 477 |
+
2026-01-08 09:19:56,099 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:56).
|
| 478 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 479 |
+
2026-01-08 09:19:56,432 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 480 |
+
2026-01-08 09:19:56,432 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 481 |
+
2026-01-08 09:19:56,432 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 482 |
+
2026-01-08 09:19:57,589 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:19:57).
|
| 483 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 484 |
+
2026-01-08 09:20:02,381 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:02).
|
| 485 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 486 |
+
2026-01-08 09:20:02,695 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 487 |
+
2026-01-08 09:20:02,696 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 488 |
+
2026-01-08 09:20:02,696 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 489 |
+
2026-01-08 09:20:03,432 - scalper - INFO - log:205 - π Shutdown signal received
|
| 490 |
+
2026-01-08 09:20:03,434 - scalper - INFO - log:205 - π Shutdown signal received
|
| 491 |
+
2026-01-08 09:20:03,854 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:03).
|
| 492 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 493 |
+
2026-01-08 09:20:08,654 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:08).
|
| 494 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 495 |
+
2026-01-08 09:20:09,284 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 496 |
+
2026-01-08 09:20:09,286 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 497 |
+
2026-01-08 09:20:09,287 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 498 |
+
2026-01-08 09:20:10,120 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:10).
|
| 499 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 500 |
+
2026-01-08 09:20:15,315 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:15).
|
| 501 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 502 |
+
2026-01-08 09:20:15,631 - scalper - INFO - log:205 - β οΈ Low price buffer for BTCUSDT: 0 points
|
| 503 |
+
2026-01-08 09:20:15,632 - scalper - INFO - log:205 - β οΈ Low price buffer for ETHUSDT: 0 points
|
| 504 |
+
2026-01-08 09:20:15,632 - scalper - INFO - log:205 - β οΈ Low price buffer for SOLUSDT: 0 points
|
| 505 |
+
2026-01-08 09:20:16,417 - scalper - ERROR - log_error:125 - POSITION_CHECK: Error checking positions: Unmatched IP, please check your API key's bound IP addresses. (ErrCode: 10010) (ErrTime: 15:20:16).
|
| 506 |
+
Request β GET https://api.bybit.com/v5/position/list: category=linear.
|
| 507 |
+
2026-01-08 09:33:54,347 - scalper - INFO - log:205 - π Starting FastAPI server for Bybit Scalping Bot
|
| 508 |
+
2026-01-08 09:38:22,572 - scalper - INFO - log:205 - π Starting FastAPI server for Bybit Scalping Bot
|
| 509 |
+
2026-01-08 09:39:30,629 - scalper - INFO - log:205 - π Started trading session for BTCUSDT (18h duration)
|
| 510 |
+
2026-01-08 09:39:30,631 - scalper - INFO - log:205 - π Starting trading session for BTCUSDT (Duration: 18h)
|
| 511 |
+
2026-01-08 10:39:14,500 - scalper - INFO - log:205 - π Trade monitoring stopped
|
| 512 |
+
2026-01-08 10:39:15,586 - scalper - INFO - log:205 - π Stopped trading for BTCUSDT
|
logs/trades.log
ADDED
|
File without changes
|
main.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Bybit Scalping Bot - FastAPI Control Interface
|
| 4 |
+
|
| 5 |
+
This bot now uses FastAPI for remote control instead of direct command line operation.
|
| 6 |
+
Use the API endpoints to start/stop trading sessions for individual pairs.
|
| 7 |
+
|
| 8 |
+
API Endpoints:
|
| 9 |
+
- GET /status - Get bot status
|
| 10 |
+
- POST /start/{symbol} - Start trading for a symbol (18h default)
|
| 11 |
+
- POST /stop/{symbol} - Stop trading for a symbol
|
| 12 |
+
- GET /sessions - Get all trading sessions
|
| 13 |
+
- GET /report/{session_id} - Get session report
|
| 14 |
+
- POST /emergency_stop - Emergency stop all trading
|
| 15 |
+
|
| 16 |
+
Examples:
|
| 17 |
+
- Start BTC trading: POST /start/BTCUSDT
|
| 18 |
+
- Stop ETH trading: POST /stop/ETHUSDT
|
| 19 |
+
- Check status: GET /status
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
import sys
|
| 23 |
+
import subprocess
|
| 24 |
+
import os
|
| 25 |
+
|
| 26 |
+
def show_usage():
|
| 27 |
+
print("""
|
| 28 |
+
π Bybit Scalping Bot - FastAPI Control Interface
|
| 29 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 30 |
+
|
| 31 |
+
π‘ API SERVER CONTROL:
|
| 32 |
+
β’ Start API Server: python api_server.py
|
| 33 |
+
β’ Server runs on: http://localhost:8000
|
| 34 |
+
|
| 35 |
+
π API ENDPOINTS:
|
| 36 |
+
|
| 37 |
+
GET /status
|
| 38 |
+
Returns current bot status and active sessions
|
| 39 |
+
|
| 40 |
+
POST /start/{symbol}?duration_hours=18
|
| 41 |
+
Start trading session for specific symbol
|
| 42 |
+
Examples:
|
| 43 |
+
β’ POST /start/BTCUSDT
|
| 44 |
+
β’ POST /start/ETHUSDT?duration_hours=24
|
| 45 |
+
|
| 46 |
+
POST /stop/{symbol}
|
| 47 |
+
Stop trading session for specific symbol
|
| 48 |
+
Example: POST /stop/BTCUSDT
|
| 49 |
+
|
| 50 |
+
GET /sessions
|
| 51 |
+
Get all trading sessions (active and completed)
|
| 52 |
+
|
| 53 |
+
GET /report/{session_id}
|
| 54 |
+
Get detailed report for a completed session
|
| 55 |
+
|
| 56 |
+
POST /emergency_stop
|
| 57 |
+
Emergency stop all trading sessions
|
| 58 |
+
|
| 59 |
+
π§ͺ TESTING & BACKTESTING:
|
| 60 |
+
|
| 61 |
+
python main.py backtest
|
| 62 |
+
Run backtesting for all configured pairs
|
| 63 |
+
|
| 64 |
+
python test_api_raw.py
|
| 65 |
+
Test API connectivity
|
| 66 |
+
|
| 67 |
+
π CONFIGURED PAIRS:
|
| 68 |
+
β’ BTCUSDT
|
| 69 |
+
β’ ETHUSDT
|
| 70 |
+
β’ SOLUSDT
|
| 71 |
+
|
| 72 |
+
βοΈ TRADING PARAMETERS:
|
| 73 |
+
β’ Leverage: 20x
|
| 74 |
+
β’ Take Profit: 2.5%
|
| 75 |
+
β’ Stop Loss: 1%
|
| 76 |
+
β’ Risk per Trade: 2%
|
| 77 |
+
β’ Strategy: EMA 9/21 + RSI 14 + Volume + Orderbook
|
| 78 |
+
|
| 79 |
+
π± TELEGRAM ALERTS:
|
| 80 |
+
Configure in .env file for trade notifications
|
| 81 |
+
|
| 82 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 83 |
+
""")
|
| 84 |
+
|
| 85 |
+
def run_backtest():
|
| 86 |
+
"""Run backtesting mode"""
|
| 87 |
+
from backtester.engine import BacktestingEngine
|
| 88 |
+
from services.logger import log
|
| 89 |
+
import yaml
|
| 90 |
+
|
| 91 |
+
log("π¬ Starting backtesting mode")
|
| 92 |
+
|
| 93 |
+
settings = yaml.safe_load(open("config/settings.yaml"))
|
| 94 |
+
pairs = yaml.safe_load(open("config/pairs.yaml"))["pairs"]
|
| 95 |
+
|
| 96 |
+
backtester = BacktestingEngine()
|
| 97 |
+
results = {}
|
| 98 |
+
|
| 99 |
+
for symbol in pairs:
|
| 100 |
+
log(f"π Backtesting {symbol}...")
|
| 101 |
+
result = backtester.run_backtest(symbol)
|
| 102 |
+
results[symbol] = result
|
| 103 |
+
|
| 104 |
+
if 'error' not in result:
|
| 105 |
+
log(f"β
{symbol}: {result['total_trades']} trades, PnL: ${result['total_pnl']:.2f}, Win Rate: {result['win_rate']:.1%}")
|
| 106 |
+
else:
|
| 107 |
+
log(f"β {symbol}: {result['error']}")
|
| 108 |
+
|
| 109 |
+
backtester.save_results()
|
| 110 |
+
|
| 111 |
+
log("\n" + "="*50)
|
| 112 |
+
log("BACKTESTING REPORT")
|
| 113 |
+
log("="*50)
|
| 114 |
+
|
| 115 |
+
for symbol in pairs:
|
| 116 |
+
if symbol in results and 'error' not in results[symbol]:
|
| 117 |
+
report = backtester.generate_report(symbol)
|
| 118 |
+
print(report)
|
| 119 |
+
|
| 120 |
+
def start_api_server():
|
| 121 |
+
"""Start the FastAPI server"""
|
| 122 |
+
print("π Starting FastAPI server...")
|
| 123 |
+
print("π‘ Server will be available at: http://localhost:8000")
|
| 124 |
+
print("π API documentation at: http://localhost:8000/docs")
|
| 125 |
+
print()
|
| 126 |
+
try:
|
| 127 |
+
subprocess.run([sys.executable, "api_server.py"])
|
| 128 |
+
except KeyboardInterrupt:
|
| 129 |
+
print("\nπ API server stopped")
|
| 130 |
+
|
| 131 |
+
if __name__ == "__main__":
|
| 132 |
+
if len(sys.argv) > 1:
|
| 133 |
+
if sys.argv[1] == "backtest":
|
| 134 |
+
run_backtest()
|
| 135 |
+
elif sys.argv[1] == "api":
|
| 136 |
+
start_api_server()
|
| 137 |
+
elif sys.argv[1] == "help" or sys.argv[1] == "-h":
|
| 138 |
+
show_usage()
|
| 139 |
+
else:
|
| 140 |
+
print(f"β Unknown command: {sys.argv[1]}")
|
| 141 |
+
print("Use 'python main.py help' for available commands")
|
| 142 |
+
else:
|
| 143 |
+
show_usage()
|
requirements.txt
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
python-dotenv
|
| 2 |
+
pybit
|
| 3 |
+
pandas
|
| 4 |
+
numpy
|
| 5 |
+
pyyaml
|
| 6 |
+
requests
|
| 7 |
+
websockets
|
| 8 |
+
asyncio
|
| 9 |
+
ccxt
|
| 10 |
+
ta
|
| 11 |
+
plotly
|
| 12 |
+
matplotlib
|
| 13 |
+
seaborn
|
| 14 |
+
scikit-learn
|
| 15 |
+
fastapi
|
| 16 |
+
uvicorn
|
| 17 |
+
streamlit
|
services/logger.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import logging.handlers
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Dict, Any, Optional
|
| 7 |
+
import yaml
|
| 8 |
+
|
| 9 |
+
try:
|
| 10 |
+
settings = yaml.safe_load(open("config/settings.yaml"))
|
| 11 |
+
except:
|
| 12 |
+
settings = {"telegram": {"enabled": False}}
|
| 13 |
+
|
| 14 |
+
class ScalperLogger:
|
| 15 |
+
def __init__(self):
|
| 16 |
+
self.setup_logging()
|
| 17 |
+
self.trade_logs = []
|
| 18 |
+
self.signal_logs = []
|
| 19 |
+
self.error_logs = []
|
| 20 |
+
|
| 21 |
+
def setup_logging(self):
|
| 22 |
+
os.makedirs("logs", exist_ok=True)
|
| 23 |
+
self.logger = logging.getLogger('scalper')
|
| 24 |
+
self.logger.setLevel(logging.DEBUG)
|
| 25 |
+
self.logger.handlers.clear()
|
| 26 |
+
console_handler = logging.StreamHandler()
|
| 27 |
+
console_handler.setLevel(logging.INFO)
|
| 28 |
+
console_formatter = logging.Formatter(
|
| 29 |
+
'%(asctime)s - %(levelname)s - %(message)s'
|
| 30 |
+
)
|
| 31 |
+
console_handler.setFormatter(console_formatter)
|
| 32 |
+
self.logger.addHandler(console_handler)
|
| 33 |
+
file_handler = logging.handlers.RotatingFileHandler(
|
| 34 |
+
'logs/scalper.log',
|
| 35 |
+
maxBytes=10*1024*1024,
|
| 36 |
+
backupCount=5
|
| 37 |
+
)
|
| 38 |
+
file_handler.setLevel(logging.DEBUG)
|
| 39 |
+
file_formatter = logging.Formatter(
|
| 40 |
+
'%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
|
| 41 |
+
)
|
| 42 |
+
file_handler.setFormatter(file_formatter)
|
| 43 |
+
self.logger.addHandler(file_handler)
|
| 44 |
+
trade_handler = logging.handlers.RotatingFileHandler(
|
| 45 |
+
'logs/trades.log',
|
| 46 |
+
maxBytes=5*1024*1024,
|
| 47 |
+
backupCount=3
|
| 48 |
+
)
|
| 49 |
+
trade_handler.setLevel(logging.INFO)
|
| 50 |
+
trade_formatter = logging.Formatter(
|
| 51 |
+
'%(asctime)s - TRADE - %(message)s'
|
| 52 |
+
)
|
| 53 |
+
trade_handler.setFormatter(trade_formatter)
|
| 54 |
+
trade_handler.addFilter(self._trade_filter)
|
| 55 |
+
self.logger.addHandler(trade_handler)
|
| 56 |
+
error_handler = logging.handlers.RotatingFileHandler(
|
| 57 |
+
'logs/errors.log',
|
| 58 |
+
maxBytes=5*1024*1024,
|
| 59 |
+
backupCount=3
|
| 60 |
+
)
|
| 61 |
+
error_handler.setLevel(logging.ERROR)
|
| 62 |
+
error_formatter = logging.Formatter(
|
| 63 |
+
'%(asctime)s - ERROR - %(message)s'
|
| 64 |
+
)
|
| 65 |
+
error_handler.setFormatter(error_formatter)
|
| 66 |
+
self.logger.addHandler(error_handler)
|
| 67 |
+
|
| 68 |
+
def _trade_filter(self, record):
|
| 69 |
+
return 'TRADE' in record.getMessage() or 'SIGNAL' in record.getMessage()
|
| 70 |
+
|
| 71 |
+
def log_signal(self, symbol: str, signal: str, confidence: float, price: float,
|
| 72 |
+
strategy_details: Optional[Dict[str, Any]] = None):
|
| 73 |
+
try:
|
| 74 |
+
signal_data = {
|
| 75 |
+
'timestamp': datetime.now().isoformat(),
|
| 76 |
+
'symbol': symbol,
|
| 77 |
+
'signal': signal,
|
| 78 |
+
'confidence': confidence,
|
| 79 |
+
'price': price,
|
| 80 |
+
'strategy_details': strategy_details or {}
|
| 81 |
+
}
|
| 82 |
+
self.signal_logs.append(signal_data)
|
| 83 |
+
if len(self.signal_logs) > 1000:
|
| 84 |
+
self.signal_logs = self.signal_logs[-1000:]
|
| 85 |
+
self._save_signals_to_file()
|
| 86 |
+
self.logger.info(f"SIGNAL - {symbol}: {signal} (conf: {confidence:.2f}) at {price}")
|
| 87 |
+
except Exception as e:
|
| 88 |
+
self.logger.error(f"Error logging signal: {e}")
|
| 89 |
+
|
| 90 |
+
def log_trade(self, symbol: str, side: str, qty: float, price: float,
|
| 91 |
+
order_type: str = "MARKET", pnl: Optional[float] = None,
|
| 92 |
+
reason: str = "signal"):
|
| 93 |
+
try:
|
| 94 |
+
trade_data = {
|
| 95 |
+
'timestamp': datetime.now().isoformat(),
|
| 96 |
+
'symbol': symbol,
|
| 97 |
+
'side': side,
|
| 98 |
+
'quantity': qty,
|
| 99 |
+
'price': price,
|
| 100 |
+
'order_type': order_type,
|
| 101 |
+
'pnl': pnl,
|
| 102 |
+
'reason': reason
|
| 103 |
+
}
|
| 104 |
+
self.trade_logs.append(trade_data)
|
| 105 |
+
if len(self.trade_logs) > 1000:
|
| 106 |
+
self.trade_logs = self.trade_logs[-1000:]
|
| 107 |
+
self._save_trades_to_file()
|
| 108 |
+
pnl_str = f" (PnL: {pnl:.2f})" if pnl is not None else ""
|
| 109 |
+
self.logger.info(f"TRADE - {symbol}: {side} {qty} at {price}{pnl_str}")
|
| 110 |
+
except Exception as e:
|
| 111 |
+
self.logger.error(f"Error logging trade: {e}")
|
| 112 |
+
|
| 113 |
+
def log_error(self, error_type: str, message: str, details: Optional[Dict[str, Any]] = None):
|
| 114 |
+
try:
|
| 115 |
+
error_data = {
|
| 116 |
+
'timestamp': datetime.now().isoformat(),
|
| 117 |
+
'type': error_type,
|
| 118 |
+
'message': message,
|
| 119 |
+
'details': details or {}
|
| 120 |
+
}
|
| 121 |
+
self.error_logs.append(error_data)
|
| 122 |
+
if len(self.error_logs) > 500:
|
| 123 |
+
self.error_logs = self.error_logs[-500:]
|
| 124 |
+
self._save_errors_to_file()
|
| 125 |
+
self.logger.error(f"{error_type.upper()}: {message}")
|
| 126 |
+
except Exception as e:
|
| 127 |
+
print(f"Critical logging error: {e}")
|
| 128 |
+
|
| 129 |
+
def log_performance(self, symbol: str, period: str, metrics: Dict[str, Any]):
|
| 130 |
+
try:
|
| 131 |
+
perf_data = {
|
| 132 |
+
'timestamp': datetime.now().isoformat(),
|
| 133 |
+
'symbol': symbol,
|
| 134 |
+
'period': period,
|
| 135 |
+
'metrics': metrics
|
| 136 |
+
}
|
| 137 |
+
self._save_performance_to_file(perf_data)
|
| 138 |
+
metrics_str = ", ".join([f"{k}: {v:.2f}" if isinstance(v, (int, float)) else f"{k}: {v}"
|
| 139 |
+
for k, v in metrics.items()])
|
| 140 |
+
self.logger.info(f"PERFORMANCE - {symbol} ({period}): {metrics_str}")
|
| 141 |
+
except Exception as e:
|
| 142 |
+
self.logger.error(f"Error logging performance: {e}")
|
| 143 |
+
|
| 144 |
+
def _save_signals_to_file(self):
|
| 145 |
+
try:
|
| 146 |
+
with open('logs/signals.json', 'w') as f:
|
| 147 |
+
json.dump(self.signal_logs, f, indent=2)
|
| 148 |
+
except Exception as e:
|
| 149 |
+
self.logger.error(f"Error saving signals: {e}")
|
| 150 |
+
|
| 151 |
+
def _save_trades_to_file(self):
|
| 152 |
+
try:
|
| 153 |
+
with open('logs/trades.json', 'w') as f:
|
| 154 |
+
json.dump(self.trade_logs, f, indent=2)
|
| 155 |
+
except Exception as e:
|
| 156 |
+
self.logger.error(f"Error saving trades: {e}")
|
| 157 |
+
|
| 158 |
+
def _save_errors_to_file(self):
|
| 159 |
+
try:
|
| 160 |
+
with open('logs/errors.json', 'w') as f:
|
| 161 |
+
json.dump(self.error_logs, f, indent=2)
|
| 162 |
+
except Exception as e:
|
| 163 |
+
print(f"Error saving errors: {e}")
|
| 164 |
+
|
| 165 |
+
def _save_performance_to_file(self, perf_data: Dict[str, Any]):
|
| 166 |
+
try:
|
| 167 |
+
with open('logs/performance.jsonl', 'a') as f:
|
| 168 |
+
json.dump(perf_data, f)
|
| 169 |
+
f.write('\n')
|
| 170 |
+
except Exception as e:
|
| 171 |
+
self.logger.error(f"Error saving performance: {e}")
|
| 172 |
+
|
| 173 |
+
def get_recent_signals(self, limit: int = 50) -> list:
|
| 174 |
+
return self.signal_logs[-limit:]
|
| 175 |
+
|
| 176 |
+
def get_recent_trades(self, limit: int = 50) -> list:
|
| 177 |
+
return self.trade_logs[-limit:]
|
| 178 |
+
|
| 179 |
+
def get_error_summary(self) -> Dict[str, int]:
|
| 180 |
+
summary = {}
|
| 181 |
+
for error in self.error_logs:
|
| 182 |
+
error_type = error.get('type', 'unknown')
|
| 183 |
+
summary[error_type] = summary.get(error_type, 0) + 1
|
| 184 |
+
return summary
|
| 185 |
+
|
| 186 |
+
def get_trade_statistics(self) -> Dict[str, Any]:
|
| 187 |
+
if not self.trade_logs:
|
| 188 |
+
return {'total_trades': 0, 'win_rate': 0.0, 'total_pnl': 0.0}
|
| 189 |
+
total_trades = len(self.trade_logs)
|
| 190 |
+
winning_trades = sum(1 for trade in self.trade_logs if trade.get('pnl', 0) > 0)
|
| 191 |
+
total_pnl = sum(trade.get('pnl', 0) for trade in self.trade_logs)
|
| 192 |
+
return {
|
| 193 |
+
'total_trades': total_trades,
|
| 194 |
+
'win_rate': winning_trades / total_trades if total_trades > 0 else 0.0,
|
| 195 |
+
'total_pnl': total_pnl,
|
| 196 |
+
'avg_pnl_per_trade': total_pnl / total_trades if total_trades > 0 else 0.0
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
logger_instance = ScalperLogger()
|
| 200 |
+
|
| 201 |
+
def log(message: str, level: str = "info"):
|
| 202 |
+
if level.lower() == "debug":
|
| 203 |
+
logger_instance.logger.debug(message)
|
| 204 |
+
elif level.lower() == "info":
|
| 205 |
+
logger_instance.logger.info(message)
|
| 206 |
+
elif level.lower() == "warning":
|
| 207 |
+
logger_instance.logger.warning(message)
|
| 208 |
+
elif level.lower() == "error":
|
| 209 |
+
logger_instance.logger.error(message)
|
| 210 |
+
elif level.lower() == "critical":
|
| 211 |
+
logger_instance.logger.critical(message)
|
| 212 |
+
|
| 213 |
+
def log_signal(symbol: str, signal: str, confidence: float, price: float,
|
| 214 |
+
strategy_details: Optional[Dict[str, Any]] = None):
|
| 215 |
+
logger_instance.log_signal(symbol, signal, confidence, price, strategy_details)
|
| 216 |
+
|
| 217 |
+
def log_trade(symbol: str, side: str, qty: float, price: float,
|
| 218 |
+
order_type: str = "MARKET", pnl: Optional[float] = None,
|
| 219 |
+
reason: str = "signal"):
|
| 220 |
+
logger_instance.log_trade(symbol, side, qty, price, order_type, pnl, reason)
|
| 221 |
+
|
| 222 |
+
def log_error(error_type: str, message: str, details: Optional[Dict[str, Any]] = None):
|
| 223 |
+
logger_instance.log_error(error_type, message, details)
|
| 224 |
+
|
| 225 |
+
def log_performance(symbol: str, period: str, metrics: Dict[str, Any]):
|
| 226 |
+
logger_instance.log_performance(symbol, period, metrics)
|
services/telegram.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import requests
|
| 3 |
+
from dotenv import load_dotenv
|
| 4 |
+
import yaml
|
| 5 |
+
|
| 6 |
+
load_dotenv()
|
| 7 |
+
settings = yaml.safe_load(open("config/settings.yaml"))
|
| 8 |
+
|
| 9 |
+
BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
|
| 10 |
+
CHAT_ID = os.getenv("TELEGRAM_CHAT_ID")
|
| 11 |
+
|
| 12 |
+
def send_telegram(msg):
|
| 13 |
+
if not settings["telegram"]["enabled"]:
|
| 14 |
+
return
|
| 15 |
+
|
| 16 |
+
if not BOT_TOKEN or not CHAT_ID:
|
| 17 |
+
print("[WARNING] Telegram not configured.")
|
| 18 |
+
return
|
| 19 |
+
|
| 20 |
+
url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
|
| 21 |
+
requests.post(url, data={"chat_id": CHAT_ID, "text": msg})
|
test/MAINNET_STATUS.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# π MAINNET CONFIGURATION COMPLETE
|
| 2 |
+
|
| 3 |
+
β
API Connection: WORKING
|
| 4 |
+
β
Account Balance: ACCESSIBLE
|
| 5 |
+
β
Position Monitoring: WORKING
|
| 6 |
+
β
Order Monitoring: WORKING
|
| 7 |
+
β οΈ Order Creation: BLOCKED (Symbol whitelisting)
|
| 8 |
+
|
| 9 |
+
The bot is ready for monitoring and analysis on mainnet!
|
| 10 |
+
For live trading, BTCUSDT needs to be whitelisted for your API key.
|
| 11 |
+
|
| 12 |
+
Current .env configuration:
|
| 13 |
+
- BYBIT_TESTNET=false (Mainnet enabled)
|
| 14 |
+
- API credentials loaded
|
| 15 |
+
|
| 16 |
+
Ready to run: python3 main.py
|
test/README.md
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Bybit Scalping Bot π€
|
| 2 |
+
|
| 3 |
+
A production-ready cryptocurrency scalping bot that trades BTCUSDT, ETHUSDT, and SOLUSDT using multiple sophisticated strategies with 20x leverage and automated risk management.
|
| 4 |
+
|
| 5 |
+

|
| 6 |
+

|
| 7 |
+

|
| 8 |
+
|
| 9 |
+
## β‘ Features
|
| 10 |
+
|
| 11 |
+
- **Multi-Strategy Trading**: Three advanced scalping strategies (EMA Momentum, Breakout, Pullback)
|
| 12 |
+
- **20x Leverage**: Configurable leverage with automatic position sizing
|
| 13 |
+
- **Automated TP/SL**: Take profit at 2.5%, stop loss at 1%
|
| 14 |
+
- **Real-Time Data**: WebSocket streaming for ultra-fast signal generation
|
| 15 |
+
- **Risk Management**: Dynamic position sizing, daily loss limits, volatility filters
|
| 16 |
+
- **Backtesting Engine**: Historical performance analysis and parameter optimization
|
| 17 |
+
- **Comprehensive Logging**: Detailed trade logs, performance metrics, error tracking
|
| 18 |
+
- **Telegram Notifications**: Real-time trade alerts and bot status updates
|
| 19 |
+
- **Production Ready**: Error handling, reconnection logic, graceful shutdown
|
| 20 |
+
|
| 21 |
+
## π Quick Start
|
| 22 |
+
|
| 23 |
+
### 1. Environment Setup
|
| 24 |
+
|
| 25 |
+
```bash
|
| 26 |
+
# Clone the repository
|
| 27 |
+
git clone <repository-url>
|
| 28 |
+
cd scalper
|
| 29 |
+
|
| 30 |
+
# Install dependencies
|
| 31 |
+
pip install -r requirements.txt
|
| 32 |
+
|
| 33 |
+
# Copy environment template
|
| 34 |
+
cp .env.example .env
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
### 2. API Configuration
|
| 38 |
+
|
| 39 |
+
1. Create a Bybit account at [bybit.com](https://bybit.com)
|
| 40 |
+
2. Generate API keys in Account > API Management
|
| 41 |
+
3. **Required Permissions:**
|
| 42 |
+
- β
**Unified Trading Account** β Read & Trade
|
| 43 |
+
- β
**USDC Derivatives** β Read & Trade
|
| 44 |
+
- β
**Spot** β Read & Trade
|
| 45 |
+
4. **Important**: Add your IP address to the whitelist
|
| 46 |
+
|
| 47 |
+
Edit `.env` file:
|
| 48 |
+
```env
|
| 49 |
+
BYBIT_API_KEY=your_api_key_here
|
| 50 |
+
BYBIT_API_SECRET=your_api_secret_here
|
| 51 |
+
BYBIT_TESTNET=false # Use false for mainnet
|
| 52 |
+
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here # Optional
|
| 53 |
+
TELEGRAM_CHAT_ID=your_chat_id_here # Optional
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
### 3. Start API Server
|
| 57 |
+
|
| 58 |
+
```bash
|
| 59 |
+
# Start the FastAPI control server
|
| 60 |
+
python3 api_server.py
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
Server will be available at: **http://localhost:8000**
|
| 64 |
+
|
| 65 |
+
### 4. Control Trading via API
|
| 66 |
+
|
| 67 |
+
#### Start Trading for BTC (18 hours default):
|
| 68 |
+
```bash
|
| 69 |
+
curl -X POST http://localhost:8000/start/BTCUSDT
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
#### Start Trading for ETH (custom duration):
|
| 73 |
+
```bash
|
| 74 |
+
curl -X POST "http://localhost:8000/start/ETHUSDT?duration_hours=24"
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
#### Check Bot Status:
|
| 78 |
+
```bash
|
| 79 |
+
curl http://localhost:8000/status
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
#### Stop Trading:
|
| 83 |
+
```bash
|
| 84 |
+
curl -X POST http://localhost:8000/stop/BTCUSDT
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
### 5. Run Backtest
|
| 88 |
+
|
| 89 |
+
```bash
|
| 90 |
+
python3 main.py backtest
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
## π‘ FastAPI Control Interface
|
| 94 |
+
|
| 95 |
+
The bot uses FastAPI for remote control, allowing you to start/stop trading sessions for individual pairs.
|
| 96 |
+
|
| 97 |
+
### API Endpoints
|
| 98 |
+
|
| 99 |
+
#### `GET /status`
|
| 100 |
+
Get current bot status and active sessions.
|
| 101 |
+
```bash
|
| 102 |
+
curl http://localhost:8000/status
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
#### `POST /start/{symbol}`
|
| 106 |
+
Start trading session for a specific symbol.
|
| 107 |
+
```bash
|
| 108 |
+
# Default 18 hours
|
| 109 |
+
curl -X POST http://localhost:8000/start/BTCUSDT
|
| 110 |
+
|
| 111 |
+
# Custom duration
|
| 112 |
+
curl -X POST "http://localhost:8000/start/ETHUSDT?duration_hours=24"
|
| 113 |
+
|
| 114 |
+
# Limited trades
|
| 115 |
+
curl -X POST "http://localhost:8000/start/SOLUSDT?duration_hours=12&max_trades=50"
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
#### `POST /stop/{symbol}`
|
| 119 |
+
Stop trading session for a specific symbol.
|
| 120 |
+
```bash
|
| 121 |
+
curl -X POST http://localhost:8000/stop/BTCUSDT
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
#### `GET /sessions`
|
| 125 |
+
Get all trading sessions (active and completed).
|
| 126 |
+
```bash
|
| 127 |
+
curl http://localhost:8000/sessions
|
| 128 |
+
```
|
| 129 |
+
|
| 130 |
+
#### `GET /report/{session_id}`
|
| 131 |
+
Get detailed report for a completed session.
|
| 132 |
+
```bash
|
| 133 |
+
curl http://localhost:8000/report/BTCUSDT_1703123456
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
#### `POST /start_all`
|
| 137 |
+
Start trading sessions for ALL configured pairs simultaneously.
|
| 138 |
+
```bash
|
| 139 |
+
# Start all pairs (BTC, ETH, SOL) for 18 hours
|
| 140 |
+
curl -X POST "http://localhost:8000/start_all"
|
| 141 |
+
|
| 142 |
+
# Custom duration for all pairs
|
| 143 |
+
curl -X POST "http://localhost:8000/start_all?duration_hours=24"
|
| 144 |
+
```
|
| 145 |
+
|
| 146 |
+
#### `POST /stop_all`
|
| 147 |
+
Stop trading sessions for ALL pairs.
|
| 148 |
+
```bash
|
| 149 |
+
curl -X POST http://localhost:8000/stop_all
|
| 150 |
+
```
|
| 151 |
+
|
| 152 |
+
#### `GET /logs/analysis`
|
| 153 |
+
Get recent analysis logs (strategy signals, indicator values).
|
| 154 |
+
```bash
|
| 155 |
+
curl "http://localhost:8000/logs/analysis"
|
| 156 |
+
curl "http://localhost:8000/logs/analysis?lines=100"
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
#### `GET /logs/live`
|
| 160 |
+
Get recent live logs (all bot activities).
|
| 161 |
+
```bash
|
| 162 |
+
curl "http://localhost:8000/logs/live"
|
| 163 |
+
curl "http://localhost:8000/logs/live?lines=50"
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
#### `GET /analysis/status`
|
| 167 |
+
Get real-time analysis status for all active sessions.
|
| 168 |
+
```bash
|
| 169 |
+
curl "http://localhost:8000/analysis/status"
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
#### `POST /emergency_stop`
|
| 173 |
+
Emergency stop all trading sessions.
|
| 174 |
+
```bash
|
| 175 |
+
curl -X POST http://localhost:8000/emergency_stop
|
| 176 |
+
```
|
| 177 |
+
|
| 178 |
+
### Interactive API Documentation
|
| 179 |
+
|
| 180 |
+
When the server is running, visit: **http://localhost:8000/docs**
|
| 181 |
+
|
| 182 |
+
This provides an interactive Swagger UI to test all endpoints.
|
| 183 |
+
|
| 184 |
+
### Example Workflow
|
| 185 |
+
|
| 186 |
+
```bash
|
| 187 |
+
# 1. Start API server
|
| 188 |
+
python3 api_server.py
|
| 189 |
+
|
| 190 |
+
# 2. Start BTC trading for 18 hours
|
| 191 |
+
curl -X POST http://localhost:8000/start/BTCUSDT
|
| 192 |
+
|
| 193 |
+
# 3. Check status
|
| 194 |
+
curl http://localhost:8000/status
|
| 195 |
+
|
| 196 |
+
# 4. Start ETH trading for 24 hours
|
| 197 |
+
curl -X POST "http://localhost:8000/start/ETHUSDT?duration_hours=24"
|
| 198 |
+
|
| 199 |
+
# 5. Stop BTC trading
|
| 200 |
+
curl -X POST http://localhost:8000/stop/BTCUSDT
|
| 201 |
+
|
| 202 |
+
# 6. Get final reports
|
| 203 |
+
curl http://localhost:8000/sessions
|
| 204 |
+
```
|
| 205 |
+
|
| 206 |
+
## π Trading Strategies
|
| 207 |
+
|
| 208 |
+
### 1. EMA Momentum Scalping
|
| 209 |
+
- **Entry**: EMA9 crosses above EMA21 in bullish direction
|
| 210 |
+
- **Confirmation**: RSI oversold (<35) + bid dominance in orderbook
|
| 211 |
+
- **Exit**: 2.5% profit or 1% stop loss
|
| 212 |
+
|
| 213 |
+
### 2. Breakout Scalper
|
| 214 |
+
- **Entry**: Price breaks above recent highs with volume spike
|
| 215 |
+
- **Confirmation**: Spread contraction + orderbook imbalance
|
| 216 |
+
- **Exit**: Time-based (5-15 min) or TP/SL hit
|
| 217 |
+
|
| 218 |
+
### 3. Pullback Scalper
|
| 219 |
+
- **Entry**: Price retraces to EMA21 in trending market
|
| 220 |
+
- **Confirmation**: Volume decreases on pullback, increases on continuation
|
| 221 |
+
- **Exit**: Trend continuation signal or timeout
|
| 222 |
+
|
| 223 |
+
## ποΈ Architecture
|
| 224 |
+
|
| 225 |
+
```
|
| 226 |
+
scalper/
|
| 227 |
+
βββ main.py # Entry point
|
| 228 |
+
βββ config/
|
| 229 |
+
β βββ settings.yaml # Trading parameters
|
| 230 |
+
β βββ pairs.yaml # Trading pairs
|
| 231 |
+
βββ core/
|
| 232 |
+
β βββ exchange.py # Bybit API wrapper
|
| 233 |
+
β βββ strategy.py # Trading strategies
|
| 234 |
+
β βββ risk.py # Risk management
|
| 235 |
+
β βββ data_engine.py # Data storage & indicators
|
| 236 |
+
β βββ trade_monitor.py # Trade monitoring loop
|
| 237 |
+
β βββ websockets.py # Real-time data streaming
|
| 238 |
+
βββ services/
|
| 239 |
+
β βββ logger.py # Logging system
|
| 240 |
+
β βββ telegram.py # Telegram notifications
|
| 241 |
+
βββ backtester/
|
| 242 |
+
β βββ engine.py # Backtesting engine
|
| 243 |
+
βββ logs/ # Log files
|
| 244 |
+
βββ backtest_results/ # Backtest outputs
|
| 245 |
+
βββ requirements.txt # Dependencies
|
| 246 |
+
```
|
| 247 |
+
|
| 248 |
+
## βοΈ Configuration
|
| 249 |
+
|
| 250 |
+
### Trading Parameters
|
| 251 |
+
|
| 252 |
+
| Parameter | Default | Description |
|
| 253 |
+
|-----------|---------|-------------|
|
| 254 |
+
| `leverage` | 20 | Trading leverage |
|
| 255 |
+
| `tp_percent` | 0.025 | Take profit percentage |
|
| 256 |
+
| `sl_percent` | 0.01 | Stop loss percentage |
|
| 257 |
+
| `risk_per_trade` | 0.02 | Risk per trade (% of balance) |
|
| 258 |
+
|
| 259 |
+
### Strategy Parameters
|
| 260 |
+
|
| 261 |
+
| Parameter | Default | Description |
|
| 262 |
+
|-----------|---------|-------------|
|
| 263 |
+
| `interval` | "1" | Candle interval (minutes) |
|
| 264 |
+
| `rsi_period` | 14 | RSI calculation period |
|
| 265 |
+
| `ema_fast` | 9 | Fast EMA period |
|
| 266 |
+
| `ema_slow` | 21 | Slow EMA period |
|
| 267 |
+
|
| 268 |
+
## π¬ Backtesting
|
| 269 |
+
|
| 270 |
+
Run comprehensive backtests to evaluate strategy performance:
|
| 271 |
+
|
| 272 |
+
```bash
|
| 273 |
+
python main.py backtest
|
| 274 |
+
```
|
| 275 |
+
|
| 276 |
+
### Backtest Metrics
|
| 277 |
+
- **Win Rate**: Percentage of profitable trades
|
| 278 |
+
- **Profit Factor**: Gross profit / Gross loss
|
| 279 |
+
- **Max Drawdown**: Largest peak-to-valley decline
|
| 280 |
+
- **Sharpe Ratio**: Risk-adjusted returns
|
| 281 |
+
- **Average Trade Duration**: Typical trade holding time
|
| 282 |
+
|
| 283 |
+
### Parameter Optimization
|
| 284 |
+
|
| 285 |
+
The backtester includes grid search optimization for:
|
| 286 |
+
- EMA periods
|
| 287 |
+
- RSI thresholds
|
| 288 |
+
- Risk percentages
|
| 289 |
+
- Timeframe settings
|
| 290 |
+
|
| 291 |
+
## π Risk Management
|
| 292 |
+
|
| 293 |
+
### Safety Features
|
| 294 |
+
- **Daily Loss Limits**: Automatic stop when daily loss exceeds threshold
|
| 295 |
+
- **Volatility Filters**: Skip trading during high volatility periods
|
| 296 |
+
- **Spread Filters**: Avoid wide spreads that increase slippage
|
| 297 |
+
- **Position Limits**: Maximum one position per symbol
|
| 298 |
+
- **Emergency Stop**: Manual override capability
|
| 299 |
+
|
| 300 |
+
### Position Sizing
|
| 301 |
+
```python
|
| 302 |
+
risk_amount = balance * risk_per_trade
|
| 303 |
+
position_size = risk_amount / (sl_distance * leverage)
|
| 304 |
+
```
|
| 305 |
+
|
| 306 |
+
## π WebSocket Integration
|
| 307 |
+
|
| 308 |
+
Real-time data streams for ultra-fast execution:
|
| 309 |
+
- **Tickers**: Price updates every 100ms
|
| 310 |
+
- **Klines**: 1-minute and 5-minute candles
|
| 311 |
+
- **Orderbook**: Top 25 bids/asks with depth
|
| 312 |
+
- **Trades**: Recent market trades
|
| 313 |
+
- **Auto-Reconnect**: Handles connection drops gracefully
|
| 314 |
+
|
| 315 |
+
## π Monitoring & Logging
|
| 316 |
+
|
| 317 |
+
### Log Files
|
| 318 |
+
- `logs/scalper.log`: General application logs
|
| 319 |
+
- `logs/trades.log`: Trade execution logs
|
| 320 |
+
- `logs/errors.log`: Error tracking
|
| 321 |
+
- `logs/signals.json`: Trading signals
|
| 322 |
+
- `logs/performance.jsonl`: Performance metrics
|
| 323 |
+
|
| 324 |
+
### Telegram Alerts
|
| 325 |
+
Configure for real-time notifications:
|
| 326 |
+
- Trade executions
|
| 327 |
+
- Strategy signals
|
| 328 |
+
- Error alerts
|
| 329 |
+
- Daily performance summaries
|
| 330 |
+
|
| 331 |
+
## π Deployment
|
| 332 |
+
|
| 333 |
+
### Local Machine
|
| 334 |
+
```bash
|
| 335 |
+
# Run in background
|
| 336 |
+
nohup python main.py &
|
| 337 |
+
```
|
| 338 |
+
|
| 339 |
+
### VPS Deployment (Recommended)
|
| 340 |
+
```bash
|
| 341 |
+
# Install dependencies
|
| 342 |
+
sudo apt update
|
| 343 |
+
sudo apt install python3 python3-pip tmux
|
| 344 |
+
|
| 345 |
+
# Clone and setup
|
| 346 |
+
git clone <repository-url>
|
| 347 |
+
cd scalper
|
| 348 |
+
pip install -r requirements.txt
|
| 349 |
+
|
| 350 |
+
# Run with process manager
|
| 351 |
+
tmux new -s scalper
|
| 352 |
+
python main.py
|
| 353 |
+
# Ctrl+B, D to detach
|
| 354 |
+
```
|
| 355 |
+
|
| 356 |
+
### Process Manager (PM2)
|
| 357 |
+
```bash
|
| 358 |
+
npm install -g pm2
|
| 359 |
+
pm2 start main.py --name scalper
|
| 360 |
+
pm2 startup
|
| 361 |
+
pm2 save
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
## π§ Troubleshooting
|
| 365 |
+
|
| 366 |
+
### Common Issues
|
| 367 |
+
|
| 368 |
+
**API Connection Failed**
|
| 369 |
+
```bash
|
| 370 |
+
# Check API keys in .env
|
| 371 |
+
cat .env
|
| 372 |
+
# Verify testnet setting
|
| 373 |
+
# Check Bybit API status
|
| 374 |
+
```
|
| 375 |
+
|
| 376 |
+
**WebSocket Disconnection**
|
| 377 |
+
- Automatic reconnection is built-in
|
| 378 |
+
- Check internet connection
|
| 379 |
+
- Verify Bybit service status
|
| 380 |
+
|
| 381 |
+
**No Trading Signals**
|
| 382 |
+
```bash
|
| 383 |
+
# Check market conditions
|
| 384 |
+
python -c "from core.data_engine import DataEngine; de = DataEngine(); print(de.get_buffer_status())"
|
| 385 |
+
# Verify strategy parameters
|
| 386 |
+
# Check volatility filters
|
| 387 |
+
```
|
| 388 |
+
|
| 389 |
+
**Position Not Opening**
|
| 390 |
+
- Verify account balance
|
| 391 |
+
- Check risk management settings
|
| 392 |
+
- Review API permissions
|
| 393 |
+
|
| 394 |
+
### Debug Mode
|
| 395 |
+
```bash
|
| 396 |
+
# Enable debug logging
|
| 397 |
+
export LOG_LEVEL=DEBUG
|
| 398 |
+
python main.py
|
| 399 |
+
```
|
| 400 |
+
|
| 401 |
+
## π Performance Optimization
|
| 402 |
+
|
| 403 |
+
### Strategy Tuning
|
| 404 |
+
1. Run backtests with different parameters
|
| 405 |
+
2. Optimize for specific market conditions
|
| 406 |
+
3. Adjust risk parameters based on drawdown
|
| 407 |
+
|
| 408 |
+
### Hardware Requirements
|
| 409 |
+
- **CPU**: 2+ cores recommended
|
| 410 |
+
- **RAM**: 2-4 GB sufficient
|
| 411 |
+
- **Network**: Stable internet connection
|
| 412 |
+
- **Storage**: 1GB for logs and data
|
| 413 |
+
|
| 414 |
+
## π€ Contributing
|
| 415 |
+
|
| 416 |
+
1. Fork the repository
|
| 417 |
+
2. Create a feature branch
|
| 418 |
+
3. Add tests for new functionality
|
| 419 |
+
4. Submit a pull request
|
| 420 |
+
|
| 421 |
+
## π License
|
| 422 |
+
|
| 423 |
+
MIT License - see [LICENSE](LICENSE) file for details.
|
| 424 |
+
|
| 425 |
+
## β οΈ Disclaimer
|
| 426 |
+
|
| 427 |
+
**This software is for educational and research purposes only. Trading cryptocurrencies involves substantial risk of loss and is not suitable for every investor. Past performance does not guarantee future results. Please test thoroughly and use at your own risk.**
|
| 428 |
+
|
| 429 |
+
### Risk Warnings
|
| 430 |
+
- **Leverage Trading**: 20x leverage can amplify both gains and losses
|
| 431 |
+
- **Market Volatility**: Crypto markets can be highly volatile
|
| 432 |
+
- **Technical Risks**: Software bugs, internet outages, API issues
|
| 433 |
+
- **Financial Risk**: Never invest more than you can afford to lose
|
| 434 |
+
|
| 435 |
+
## π Support
|
| 436 |
+
|
| 437 |
+
- **Issues**: Open a GitHub issue
|
| 438 |
+
- **Documentation**: Check this README and inline code comments
|
| 439 |
+
- **Community**: Join crypto trading communities for general advice
|
| 440 |
+
|
| 441 |
+
---
|
| 442 |
+
|
| 443 |
+
## π **Hugging Face Deployment**
|
| 444 |
+
|
| 445 |
+
### **Deploy to Hugging Face Spaces**
|
| 446 |
+
|
| 447 |
+
1. **Create Space**: Go to [hf.co/spaces](https://hf.co/spaces) β "Create new Space"
|
| 448 |
+
- **Name**: `your-scalping-bot`
|
| 449 |
+
- **SDK**: `Docker`
|
| 450 |
+
- **Storage**: `Small`
|
| 451 |
+
|
| 452 |
+
2. **Repository Setup**:
|
| 453 |
+
```bash
|
| 454 |
+
# Push your code to GitHub
|
| 455 |
+
git add .
|
| 456 |
+
git commit -m "Scalping bot deployment"
|
| 457 |
+
git push origin main
|
| 458 |
+
```
|
| 459 |
+
|
| 460 |
+
3. **Connect Repository**: In Space settings β "Connect Repository"
|
| 461 |
+
- Enter your GitHub URL
|
| 462 |
+
- Branch: `main`
|
| 463 |
+
|
| 464 |
+
4. **Public URL**: You'll get a URL like:
|
| 465 |
+
```
|
| 466 |
+
https://yourusername-your-scalping-bot.hf.space
|
| 467 |
+
```
|
| 468 |
+
**This is your public IP for remote access!**
|
| 469 |
+
|
| 470 |
+
5. **Configure API Keys**: Use the web interface to set your Bybit credentials
|
| 471 |
+
|
| 472 |
+
### **Hugging Face Features**
|
| 473 |
+
- π **Public web dashboard** with real-time analysis
|
| 474 |
+
- π **Interactive charts** and performance metrics
|
| 475 |
+
- ποΈ **One-click trading** controls
|
| 476 |
+
- π± **Mobile-friendly** interface
|
| 477 |
+
- π **Auto-scaling** and uptime monitoring
|
| 478 |
+
|
| 479 |
+
---
|
| 480 |
+
|
| 481 |
+
**Happy Trading! π**
|
test/get_chat_id.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Get Telegram Chat ID for bot notifications
|
| 4 |
+
Run this script, then send a message to your bot
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import requests
|
| 8 |
+
import time
|
| 9 |
+
|
| 10 |
+
# Your bot token
|
| 11 |
+
BOT_TOKEN = "8555333979:AAH_oCYvbXEt6IQ0-qbFdb9kSjdqpyIJcqA"
|
| 12 |
+
BASE_URL = f"https://api.telegram.org/bot{BOT_TOKEN}"
|
| 13 |
+
|
| 14 |
+
def get_bot_info():
|
| 15 |
+
"""Get bot information"""
|
| 16 |
+
try:
|
| 17 |
+
response = requests.get(f"{BASE_URL}/getMe")
|
| 18 |
+
if response.status_code == 200:
|
| 19 |
+
bot_info = response.json()
|
| 20 |
+
if bot_info['ok']:
|
| 21 |
+
print("π€ Bot Information:")
|
| 22 |
+
print(f" Name: {bot_info['result']['first_name']}")
|
| 23 |
+
print(f" Username: @{bot_info['result']['username']}")
|
| 24 |
+
print(f" Can read messages: {bot_info['result']['can_read_all_group_messages']}")
|
| 25 |
+
return True
|
| 26 |
+
print(f"β Failed to get bot info: {response.text}")
|
| 27 |
+
return False
|
| 28 |
+
except Exception as e:
|
| 29 |
+
print(f"β Error: {e}")
|
| 30 |
+
return False
|
| 31 |
+
|
| 32 |
+
def get_updates():
|
| 33 |
+
"""Get recent updates (messages) to extract chat ID"""
|
| 34 |
+
try:
|
| 35 |
+
response = requests.get(f"{BASE_URL}/getUpdates", timeout=30)
|
| 36 |
+
if response.status_code == 200:
|
| 37 |
+
updates = response.json()
|
| 38 |
+
if updates['ok'] and updates['result']:
|
| 39 |
+
print("\nπ¨ Recent Messages:")
|
| 40 |
+
for update in updates['result'][-5:]: # Show last 5 messages
|
| 41 |
+
if 'message' in update:
|
| 42 |
+
message = update['message']
|
| 43 |
+
chat = message['chat']
|
| 44 |
+
from_user = message.get('from', {})
|
| 45 |
+
|
| 46 |
+
print(f"\n㪠Message from: {from_user.get('first_name', 'Unknown')}")
|
| 47 |
+
print(f" Chat ID: {chat['id']}")
|
| 48 |
+
print(f" Chat Type: {chat['type']}")
|
| 49 |
+
print(f" Message: {message.get('text', 'N/A')}")
|
| 50 |
+
print(f" Time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(message['date']))}")
|
| 51 |
+
|
| 52 |
+
if chat['type'] == 'private':
|
| 53 |
+
print(f"\nπ― YOUR CHAT ID: {chat['id']}")
|
| 54 |
+
print(" Use this in your .env file as TELEGRAM_CHAT_ID")
|
| 55 |
+
return chat['id']
|
| 56 |
+
print("\nπ‘ No recent messages found. Send a message to your bot and run this again.")
|
| 57 |
+
return None
|
| 58 |
+
else:
|
| 59 |
+
print("\nπ‘ No messages received yet. Please:")
|
| 60 |
+
print(" 1. Open Telegram")
|
| 61 |
+
print(" 2. Find your bot: Search for the username shown above")
|
| 62 |
+
print(" 3. Send any message to the bot")
|
| 63 |
+
print(" 4. Run this script again")
|
| 64 |
+
return None
|
| 65 |
+
else:
|
| 66 |
+
print(f"β Failed to get updates: {response.text}")
|
| 67 |
+
return None
|
| 68 |
+
except Exception as e:
|
| 69 |
+
print(f"β Error getting updates: {e}")
|
| 70 |
+
return None
|
| 71 |
+
|
| 72 |
+
def send_test_message(chat_id):
|
| 73 |
+
"""Send a test message to confirm chat ID works"""
|
| 74 |
+
try:
|
| 75 |
+
message = "β
Telegram integration test successful! Your chat ID is working."
|
| 76 |
+
data = {
|
| 77 |
+
'chat_id': chat_id,
|
| 78 |
+
'text': message
|
| 79 |
+
}
|
| 80 |
+
response = requests.post(f"{BASE_URL}/sendMessage", data=data)
|
| 81 |
+
if response.status_code == 200:
|
| 82 |
+
print("\nπ€ Test message sent! Check your Telegram.")
|
| 83 |
+
return True
|
| 84 |
+
else:
|
| 85 |
+
print(f"β Failed to send test message: {response.text}")
|
| 86 |
+
return False
|
| 87 |
+
except Exception as e:
|
| 88 |
+
print(f"β Error sending test message: {e}")
|
| 89 |
+
return False
|
| 90 |
+
|
| 91 |
+
def main():
|
| 92 |
+
print("π Telegram Chat ID Finder")
|
| 93 |
+
print("=" * 40)
|
| 94 |
+
|
| 95 |
+
if not get_bot_info():
|
| 96 |
+
print("β Bot token is invalid. Please check your token.")
|
| 97 |
+
return
|
| 98 |
+
|
| 99 |
+
print("\nβ³ Waiting for messages... (you have 30 seconds)")
|
| 100 |
+
print("π± Please send a message to your bot now...")
|
| 101 |
+
|
| 102 |
+
chat_id = get_updates()
|
| 103 |
+
|
| 104 |
+
if chat_id:
|
| 105 |
+
print(f"\nπ Found your Chat ID: {chat_id}")
|
| 106 |
+
|
| 107 |
+
if send_test_message(chat_id):
|
| 108 |
+
print("\nβ
Setup complete!")
|
| 109 |
+
print("\nπ Add this to your .env file:")
|
| 110 |
+
print(f"TELEGRAM_BOT_TOKEN={BOT_TOKEN}")
|
| 111 |
+
print(f"TELEGRAM_CHAT_ID={chat_id}")
|
| 112 |
+
|
| 113 |
+
print("\nπ Or update your existing .env:")
|
| 114 |
+
print("TELEGRAM_CHAT_ID=your_chat_id_here")
|
| 115 |
+
print(f" β replace with: {chat_id}")
|
| 116 |
+
else:
|
| 117 |
+
print("\nβ οΈ Chat ID found but test message failed.")
|
| 118 |
+
print(" You can still use the chat ID, but double-check your setup.")
|
| 119 |
+
else:
|
| 120 |
+
print("\nβ No chat ID found.")
|
| 121 |
+
print(" Make sure to send a message to your bot and run this script again.")
|
| 122 |
+
|
| 123 |
+
if __name__ == "__main__":
|
| 124 |
+
main()
|
test/huggingface_README.md
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# π Deploy Scalping Bot on Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
## Prerequisites
|
| 4 |
+
|
| 5 |
+
1. **Hugging Face Account**: Create account at [huggingface.co](https://huggingface.co)
|
| 6 |
+
2. **API Credentials**: Your Bybit API keys ready
|
| 7 |
+
3. **Git Repository**: Push your code to GitHub/GitLab
|
| 8 |
+
|
| 9 |
+
## Step 1: Create Hugging Face Space
|
| 10 |
+
|
| 11 |
+
1. Go to [huggingface.co/spaces](https://huggingface.co/spaces)
|
| 12 |
+
2. Click "Create new Space"
|
| 13 |
+
3. Choose:
|
| 14 |
+
- **Space name**: `your-scalping-bot`
|
| 15 |
+
- **License**: Apache-2.0
|
| 16 |
+
- **SDK**: **Docker** (for full control)
|
| 17 |
+
- **Storage**: Small (sufficient)
|
| 18 |
+
|
| 19 |
+
## Step 2: Repository Structure
|
| 20 |
+
|
| 21 |
+
Your repository should look like:
|
| 22 |
+
```
|
| 23 |
+
your-scalping-bot/
|
| 24 |
+
βββ app.py # Streamlit/FastAPI app
|
| 25 |
+
βββ requirements.txt # Dependencies
|
| 26 |
+
βββ packages.txt # System packages (if needed)
|
| 27 |
+
βββ config/
|
| 28 |
+
β βββ settings.yaml
|
| 29 |
+
β βββ pairs.yaml
|
| 30 |
+
βββ core/
|
| 31 |
+
β βββ exchange.py
|
| 32 |
+
β βββ strategy.py
|
| 33 |
+
β βββ ...
|
| 34 |
+
βββ services/
|
| 35 |
+
β βββ ...
|
| 36 |
+
βββ README.md
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
## Step 3: Create app.py
|
| 40 |
+
|
| 41 |
+
```python
|
| 42 |
+
import streamlit as st
|
| 43 |
+
import subprocess
|
| 44 |
+
import os
|
| 45 |
+
import signal
|
| 46 |
+
import time
|
| 47 |
+
from threading import Thread
|
| 48 |
+
import requests
|
| 49 |
+
|
| 50 |
+
# Set page config
|
| 51 |
+
st.set_page_config(
|
| 52 |
+
page_title="Scalping Bot Control",
|
| 53 |
+
page_icon="π€",
|
| 54 |
+
layout="wide"
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
st.title("π€ Bybit Scalping Bot Control Panel")
|
| 58 |
+
|
| 59 |
+
# Sidebar
|
| 60 |
+
st.sidebar.header("βοΈ Configuration")
|
| 61 |
+
|
| 62 |
+
# Environment variables setup
|
| 63 |
+
st.sidebar.subheader("π API Configuration")
|
| 64 |
+
bybit_key = st.sidebar.text_input("Bybit API Key", type="password")
|
| 65 |
+
bybit_secret = st.sidebar.text_input("Bybit API Secret", type="password")
|
| 66 |
+
bybit_testnet = st.sidebar.checkbox("Use Testnet", value=False)
|
| 67 |
+
telegram_token = st.sidebar.text_input("Telegram Bot Token (optional)")
|
| 68 |
+
telegram_chat = st.sidebar.text_input("Telegram Chat ID (optional)")
|
| 69 |
+
|
| 70 |
+
if st.sidebar.button("πΎ Save Configuration"):
|
| 71 |
+
# Save to .env file
|
| 72 |
+
env_content = f"""BYBIT_API_KEY={bybit_key}
|
| 73 |
+
BYBIT_API_SECRET={bybit_secret}
|
| 74 |
+
BYBIT_TESTNET={str(bybit_testnet).lower()}
|
| 75 |
+
TELEGRAM_BOT_TOKEN={telegram_token}
|
| 76 |
+
TELEGRAM_CHAT_ID={telegram_chat}
|
| 77 |
+
"""
|
| 78 |
+
with open('.env', 'w') as f:
|
| 79 |
+
f.write(env_content)
|
| 80 |
+
st.sidebar.success("Configuration saved!")
|
| 81 |
+
|
| 82 |
+
# Main content
|
| 83 |
+
col1, col2, col3 = st.columns(3)
|
| 84 |
+
|
| 85 |
+
with col1:
|
| 86 |
+
st.subheader("π Start Trading")
|
| 87 |
+
symbol = st.selectbox("Symbol", ["BTCUSDT", "ETHUSDT", "SOLUSDT"])
|
| 88 |
+
duration = st.slider("Duration (hours)", 1, 24, 18)
|
| 89 |
+
max_trades = st.number_input("Max Trades (optional)", min_value=1, value=50)
|
| 90 |
+
|
| 91 |
+
if st.button("βΆοΈ Start Trading", key="start"):
|
| 92 |
+
try:
|
| 93 |
+
response = requests.post(f"http://localhost:8000/start/{symbol}",
|
| 94 |
+
params={"duration_hours": duration, "max_trades": max_trades})
|
| 95 |
+
if response.status_code == 200:
|
| 96 |
+
st.success(f"β
Started trading {symbol} for {duration} hours")
|
| 97 |
+
else:
|
| 98 |
+
st.error(f"β Failed to start: {response.text}")
|
| 99 |
+
except Exception as e:
|
| 100 |
+
st.error(f"β Error: {e}")
|
| 101 |
+
|
| 102 |
+
with col2:
|
| 103 |
+
st.subheader("π Stop Trading")
|
| 104 |
+
stop_symbol = st.selectbox("Symbol to Stop", ["BTCUSDT", "ETHUSDT", "SOLUSDT"], key="stop_select")
|
| 105 |
+
|
| 106 |
+
if st.button("βΉοΈ Stop Trading", key="stop"):
|
| 107 |
+
try:
|
| 108 |
+
response = requests.post(f"http://localhost:8000/stop/{stop_symbol}")
|
| 109 |
+
if response.status_code == 200:
|
| 110 |
+
st.success(f"β
Stopped trading {stop_symbol}")
|
| 111 |
+
else:
|
| 112 |
+
st.error(f"β Failed to stop: {response.text}")
|
| 113 |
+
except Exception as e:
|
| 114 |
+
st.error(f"β Error: {e}")
|
| 115 |
+
|
| 116 |
+
with col3:
|
| 117 |
+
st.subheader("π¨ Emergency")
|
| 118 |
+
if st.button("π¨ EMERGENCY STOP ALL", key="emergency"):
|
| 119 |
+
try:
|
| 120 |
+
response = requests.post("http://localhost:8000/emergency_stop")
|
| 121 |
+
if response.status_code == 200:
|
| 122 |
+
st.error("π¨ ALL TRADING STOPPED!")
|
| 123 |
+
else:
|
| 124 |
+
st.error(f"β Failed: {response.text}")
|
| 125 |
+
except Exception as e:
|
| 126 |
+
st.error(f"β Error: {e}")
|
| 127 |
+
|
| 128 |
+
# Status section
|
| 129 |
+
st.header("π Bot Status")
|
| 130 |
+
|
| 131 |
+
if st.button("π Refresh Status"):
|
| 132 |
+
try:
|
| 133 |
+
response = requests.get("http://localhost:8000/status")
|
| 134 |
+
if response.status_code == 200:
|
| 135 |
+
status = response.json()
|
| 136 |
+
|
| 137 |
+
# Overall status
|
| 138 |
+
if status["is_running"]:
|
| 139 |
+
st.success("β
Bot is RUNNING")
|
| 140 |
+
else:
|
| 141 |
+
st.warning("βΈοΈ Bot is STOPPED")
|
| 142 |
+
|
| 143 |
+
st.metric("Total P&L", f"${status['total_pnl']:.2f}")
|
| 144 |
+
st.metric("Trades Today", status['trades_today'])
|
| 145 |
+
|
| 146 |
+
# Active sessions
|
| 147 |
+
if status["active_sessions"]:
|
| 148 |
+
st.subheader("π― Active Sessions")
|
| 149 |
+
for session in status["active_sessions"]:
|
| 150 |
+
with st.expander(f"{session['symbol']} - {session['status'].upper()}"):
|
| 151 |
+
st.write(f"**Session ID:** {session['session_id']}")
|
| 152 |
+
st.write(f"**Started:** {session['start_time'][:19]}")
|
| 153 |
+
st.write(f"**P&L:** ${session['pnl']:.2f}")
|
| 154 |
+
st.write(f"**Trades:** {session['trades']}")
|
| 155 |
+
else:
|
| 156 |
+
st.info("No active sessions")
|
| 157 |
+
|
| 158 |
+
else:
|
| 159 |
+
st.error(f"β Failed to get status: {response.status_code}")
|
| 160 |
+
except Exception as e:
|
| 161 |
+
st.error(f"β Connection error: {e}")
|
| 162 |
+
|
| 163 |
+
# Analysis section
|
| 164 |
+
st.header("π Live Analysis")
|
| 165 |
+
|
| 166 |
+
if st.button("π Refresh Analysis"):
|
| 167 |
+
try:
|
| 168 |
+
response = requests.get("http://localhost:8000/analysis/status")
|
| 169 |
+
if response.status_code == 200:
|
| 170 |
+
analysis = response.json()
|
| 171 |
+
|
| 172 |
+
for symbol, data in analysis["analysis_status"].items():
|
| 173 |
+
with st.expander(f"π {symbol} Analysis"):
|
| 174 |
+
col1, col2 = st.columns(2)
|
| 175 |
+
|
| 176 |
+
with col1:
|
| 177 |
+
st.metric("Current Price", f"${data['current_price']:.2f}" if data['current_price'] else "N/A")
|
| 178 |
+
|
| 179 |
+
indicators = data['indicators']
|
| 180 |
+
st.write("**Indicators:**")
|
| 181 |
+
st.write(f"EMA 9: {indicators['ema_9']:.4f}" if indicators['ema_9'] else "EMA 9: N/A")
|
| 182 |
+
st.write(f"EMA 21: {indicators['ema_21']:.4f}" if indicators['ema_21'] else "EMA 21: N/A")
|
| 183 |
+
st.write(f"RSI 14: {indicators['rsi_14']:.1f}" if indicators['rsi_14'] else "RSI 14: N/A")
|
| 184 |
+
|
| 185 |
+
with col2:
|
| 186 |
+
conditions = data['strategy_conditions']
|
| 187 |
+
st.write("**Strategy Conditions:**")
|
| 188 |
+
st.write(f"π Trend Up: {'β
' if conditions['trend_up'] else 'β'}")
|
| 189 |
+
st.write(f"π RSI Valid: {'β
' if conditions['rsi_valid'] else 'β'}")
|
| 190 |
+
st.write(f"π₯ Volume Spike: {'β
' if conditions['volume_spike'] else 'β'}")
|
| 191 |
+
st.write(f"π― Orderbook OK: {'β
' if conditions['orderbook_aligned'] else 'β'}")
|
| 192 |
+
|
| 193 |
+
all_conditions = all([conditions['trend_up'], conditions['rsi_valid'],
|
| 194 |
+
conditions['volume_spike'], conditions['orderbook_aligned']])
|
| 195 |
+
|
| 196 |
+
if all_conditions:
|
| 197 |
+
st.success("π― TRADE SIGNAL READY!")
|
| 198 |
+
else:
|
| 199 |
+
st.info("β³ Waiting for conditions...")
|
| 200 |
+
else:
|
| 201 |
+
st.error(f"β Failed to get analysis: {response.status_code}")
|
| 202 |
+
except Exception as e:
|
| 203 |
+
st.error(f"β Connection error: {e}")
|
| 204 |
+
|
| 205 |
+
# Logs section
|
| 206 |
+
st.header("π Recent Logs")
|
| 207 |
+
|
| 208 |
+
log_type = st.selectbox("Log Type", ["Live Logs", "Analysis Logs"])
|
| 209 |
+
|
| 210 |
+
if st.button("π Refresh Logs"):
|
| 211 |
+
try:
|
| 212 |
+
if log_type == "Live Logs":
|
| 213 |
+
response = requests.get("http://localhost:8000/logs/live")
|
| 214 |
+
else:
|
| 215 |
+
response = requests.get("http://localhost:8000/logs/analysis")
|
| 216 |
+
|
| 217 |
+
if response.status_code == 200:
|
| 218 |
+
logs_data = response.json()
|
| 219 |
+
|
| 220 |
+
st.write(f"**Total {log_type}:** {logs_data['count']}")
|
| 221 |
+
|
| 222 |
+
logs_text = ""
|
| 223 |
+
for log_line in logs_data['logs'][-10:]: # Show last 10
|
| 224 |
+
logs_text += log_line
|
| 225 |
+
|
| 226 |
+
st.code(logs_text, language="text")
|
| 227 |
+
else:
|
| 228 |
+
st.error(f"β Failed to get logs: {response.status_code}")
|
| 229 |
+
except Exception as e:
|
| 230 |
+
st.error(f"β Connection error: {e}")
|
| 231 |
+
|
| 232 |
+
# Footer
|
| 233 |
+
st.markdown("---")
|
| 234 |
+
st.markdown("*π€ Bybit Scalping Bot - FastAPI Control Interface*")
|
| 235 |
+
st.markdown("*Made with β€οΈ for automated crypto trading*")
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
## Step 4: Create requirements.txt
|
| 239 |
+
|
| 240 |
+
```txt
|
| 241 |
+
fastapi==0.104.1
|
| 242 |
+
uvicorn[standard]==0.24.0
|
| 243 |
+
streamlit==1.28.1
|
| 244 |
+
requests==2.31.0
|
| 245 |
+
python-dotenv==1.0.0
|
| 246 |
+
pybit==5.7.0
|
| 247 |
+
pyyaml==6.0.1
|
| 248 |
+
pandas==2.1.3
|
| 249 |
+
numpy==1.26.2
|
| 250 |
+
websockets==12.0
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
## Step 5: Create packages.txt (Optional)
|
| 254 |
+
|
| 255 |
+
```
|
| 256 |
+
# No system packages needed
|
| 257 |
+
```
|
| 258 |
+
|
| 259 |
+
## Step 6: Deploy to Hugging Face
|
| 260 |
+
|
| 261 |
+
1. **Push to Git**: Commit and push your code to a Git repository
|
| 262 |
+
2. **Connect Space**: In your Hugging Face Space settings:
|
| 263 |
+
- Go to "Settings" β "Connect Repository"
|
| 264 |
+
- Enter your repository URL
|
| 265 |
+
- Set branch to `main`
|
| 266 |
+
3. **Auto-deploy**: Hugging Face will automatically build and deploy
|
| 267 |
+
|
| 268 |
+
## Step 7: Access Your Bot
|
| 269 |
+
|
| 270 |
+
Once deployed, you'll get a public URL like:
|
| 271 |
+
```
|
| 272 |
+
https://yourusername-your-scalping-bot.hf.space
|
| 273 |
+
```
|
| 274 |
+
|
| 275 |
+
This will be your **public IP/URL** for accessing the bot remotely!
|
| 276 |
+
|
| 277 |
+
## Step 8: Start the Bot
|
| 278 |
+
|
| 279 |
+
In your Hugging Face Space:
|
| 280 |
+
1. β
Configure API keys in the sidebar
|
| 281 |
+
2. β
Click "Start Trading" for your desired pairs
|
| 282 |
+
3. β
Monitor analysis in real-time
|
| 283 |
+
4. β
View logs and performance
|
| 284 |
+
|
| 285 |
+
## π Security Notes
|
| 286 |
+
|
| 287 |
+
- **Never expose real API keys** in public repositories
|
| 288 |
+
- **Use environment variables** for sensitive data
|
| 289 |
+
- **Consider IP restrictions** on your Bybit API keys
|
| 290 |
+
- **Monitor your bot regularly** via the web interface
|
| 291 |
+
|
| 292 |
+
## π Public Access
|
| 293 |
+
|
| 294 |
+
Your bot will be accessible worldwide via:
|
| 295 |
+
```
|
| 296 |
+
https://[your-username]-[space-name].hf.space
|
| 297 |
+
```
|
| 298 |
+
|
| 299 |
+
This serves as your **public IP address** for remote access!
|
| 300 |
+
|
| 301 |
+
## π‘ Usage Examples
|
| 302 |
+
|
| 303 |
+
```bash
|
| 304 |
+
# Direct API calls (replace with your HF Space URL)
|
| 305 |
+
curl -X POST "https://your-bot.hf.space/start/BTCUSDT"
|
| 306 |
+
curl "https://your-bot.hf.space/status"
|
| 307 |
+
curl "https://your-bot.hf.space/analysis/status"
|
| 308 |
+
```
|
| 309 |
+
|
| 310 |
+
## π You're Done!
|
| 311 |
+
|
| 312 |
+
Your scalping bot is now hosted on Hugging Face with:
|
| 313 |
+
- π **Public web interface**
|
| 314 |
+
- π **Real-time analysis dashboard**
|
| 315 |
+
- ποΈ **Remote control via API**
|
| 316 |
+
- π± **Mobile-friendly interface**
|
| 317 |
+
|
| 318 |
+
**Happy trading!** ππ€
|
test/test_api.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
import os
|
| 3 |
+
from dotenv import load_dotenv
|
| 4 |
+
from pybit.unified_trading import HTTP
|
| 5 |
+
|
| 6 |
+
load_dotenv()
|
| 7 |
+
|
| 8 |
+
def test_public_endpoints():
|
| 9 |
+
print("Testing public API endpoints...")
|
| 10 |
+
try:
|
| 11 |
+
session = HTTP(testnet=True)
|
| 12 |
+
|
| 13 |
+
server_time = session.get_server_time()
|
| 14 |
+
print(f"β
Server time: {server_time['result']['timeSecond']}")
|
| 15 |
+
|
| 16 |
+
ticker = session.get_tickers(category="linear", symbol="BTCUSDT")
|
| 17 |
+
if ticker['result']['list']:
|
| 18 |
+
price = ticker['result']['list'][0]['lastPrice']
|
| 19 |
+
print(f"β
BTCUSDT price: ${price}")
|
| 20 |
+
|
| 21 |
+
kline = session.get_kline(
|
| 22 |
+
category="linear",
|
| 23 |
+
symbol="BTCUSDT",
|
| 24 |
+
interval="1",
|
| 25 |
+
limit=1
|
| 26 |
+
)
|
| 27 |
+
if kline['result']['list']:
|
| 28 |
+
print("β
Kline data retrieved successfully")
|
| 29 |
+
|
| 30 |
+
instruments = session.get_instruments_info(category="linear", symbol="BTCUSDT")
|
| 31 |
+
if instruments['result']['list']:
|
| 32 |
+
print("β
Instrument info retrieved successfully")
|
| 33 |
+
|
| 34 |
+
return True
|
| 35 |
+
except Exception as e:
|
| 36 |
+
print(f"β Public API test failed: {e}")
|
| 37 |
+
return False
|
| 38 |
+
|
| 39 |
+
def test_private_endpoints():
|
| 40 |
+
print("\nTesting private API endpoints...")
|
| 41 |
+
|
| 42 |
+
API_KEY = os.getenv("BYBIT_API_KEY")
|
| 43 |
+
API_SECRET = os.getenv("BYBIT_API_SECRET")
|
| 44 |
+
TESTNET = os.getenv("BYBIT_TESTNET", "true").lower() == "true"
|
| 45 |
+
|
| 46 |
+
print(f"API Key configured: {'Yes' if API_KEY else 'No'}")
|
| 47 |
+
print(f"API Secret configured: {'Yes' if API_SECRET else 'No'}")
|
| 48 |
+
print(f"Using {'TESTNET' if TESTNET else 'MAINNET'}")
|
| 49 |
+
|
| 50 |
+
if not API_KEY or not API_SECRET:
|
| 51 |
+
print("β ERROR: API credentials not found in .env file")
|
| 52 |
+
print("Please set BYBIT_API_KEY and BYBIT_API_SECRET in your .env file")
|
| 53 |
+
return False
|
| 54 |
+
|
| 55 |
+
if API_KEY == "your_api_key_here" or API_SECRET == "your_api_secret_here":
|
| 56 |
+
print("β ERROR: API credentials are still set to default placeholder values")
|
| 57 |
+
print("Please update .env with your actual Bybit API credentials")
|
| 58 |
+
return False
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
session = HTTP(
|
| 62 |
+
api_key=API_KEY,
|
| 63 |
+
api_secret=API_SECRET,
|
| 64 |
+
testnet=TESTNET
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
print("β
HTTP client initialized with API credentials")
|
| 68 |
+
|
| 69 |
+
balance_success = False
|
| 70 |
+
positions_success = False
|
| 71 |
+
orders_success = False
|
| 72 |
+
|
| 73 |
+
try:
|
| 74 |
+
balance = session.get_wallet_balance(accountType="UNIFIED")
|
| 75 |
+
print(f"β
Account balance retrieved: {len(balance['result']['list'])} coins")
|
| 76 |
+
|
| 77 |
+
for coin in balance['result']['list']:
|
| 78 |
+
if coin['coin'] == 'USDT':
|
| 79 |
+
print(f" USDT Balance: {coin.get('walletBalance', '0')}")
|
| 80 |
+
|
| 81 |
+
balance_success = True
|
| 82 |
+
|
| 83 |
+
except Exception as e:
|
| 84 |
+
print(f"β Balance check failed: {e}")
|
| 85 |
+
print(" Trying CONTRACT account type...")
|
| 86 |
+
|
| 87 |
+
try:
|
| 88 |
+
balance = session.get_wallet_balance(accountType="CONTRACT")
|
| 89 |
+
print(f"β
Contract account balance retrieved: {len(balance['result']['list'])} coins")
|
| 90 |
+
balance_success = True
|
| 91 |
+
except Exception as e2:
|
| 92 |
+
print(f"β Contract balance also failed: {e2}")
|
| 93 |
+
print(" Common causes:")
|
| 94 |
+
print(" - API key lacks balance viewing permissions")
|
| 95 |
+
print(" - Invalid API credentials")
|
| 96 |
+
print(" - Account not funded on testnet")
|
| 97 |
+
print(" - Check API key permissions in Bybit dashboard")
|
| 98 |
+
|
| 99 |
+
try:
|
| 100 |
+
positions = session.get_positions(category="linear")
|
| 101 |
+
print(f"β
Positions retrieved: {len(positions['result']['list'])} positions")
|
| 102 |
+
positions_success = True
|
| 103 |
+
except Exception as e:
|
| 104 |
+
print(f"β Positions check failed: {e}")
|
| 105 |
+
print(" This requires 'View Positions' permission")
|
| 106 |
+
|
| 107 |
+
try:
|
| 108 |
+
orders = session.get_open_orders(category="linear")
|
| 109 |
+
print(f"β
Open orders retrieved: {len(orders['result']['list'])} orders")
|
| 110 |
+
orders_success = True
|
| 111 |
+
except Exception as e:
|
| 112 |
+
print(f"β Orders check failed: {e}")
|
| 113 |
+
print(" This requires 'View Orders' permission")
|
| 114 |
+
|
| 115 |
+
if balance_success and positions_success and orders_success:
|
| 116 |
+
print("π All private API endpoints working!")
|
| 117 |
+
return True
|
| 118 |
+
else:
|
| 119 |
+
print("β οΈ Some private API endpoints failed")
|
| 120 |
+
print(" For trading bot to work, you need:")
|
| 121 |
+
print(" - Valid API credentials")
|
| 122 |
+
print(" - API key with permissions: View Account Balance, View Positions, View Orders")
|
| 123 |
+
print(" - API key with permissions: Trade (for placing orders)")
|
| 124 |
+
print(" - Funded testnet account (if using testnet)")
|
| 125 |
+
return False
|
| 126 |
+
|
| 127 |
+
except Exception as e:
|
| 128 |
+
print(f"β Private API connection failed: {e}")
|
| 129 |
+
return False
|
| 130 |
+
|
| 131 |
+
def main():
|
| 132 |
+
print("π Bybit API Connection Test")
|
| 133 |
+
print("=" * 40)
|
| 134 |
+
|
| 135 |
+
public_ok = test_public_endpoints()
|
| 136 |
+
private_ok = test_private_endpoints()
|
| 137 |
+
|
| 138 |
+
print("\n" + "=" * 40)
|
| 139 |
+
print("π Test Results:")
|
| 140 |
+
|
| 141 |
+
if public_ok:
|
| 142 |
+
print("β
Public API: Working")
|
| 143 |
+
else:
|
| 144 |
+
print("β Public API: Failed")
|
| 145 |
+
|
| 146 |
+
if private_ok:
|
| 147 |
+
print("β
Private API: Working")
|
| 148 |
+
print("π All API tests passed! Ready for trading.")
|
| 149 |
+
return True
|
| 150 |
+
else:
|
| 151 |
+
print("β Private API: Failed")
|
| 152 |
+
print("\nπ§ To fix private API issues:")
|
| 153 |
+
print("1. Go to Bybit account > API Management")
|
| 154 |
+
print("2. Ensure your API key has these permissions:")
|
| 155 |
+
print(" - 'Unified Trading Account' - Read & Trade")
|
| 156 |
+
print(" - 'USDC Derivatives' - Read & Trade (if using USDC)")
|
| 157 |
+
print(" - 'Spot' - Read & Trade (if using spot)")
|
| 158 |
+
print("3. Make sure your account is upgraded to Unified Trading Account")
|
| 159 |
+
print("4. Fund your testnet account with USDT:")
|
| 160 |
+
print(" - Go to Bybit testnet faucet or transfer from mainnet")
|
| 161 |
+
print(" - Minimum 10 USDT recommended for testing")
|
| 162 |
+
print("5. Verify API credentials in .env file are correct")
|
| 163 |
+
return False
|
| 164 |
+
|
| 165 |
+
if __name__ == "__main__":
|
| 166 |
+
success = main()
|
| 167 |
+
exit(0 if success else 1)
|
test/test_api_client.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
FastAPI Client Test - Test the scalping bot API endpoints
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import requests
|
| 7 |
+
import time
|
| 8 |
+
import json
|
| 9 |
+
|
| 10 |
+
BASE_URL = "http://localhost:8000"
|
| 11 |
+
|
| 12 |
+
def test_endpoint(method, endpoint, data=None, params=None):
|
| 13 |
+
"""Test an API endpoint"""
|
| 14 |
+
try:
|
| 15 |
+
url = f"{BASE_URL}{endpoint}"
|
| 16 |
+
|
| 17 |
+
if method == "GET":
|
| 18 |
+
response = requests.get(url, params=params)
|
| 19 |
+
elif method == "POST":
|
| 20 |
+
response = requests.post(url, json=data, params=params)
|
| 21 |
+
else:
|
| 22 |
+
print(f"β Unsupported method: {method}")
|
| 23 |
+
return None
|
| 24 |
+
|
| 25 |
+
print(f"\n{method} {endpoint}")
|
| 26 |
+
print(f"Status: {response.status_code}")
|
| 27 |
+
|
| 28 |
+
if response.status_code == 200:
|
| 29 |
+
try:
|
| 30 |
+
result = response.json()
|
| 31 |
+
print("β
Success")
|
| 32 |
+
return result
|
| 33 |
+
except:
|
| 34 |
+
print(f"Response: {response.text[:200]}...")
|
| 35 |
+
return response.text
|
| 36 |
+
else:
|
| 37 |
+
print(f"β Error: {response.text}")
|
| 38 |
+
return None
|
| 39 |
+
|
| 40 |
+
except Exception as e:
|
| 41 |
+
print(f"β Connection Error: {e}")
|
| 42 |
+
return None
|
| 43 |
+
|
| 44 |
+
def main():
|
| 45 |
+
print("π§ͺ FastAPI Client Test")
|
| 46 |
+
print("=" * 40)
|
| 47 |
+
|
| 48 |
+
# Test 1: Get status
|
| 49 |
+
print("\n1οΈβ£ Testing /status endpoint...")
|
| 50 |
+
status = test_endpoint("GET", "/status")
|
| 51 |
+
if status:
|
| 52 |
+
print(f" Bot running: {status.get('is_running', 'Unknown')}")
|
| 53 |
+
print(f" Active sessions: {len(status.get('active_sessions', []))}")
|
| 54 |
+
|
| 55 |
+
# Test 2: Get root
|
| 56 |
+
print("\n2οΈβ£ Testing / root endpoint...")
|
| 57 |
+
root = test_endpoint("GET", "/")
|
| 58 |
+
|
| 59 |
+
# Test 3: Try to start BTC trading (will fail if server not running)
|
| 60 |
+
print("\n3οΈβ£ Testing /start/BTCUSDT endpoint...")
|
| 61 |
+
start_result = test_endpoint("POST", "/start/BTCUSDT", params={"duration_hours": 1})
|
| 62 |
+
if start_result:
|
| 63 |
+
print(" BTC trading session started!")
|
| 64 |
+
else:
|
| 65 |
+
print(" (Expected to fail if API server not running)")
|
| 66 |
+
|
| 67 |
+
# Test 4: Get sessions
|
| 68 |
+
print("\n4οΈβ£ Testing /sessions endpoint...")
|
| 69 |
+
sessions = test_endpoint("GET", "/sessions")
|
| 70 |
+
|
| 71 |
+
print("\n" + "=" * 40)
|
| 72 |
+
print("π Test Complete")
|
| 73 |
+
print("\nTo run the API server:")
|
| 74 |
+
print(" python3 api_server.py")
|
| 75 |
+
print("\nTo start trading:")
|
| 76 |
+
print(" curl -X POST http://localhost:8000/start/BTCUSDT")
|
| 77 |
+
print("\nTo check status:")
|
| 78 |
+
print(" curl http://localhost:8000/status")
|
| 79 |
+
|
| 80 |
+
if __name__ == "__main__":
|
| 81 |
+
main()
|
test/test_api_raw.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import time
|
| 3 |
+
import hashlib
|
| 4 |
+
import hmac
|
| 5 |
+
import uuid
|
| 6 |
+
import os
|
| 7 |
+
from dotenv import load_dotenv
|
| 8 |
+
|
| 9 |
+
load_dotenv()
|
| 10 |
+
|
| 11 |
+
api_key = os.getenv("BYBIT_API_KEY")
|
| 12 |
+
secret_key = os.getenv("BYBIT_API_SECRET")
|
| 13 |
+
httpClient = requests.Session()
|
| 14 |
+
recv_window = str(5000)
|
| 15 |
+
url = "https://api.bybit.com"
|
| 16 |
+
|
| 17 |
+
def HTTP_Request(endPoint, method, payload, Info):
|
| 18 |
+
global time_stamp
|
| 19 |
+
time_stamp = str(int(time.time() * 10 ** 3))
|
| 20 |
+
signature = genSignature(payload)
|
| 21 |
+
headers = {
|
| 22 |
+
'X-BAPI-API-KEY': api_key,
|
| 23 |
+
'X-BAPI-SIGN': signature,
|
| 24 |
+
'X-BAPI-SIGN-TYPE': '2',
|
| 25 |
+
'X-BAPI-TIMESTAMP': time_stamp,
|
| 26 |
+
'X-BAPI-RECV-WINDOW': recv_window,
|
| 27 |
+
'Content-Type': 'application/json'
|
| 28 |
+
}
|
| 29 |
+
if(method == "POST"):
|
| 30 |
+
response = httpClient.request(method, url + endPoint, headers=headers, data=payload)
|
| 31 |
+
else:
|
| 32 |
+
response = httpClient.request(method, url + endPoint + "?" + payload, headers=headers)
|
| 33 |
+
print(f"\n{Info}:")
|
| 34 |
+
print(f"Status Code: {response.status_code}")
|
| 35 |
+
print(f"Response: {response.text}")
|
| 36 |
+
print(f"Elapsed Time: {response.elapsed}")
|
| 37 |
+
return response
|
| 38 |
+
|
| 39 |
+
def genSignature(payload):
|
| 40 |
+
param_str = str(time_stamp) + api_key + recv_window + payload
|
| 41 |
+
hash = hmac.new(bytes(secret_key, "utf-8"), param_str.encode("utf-8"), hashlib.sha256)
|
| 42 |
+
signature = hash.hexdigest()
|
| 43 |
+
return signature
|
| 44 |
+
|
| 45 |
+
def test_wallet_balance():
|
| 46 |
+
print("Testing Wallet Balance...")
|
| 47 |
+
endpoint = "/v5/account/wallet-balance"
|
| 48 |
+
method = "GET"
|
| 49 |
+
params = 'accountType=UNIFIED'
|
| 50 |
+
response = HTTP_Request(endpoint, method, params, "Wallet Balance")
|
| 51 |
+
|
| 52 |
+
# Parse response to check for specific errors
|
| 53 |
+
try:
|
| 54 |
+
import json
|
| 55 |
+
resp_data = json.loads(response.text) if response.text else {}
|
| 56 |
+
ret_code = resp_data.get('retCode', -1)
|
| 57 |
+
ret_msg = resp_data.get('retMsg', 'Unknown error')
|
| 58 |
+
|
| 59 |
+
if ret_code == 0:
|
| 60 |
+
return True
|
| 61 |
+
else:
|
| 62 |
+
print(f" API Error: {ret_code} - {ret_msg}")
|
| 63 |
+
return False
|
| 64 |
+
except:
|
| 65 |
+
return response.status_code == 200
|
| 66 |
+
|
| 67 |
+
def test_positions():
|
| 68 |
+
print("Testing Positions...")
|
| 69 |
+
endpoint = "/v5/position/list"
|
| 70 |
+
method = "GET"
|
| 71 |
+
params = 'category=linear&settleCoin=USDT'
|
| 72 |
+
response = HTTP_Request(endpoint, method, params, "Positions")
|
| 73 |
+
return parse_api_response(response)
|
| 74 |
+
|
| 75 |
+
def test_create_order():
|
| 76 |
+
print("Testing Order Creation...")
|
| 77 |
+
print("Note: Order creation may fail due to symbol whitelisting or insufficient balance")
|
| 78 |
+
print("This is normal for testing - the bot will handle this gracefully")
|
| 79 |
+
|
| 80 |
+
endpoint = "/v5/order/create"
|
| 81 |
+
method = "POST"
|
| 82 |
+
orderLinkId = uuid.uuid4().hex
|
| 83 |
+
|
| 84 |
+
# Try market order with minimum viable amount
|
| 85 |
+
params = f'{{"category":"linear","symbol":"BTCUSDT","side":"Buy","orderType":"Limit","qty":"0.001","price":"84100","timeInForce":"GTC","orderLinkId":"{orderLinkId}"}}'
|
| 86 |
+
response = HTTP_Request(endpoint, method, params, "Create Order (Market)")
|
| 87 |
+
success = parse_api_response(response)
|
| 88 |
+
|
| 89 |
+
if not success:
|
| 90 |
+
print(" Market order failed - this is expected if symbol not whitelisted or insufficient balance")
|
| 91 |
+
print(" The bot can still function for monitoring and analysis")
|
| 92 |
+
|
| 93 |
+
return success, orderLinkId
|
| 94 |
+
|
| 95 |
+
def test_get_orders():
|
| 96 |
+
print("Testing Get Orders...")
|
| 97 |
+
endpoint = "/v5/order/realtime"
|
| 98 |
+
method = "GET"
|
| 99 |
+
params = 'category=linear&settleCoin=USDT'
|
| 100 |
+
response = HTTP_Request(endpoint, method, params, "Get Orders")
|
| 101 |
+
return parse_api_response(response)
|
| 102 |
+
|
| 103 |
+
def parse_api_response(response):
|
| 104 |
+
"""Parse API response and return success status"""
|
| 105 |
+
try:
|
| 106 |
+
import json
|
| 107 |
+
resp_data = json.loads(response.text) if response.text else {}
|
| 108 |
+
ret_code = resp_data.get('retCode', -1)
|
| 109 |
+
ret_msg = resp_data.get('retMsg', 'Unknown error')
|
| 110 |
+
|
| 111 |
+
if ret_code == 0:
|
| 112 |
+
return True
|
| 113 |
+
else:
|
| 114 |
+
print(f" API Error: {ret_code} - {ret_msg}")
|
| 115 |
+
return False
|
| 116 |
+
except Exception as e:
|
| 117 |
+
print(f" Parse Error: {e}")
|
| 118 |
+
return response.status_code == 200
|
| 119 |
+
|
| 120 |
+
def main():
|
| 121 |
+
print("π Bybit API Raw HTTP Test")
|
| 122 |
+
print("=" * 50)
|
| 123 |
+
|
| 124 |
+
if not api_key or not secret_key:
|
| 125 |
+
print("β API credentials not found!")
|
| 126 |
+
return
|
| 127 |
+
|
| 128 |
+
print(f"API Key: {api_key[:10]}...")
|
| 129 |
+
print(f"Secret Key: {secret_key[:10]}...")
|
| 130 |
+
print(f"Testnet URL: {url}")
|
| 131 |
+
|
| 132 |
+
tests = [
|
| 133 |
+
("Wallet Balance", test_wallet_balance),
|
| 134 |
+
("Positions", test_positions),
|
| 135 |
+
("Get Orders", test_get_orders),
|
| 136 |
+
]
|
| 137 |
+
|
| 138 |
+
results = []
|
| 139 |
+
for test_name, test_func in tests:
|
| 140 |
+
try:
|
| 141 |
+
success = test_func()
|
| 142 |
+
results.append((test_name, success))
|
| 143 |
+
print(f"β
{test_name}: {'PASS' if success else 'FAIL'}")
|
| 144 |
+
except Exception as e:
|
| 145 |
+
print(f"β {test_name}: ERROR - {e}")
|
| 146 |
+
results.append((test_name, False))
|
| 147 |
+
|
| 148 |
+
# Test order creation (optional)
|
| 149 |
+
try:
|
| 150 |
+
print("\nTesting Order Creation (will fail safely if no balance)...")
|
| 151 |
+
order_success, order_id = test_create_order()
|
| 152 |
+
results.append(("Create Order", order_success))
|
| 153 |
+
except Exception as e:
|
| 154 |
+
print(f"β Create Order: ERROR - {e}")
|
| 155 |
+
results.append(("Create Order", False))
|
| 156 |
+
|
| 157 |
+
print("\n" + "=" * 50)
|
| 158 |
+
print("π Test Results:")
|
| 159 |
+
passed = sum(1 for _, success in results if success)
|
| 160 |
+
total = len(results)
|
| 161 |
+
|
| 162 |
+
for test_name, success in results:
|
| 163 |
+
status = "β
PASS" if success else "β FAIL"
|
| 164 |
+
print(f" {test_name}: {status}")
|
| 165 |
+
|
| 166 |
+
print(f"\nπ― Overall: {passed}/{total} tests passed")
|
| 167 |
+
|
| 168 |
+
if passed == total:
|
| 169 |
+
print("π All API tests passed! Ready for trading.")
|
| 170 |
+
elif passed > 0:
|
| 171 |
+
print("β οΈ Some tests passed. Check permissions and account setup.")
|
| 172 |
+
else:
|
| 173 |
+
print("β All tests failed. Check API credentials and permissions.")
|
| 174 |
+
|
| 175 |
+
if __name__ == "__main__":
|
| 176 |
+
main()
|