-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
352 lines (313 loc) · 11 KB
/
main.py
File metadata and controls
352 lines (313 loc) · 11 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
"""
GRVT 交易机器人主程序
支持多种交易策略
"""
import argparse
import asyncio
import logging
import os
import sys
from decimal import Decimal
from dotenv import load_dotenv
from pysdk.grvt_ccxt_env import GrvtEnv
from strategies import GridTradingStrategy, PankouStrategy, HeartbeatMakerStrategy, DuiqiaoStrategy
# 加载环境变量
load_dotenv()
# ==================== 配置 ====================
TRADING_ACCOUNT_ID = os.getenv("GRVT_TRADING_ACCOUNT_ID", "")
PRIVATE_KEY = os.getenv("GRVT_PRIVATE_KEY", "")
API_KEY = os.getenv("GRVT_API_KEY", "")
ENV_NAME = os.getenv("GRVT_ENV", "TESTNET")
# ==================== 日志配置 ====================
script_name = os.path.splitext(os.path.basename(__file__))[0]
os.makedirs("log", exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(f"log/{script_name}.log"),
logging.StreamHandler(),
],
)
logger = logging.getLogger()
def validate_config():
"""验证配置是否完整"""
if not TRADING_ACCOUNT_ID:
logger.error("错误: GRVT_TRADING_ACCOUNT_ID 未设置")
return False
if not PRIVATE_KEY:
logger.error("错误: GRVT_PRIVATE_KEY 未设置")
return False
if not API_KEY:
logger.error("错误: GRVT_API_KEY 未设置")
return False
return True
def parse_args():
"""解析命令行参数"""
parser = argparse.ArgumentParser(
description="GRVT Trading Bot with Multiple Strategies",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Grid trading strategy (default)
%(prog)s --strategy grid --symbol SOL-USDT --dry-run
# Grid trading with custom parameters
%(prog)s --strategy grid --symbol BTC-USDT --grid-spacing 0.002 --order-amount 200
# Pankou (market making) strategy
%(prog)s --strategy pankou --symbol ETH-USDT --price-range 5 --trade-amount 0.01
Available Strategies:
grid - Dual-direction grid trading strategy
pankou - Market making strategy (spread capture)
heartbeat - Heartbeat Maker strategy (pure maker orders)
duiqiao - Duiqiao strategy (same-price buy/sell for volume rebates)
Fee Information:
Maker: -0.01%% (rebate)
Taker: 0.055%%
"""
)
# 策略选择
parser.add_argument(
"--strategy",
type=str,
default="grid",
choices=["grid", "pankou", "heartbeat", "duiqiao"],
help="Trading strategy to use (default: grid)",
)
# 通用参数
parser.add_argument(
"--symbol",
type=str,
default="SOL-USDT",
help="Trading pair symbol, format: BTC-USDT or ETH-USDT (default: SOL-USDT)",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Dry-run mode: simulate trades without actual execution",
)
# 网格策略参数
grid_group = parser.add_argument_group("Grid Strategy Options")
grid_group.add_argument(
"--grid-spacing",
type=float,
default=float(os.getenv("GRID_SPACING", "0.005")),
help="Grid spacing for grid strategy (default: 0.005)",
)
grid_group.add_argument(
"--order-amount",
type=str,
default=os.getenv("ORDER_AMOUNT_USDT", "100"),
help="Order amount in USDT for grid strategy (default: 100)",
)
grid_group.add_argument(
"--position-threshold",
type=str,
default=os.getenv("POSITION_THRESHOLD_USDT", "6000"),
help="Position threshold in USDT (default: 6000)",
)
grid_group.add_argument(
"--position-limit",
type=str,
default=os.getenv("POSITION_LIMIT_USDT", "3000"),
help="Position limit in USDT (default: 3000)",
)
# 盘口策略参数
pankou_group = parser.add_argument_group("Pankou Strategy Options")
pankou_group.add_argument(
"--price-range",
type=str,
default=os.getenv("PANKOU_PRICE_RANGE", "5"),
help="Price range for pankou strategy (default: 5)",
)
pankou_group.add_argument(
"--min-price-step",
type=str,
default=os.getenv("PANKOU_MIN_PRICE_STEP", "1"),
help="Minimum price step for pankou strategy (default: 1)",
)
pankou_group.add_argument(
"--trade-amount",
type=str,
default=os.getenv("PANKOU_TRADE_AMOUNT", "0.01"),
help="Trade amount for pankou strategy (default: 0.01)",
)
# 心跳策略参数
hb_group = parser.add_argument_group("Heartbeat Strategy Options")
hb_group.add_argument(
"--hb-interval",
type=int,
default=int(os.getenv("HB_INTERVAL_SECONDS", "60")),
help="Heartbeat interval in seconds (default: 60)",
)
hb_group.add_argument(
"--hb-initial-amount",
type=str,
default=os.getenv("HB_INITIAL_AMOUNT", "20"),
help="Initial base amount per trade (default: 20)",
)
hb_group.add_argument(
"--hb-initial-leverage",
type=int,
default=int(os.getenv("HB_INITIAL_LEVERAGE", "1")),
help="Initial leverage (default: 1)",
)
hb_group.add_argument(
"--hb-max-leverage",
type=int,
default=int(os.getenv("HB_MAX_LEVERAGE", "50")),
help="Max leverage cap (default: 50)",
)
hb_group.add_argument(
"--hb-size-scale-leverage",
action="store_true",
help="If set, multiplies order amount by leverage when API cannot set leverage",
)
# 对敲策略参数
dq_group = parser.add_argument_group("Duiqiao Strategy Options")
dq_group.add_argument(
"--dq-trade-amount",
type=str,
default=os.getenv("DUIQIAO_TRADE_AMOUNT", "0.01"),
help="Trade amount for duiqiao strategy (default: 0.01)",
)
dq_group.add_argument(
"--dq-target-amount",
type=str,
default=os.getenv("DUIQIAO_TARGET_AMOUNT", "1.0"),
help="Target total amount for duiqiao strategy (default: 1.0)",
)
dq_group.add_argument(
"--dq-use-maker-hedge",
action="store_true",
default=os.getenv("DUIQIAO_USE_MAKER_HEDGE", "true").lower() == "true",
help="Use Maker orders for hedging (default: true, gets rebate)",
)
dq_group.add_argument(
"--dq-hedge-wait-time",
type=int,
default=int(os.getenv("DUIQIAO_HEDGE_MAKER_WAIT_TIME", "3")),
help="Wait time for Maker hedge orders in seconds (default: 3)",
)
return parser.parse_args()
async def main():
"""主函数"""
# 解析命令行参数
args = parse_args()
# Dry-run 模式下跳过配置验证
if not args.dry_run and not validate_config():
logger.error("配置验证失败,请检查 .env 文件")
logger.info("提示: 使用 --dry-run 模式可以在不配置账户的情况下测试策略")
sys.exit(1)
# 解析交易对
try:
parts = args.symbol.upper().split("-")
if len(parts) != 2:
raise ValueError("交易对格式错误")
coin_name, contract_type = parts
except Exception as e:
logger.error(f"交易对格式错误: {args.symbol},应该是 BTC-USDT 格式")
sys.exit(1)
# 获取环境
env_map = {
"TESTNET": GrvtEnv.TESTNET,
"PROD": GrvtEnv.PROD,
"DEV": GrvtEnv.DEV,
}
env = env_map.get(ENV_NAME.upper(), GrvtEnv.TESTNET)
mode_str = "DRY-RUN 模拟模式" if args.dry_run else f"实盘模式 ({ENV_NAME})"
logger.info("=" * 70)
logger.info("GRVT 交易机器人启动")
logger.info(f"运行模式: {mode_str}")
logger.info(f"选择策略: {args.strategy}")
logger.info(f"交易对: {coin_name}-{contract_type}")
logger.info("=" * 70)
if args.dry_run:
logger.warning("⚠️ DRY-RUN 模式: 所有订单仅模拟,不会实际执行")
logger.warning("⚠️ 用于测试策略逻辑和参数配置")
logger.info("=" * 70)
# 创建策略实例
strategy = None
if args.strategy == "grid":
logger.info("初始化网格交易策略...")
grid_spacing = args.grid_spacing
order_amount_usdt = Decimal(args.order_amount)
strategy = GridTradingStrategy(
trading_account_id=TRADING_ACCOUNT_ID,
private_key=PRIVATE_KEY,
api_key=API_KEY,
coin_name=coin_name,
contract_type=contract_type,
env=env,
logger=logger,
grid_spacing=grid_spacing,
order_amount_usdt=order_amount_usdt,
dry_run=args.dry_run,
)
elif args.strategy == "pankou":
logger.info("初始化盘口做市策略...")
price_range = Decimal(args.price_range)
min_price_step = Decimal(args.min_price_step)
trade_amount = Decimal(args.trade_amount)
strategy = PankouStrategy(
trading_account_id=TRADING_ACCOUNT_ID,
private_key=PRIVATE_KEY,
api_key=API_KEY,
coin_name=coin_name,
contract_type=contract_type,
env=env,
logger=logger,
price_range=price_range,
min_price_step=min_price_step,
trade_amount=trade_amount,
dry_run=args.dry_run,
)
elif args.strategy == "heartbeat":
logger.info("初始化心跳 Maker 策略...")
strategy = HeartbeatMakerStrategy(
trading_account_id=TRADING_ACCOUNT_ID,
private_key=PRIVATE_KEY,
api_key=API_KEY,
coin_name=coin_name,
contract_type=contract_type,
env=env,
logger=logger,
dry_run=args.dry_run,
heartbeat_seconds=int(args.hb_interval),
initial_amount=Decimal(args.hb_initial_amount),
initial_leverage=int(args.hb_initial_leverage),
max_leverage=int(args.hb_max_leverage),
size_scale_leverage=bool(args.hb_size_scale_leverage),
)
elif args.strategy == "duiqiao":
logger.info("初始化对敲策略...")
trade_amount = Decimal(args.dq_trade_amount)
target_amount = Decimal(args.dq_target_amount)
use_maker_hedge = args.dq_use_maker_hedge
hedge_wait_time = args.dq_hedge_wait_time
strategy = DuiqiaoStrategy(
trading_account_id=TRADING_ACCOUNT_ID,
private_key=PRIVATE_KEY,
api_key=API_KEY,
coin_name=coin_name,
contract_type=contract_type,
env=env,
logger=logger,
trade_amount=trade_amount,
target_amount=target_amount,
use_maker_hedge=use_maker_hedge,
hedge_wait_time=hedge_wait_time,
dry_run=args.dry_run,
)
else:
logger.error(f"不支持的策略: {args.strategy}")
sys.exit(1)
# 运行策略
try:
await strategy.run()
except KeyboardInterrupt:
logger.info("收到停止信号,正在退出...")
except Exception as e:
logger.error(f"程序异常退出: {e}", exc_info=True)
sys.exit(1)
if __name__ == "__main__":
asyncio.run(main())