forked from Drakkar-Software/Triangular-Arbitrage
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_dex_with_execution.py
More file actions
executable file
·241 lines (196 loc) · 6.77 KB
/
Copy pathrun_dex_with_execution.py
File metadata and controls
executable file
·241 lines (196 loc) · 6.77 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
#!/usr/bin/env python3
"""
Run DEX arbitrage scanner with execution capability.
This script extends the paper trading scanner to actually execute profitable opportunities.
MODES:
1. Paper Trading (default): Scan only, no execution
2. Dry Run: Simulate execution (logs what would happen)
3. Live: Execute real transactions (REQUIRES PRIVATE KEY)
SAFETY:
- Dry run mode is enabled by default
- Private key must be explicitly provided via env var
- Min profit threshold enforced
- Rate limiting prevents spam execution
Usage:
# Paper trading (no execution)
python run_dex_with_execution.py --config configs/dex_bsc_dynamic.yaml
# Dry run (simulate execution)
python run_dex_with_execution.py --config configs/dex_bsc_dynamic.yaml --dry-run
# Live execution (DANGEROUS - requires private key)
export DEX_PRIVATE_KEY="0x..."
python run_dex_with_execution.py --config configs/dex_bsc_dynamic.yaml --live
Environment Variables:
DEX_PRIVATE_KEY: Private key for signing transactions (required for --live)
DEX_MAX_GAS_GWEI: Maximum gas price in gwei (default: 10)
DEX_MIN_PROFIT_USD: Minimum profit to execute in USD (default: 5.0)
"""
import argparse
import asyncio
import os
import sys
from pathlib import Path
# Add project root to path
sys.path.insert(0, str(Path(__file__).parent))
from dex.config import load_config # noqa: E402
from dex.execution_wrapper import ExecutionEnabledRunner # noqa: E402
from dex.executor import ExecutionConfig # noqa: E402
from triangular_arbitrage.utils import get_logger # noqa: E402
logger = get_logger(__name__)
def parse_args():
"""Parse command line arguments."""
parser = argparse.ArgumentParser(
description="DEX Arbitrage Scanner with Execution",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
parser.add_argument(
"--config",
type=str,
required=True,
help="Path to DEX config YAML file",
)
# Execution mode
mode_group = parser.add_mutually_exclusive_group()
mode_group.add_argument(
"--paper",
action="store_true",
help="Paper trading mode (scan only, no execution) [DEFAULT]",
)
mode_group.add_argument(
"--dry-run",
action="store_true",
help="Dry run mode (simulate execution, no real transactions)",
)
mode_group.add_argument(
"--live",
action="store_true",
help="Live mode (execute real transactions - REQUIRES DEX_PRIVATE_KEY)",
)
# Execution settings
parser.add_argument(
"--auto-execute",
action="store_true",
help="Automatically execute profitable opportunities (default: False)",
)
parser.add_argument(
"--min-profit",
type=float,
default=None,
help="Minimum profit in USD to execute (default: from config or 5.0)",
)
parser.add_argument(
"--max-gas",
type=float,
default=None,
help="Maximum gas price in gwei (default: from env or 10.0)",
)
# Other options
parser.add_argument(
"--quiet",
"-q",
action="store_true",
help="Quiet mode (less output)",
)
parser.add_argument(
"--once",
action="store_true",
help="Run once and exit (for testing)",
)
parser.add_argument(
"--max-pools",
type=int,
help="Limit pools per DEX (for faster scanning)",
)
return parser.parse_args()
def get_execution_config(args) -> ExecutionConfig:
"""
Build execution configuration from args and environment.
Args:
args: Parsed command line arguments
Returns:
ExecutionConfig instance
"""
# Determine mode
if args.live:
dry_run = False
mode_name = "LIVE"
elif args.dry_run:
dry_run = True
mode_name = "DRY RUN"
else: # paper (default)
dry_run = True
mode_name = "PAPER"
# Get private key from environment
private_key = os.getenv("DEX_PRIVATE_KEY")
if args.live and not private_key:
logger.error("LIVE mode requires DEX_PRIVATE_KEY environment variable")
logger.error("Set it with: export DEX_PRIVATE_KEY='0x...'")
sys.exit(1)
# Get execution parameters from env or args
max_gas_gwei = args.max_gas or float(os.getenv("DEX_MAX_GAS_GWEI", "10.0"))
min_profit_usd = args.min_profit or float(os.getenv("DEX_MIN_PROFIT_USD", "5.0"))
# Build config
config = ExecutionConfig(
private_key=private_key,
max_gas_price_gwei=max_gas_gwei,
max_priority_fee_gwei=2.0,
use_flashbots=True, # Always use MEV protection if available
dry_run_mode=dry_run,
min_profit_threshold_usd=min_profit_usd,
max_slippage_pct=1.0,
)
logger.info(f"Execution Mode: {mode_name}")
if not dry_run and private_key:
# Show first/last 4 chars of key for verification
key_preview = f"{private_key[:6]}...{private_key[-4:]}"
logger.info(f"Private Key: {key_preview}")
logger.info(f"Min Profit: ${min_profit_usd:.2f}")
logger.info(f"Max Gas: {max_gas_gwei:.1f} gwei")
logger.info(f"Auto-Execute: {args.auto_execute}")
return config
async def main():
"""Main entry point."""
args = parse_args()
try:
# Load DEX config
logger.info(f"Loading config from {args.config}...")
config = load_config(args.config)
# Override once flag if specified
if args.once:
config.once = True
# Build execution config
exec_config = get_execution_config(args)
# Initialize runner with execution capability
runner = ExecutionEnabledRunner(
config=config,
execution_config=exec_config,
auto_execute=args.auto_execute,
quiet=args.quiet,
)
# Connect to RPC
logger.info("Connecting to RPC...")
runner.connect()
# Build token maps
runner.build_token_maps()
# Fetch pools
logger.info("Fetching pools...")
runner.fetch_pools(max_pools_per_dex=args.max_pools)
# Run scanner with execution
logger.info("Starting scanner with execution capability...\n")
if args.auto_execute:
# Run with auto-execution
await runner.run_with_execution_async()
else:
# Run normal scanner (manual execution only)
await runner.run_async()
except KeyboardInterrupt:
logger.info("\n\nShutdown requested by user")
# Print execution summary
if hasattr(runner, "print_execution_summary"):
runner.print_execution_summary()
sys.exit(0)
except Exception as e:
logger.error(f"Fatal error: {e}", exc_info=True)
sys.exit(1)
if __name__ == "__main__":
asyncio.run(main())