-
Notifications
You must be signed in to change notification settings - Fork 493
Expand file tree
/
Copy pathrunbot.py
More file actions
133 lines (111 loc) · 5.25 KB
/
runbot.py
File metadata and controls
133 lines (111 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python3
"""
Modular Trading Bot - Supports multiple exchanges
"""
import argparse
import asyncio
import logging
from pathlib import Path
import sys
import dotenv
from decimal import Decimal
from trading_bot import TradingBot, TradingConfig
from exchanges import ExchangeFactory
def parse_arguments():
"""Parse command line arguments."""
parser = argparse.ArgumentParser(description='Modular Trading Bot - Supports multiple exchanges')
# Exchange selection
parser.add_argument('--exchange', type=str, default='edgex',
choices=ExchangeFactory.get_supported_exchanges(),
help='Exchange to use (default: edgex). '
f'Available: {", ".join(ExchangeFactory.get_supported_exchanges())}')
# Trading parameters
parser.add_argument('--ticker', type=str, default='ETH',
help='Ticker (default: ETH)')
parser.add_argument('--quantity', type=Decimal, default=Decimal(0.1),
help='Order quantity (default: 0.1)')
parser.add_argument('--take-profit', type=Decimal, default=Decimal(0.02),
help='Take profit in USDT (default: 0.02)')
parser.add_argument('--direction', type=str, default='buy', choices=['buy', 'sell'],
help='Direction of the bot (default: buy)')
parser.add_argument('--max-orders', type=int, default=40,
help='Maximum number of active orders (default: 40)')
parser.add_argument('--wait-time', type=int, default=450,
help='Wait time between orders in seconds (default: 450)')
parser.add_argument('--env-file', type=str, default=".env",
help=".env file path (default: .env)")
parser.add_argument('--grid-step', type=str, default='-100',
help='The minimum distance in percentage to the next close order price (default: -100)')
parser.add_argument('--stop-price', type=Decimal, default=-1,
help='Price to stop trading and exit. Buy: exits if price >= stop-price.'
'Sell: exits if price <= stop-price. (default: -1, no stop)')
parser.add_argument('--pause-price', type=Decimal, default=-1,
help='Pause trading and wait. Buy: pause if price >= pause-price.'
'Sell: pause if price <= pause-price. (default: -1, no pause)')
parser.add_argument('--boost', action='store_true',
help='Use the Boost mode for volume boosting')
return parser.parse_args()
def setup_logging(log_level: str):
"""Setup global logging configuration."""
# Convert string level to logging constant
level = getattr(logging, log_level.upper(), logging.INFO)
# Clear any existing handlers to prevent duplicates
root_logger = logging.getLogger()
for handler in root_logger.handlers[:]:
root_logger.removeHandler(handler)
# Configure root logger WITHOUT adding a console handler
# This prevents duplicate logs when TradingLogger adds its own console handler
root_logger.setLevel(level)
# Suppress websockets debug logs unless DEBUG level is explicitly requested
if log_level.upper() != 'DEBUG':
logging.getLogger('websockets').setLevel(logging.WARNING)
# Suppress other noisy loggers
logging.getLogger('urllib3').setLevel(logging.WARNING)
logging.getLogger('requests').setLevel(logging.WARNING)
# Suppress Lighter SDK debug logs
logging.getLogger('lighter').setLevel(logging.WARNING)
# Also suppress any root logger DEBUG messages that might be coming from Lighter
if log_level.upper() != 'DEBUG':
# Set root logger to WARNING to suppress DEBUG messages from Lighter SDK
root_logger.setLevel(logging.WARNING)
async def main():
"""Main entry point."""
args = parse_arguments()
# Setup logging first
setup_logging("WARNING")
# Validate boost-mode can only be used with aster and backpack exchange
if args.boost and args.exchange.lower() != 'aster' and args.exchange.lower() != 'backpack':
print(f"Error: --boost can only be used when --exchange is 'aster' or 'backpack'. "
f"Current exchange: {args.exchange}")
sys.exit(1)
env_path = Path(args.env_file)
if not env_path.exists():
print(f"Env file not find: {env_path.resolve()}")
sys.exit(1)
dotenv.load_dotenv(args.env_file)
# Create configuration
config = TradingConfig(
ticker=args.ticker.upper(),
contract_id='', # will be set in the bot's run method
tick_size=Decimal(0),
quantity=args.quantity,
take_profit=args.take_profit,
direction=args.direction.lower(),
max_orders=args.max_orders,
wait_time=args.wait_time,
exchange=args.exchange.lower(),
grid_step=Decimal(args.grid_step),
stop_price=Decimal(args.stop_price),
pause_price=Decimal(args.pause_price),
boost_mode=args.boost
)
# Create and run the bot
bot = TradingBot(config)
try:
await bot.run()
except Exception as e:
print(f"Bot execution failed: {e}")
# The bot's run method already handles graceful shutdown
return
if __name__ == "__main__":
asyncio.run(main())