Compare commits
8 Commits
d15ebf54ca
...
8bacf3c537
Author | SHA1 | Date | |
---|---|---|---|
8bacf3c537 | |||
ab73f95a3f | |||
09ed86c8ae | |||
e4a611a0cc | |||
936ccf10e6 | |||
5bd5c9f14d | |||
118c34b990 | |||
568ec049db |
3
.env
3
.env
@ -1,6 +1,7 @@
|
||||
# MEXC API Configuration (Spot Trading)
|
||||
MEXC_API_KEY=mx0vglhVPZeIJ32Qw1
|
||||
MEXC_SECRET_KEY=3bfe4bd99d5541e4a1bca87ab257cc7e
|
||||
MEXC_SECRET_KEY=3bfe4bd99d5541e4a1bca87ab257cc7e
|
||||
#3bfe4bd99d5541e4a1bca87ab257cc7e 45d0b3c26f2644f19bfb98b07741b2f5
|
||||
|
||||
# BASE ENDPOINTS: https://api.mexc.com wss://wbs-api.mexc.com/ws !!! DO NOT CHANGE THIS
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -41,3 +41,4 @@ closed_trades_history.json
|
||||
data/cnn_training/cnn_training_data*
|
||||
testcases/*
|
||||
testcases/negative/case_index.json
|
||||
chrome_user_data/*
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -154,7 +154,7 @@ trading:
|
||||
# MEXC Trading API Configuration
|
||||
mexc_trading:
|
||||
enabled: true
|
||||
trading_mode: simulation # simulation, testnet, live
|
||||
trading_mode: live # simulation, testnet, live
|
||||
|
||||
# FIXED: Meaningful position sizes for learning
|
||||
base_position_usd: 25.0 # $25 base position (was $1)
|
||||
|
292
config.yaml.backup_20250702_202543
Normal file
292
config.yaml.backup_20250702_202543
Normal file
@ -0,0 +1,292 @@
|
||||
# Enhanced Multi-Modal Trading System Configuration
|
||||
|
||||
# System Settings
|
||||
system:
|
||||
timezone: "Europe/Sofia" # Configurable timezone for all timestamps
|
||||
log_level: "INFO" # DEBUG, INFO, WARNING, ERROR
|
||||
session_timeout: 3600 # Session timeout in seconds
|
||||
|
||||
# Trading Symbols Configuration
|
||||
# Primary trading pair: ETH/USDT (main signals generation)
|
||||
# Reference pair: BTC/USDT (correlation analysis only, no trading signals)
|
||||
symbols:
|
||||
- "ETH/USDT" # MAIN TRADING PAIR - Generate signals and execute trades
|
||||
- "BTC/USDT" # REFERENCE ONLY - For correlation analysis, no direct trading
|
||||
|
||||
# Timeframes for ultra-fast scalping (500x leverage)
|
||||
timeframes:
|
||||
- "1s" # Primary scalping timeframe
|
||||
- "1m" # Short-term confirmation
|
||||
- "1h" # Medium-term trend
|
||||
- "1d" # Long-term direction
|
||||
|
||||
# Data Provider Settings
|
||||
data:
|
||||
provider: "binance"
|
||||
cache_enabled: true
|
||||
cache_dir: "cache"
|
||||
historical_limit: 1000
|
||||
real_time_enabled: true
|
||||
websocket_reconnect: true
|
||||
feature_engineering:
|
||||
technical_indicators: true
|
||||
market_regime_detection: true
|
||||
volatility_analysis: true
|
||||
|
||||
# Enhanced CNN Configuration
|
||||
cnn:
|
||||
window_size: 20
|
||||
features: ["open", "high", "low", "close", "volume"]
|
||||
timeframes: ["1m", "5m", "15m", "1h", "4h", "1d"]
|
||||
hidden_layers: [64, 128, 256]
|
||||
dropout: 0.2
|
||||
learning_rate: 0.001
|
||||
batch_size: 32
|
||||
epochs: 100
|
||||
confidence_threshold: 0.6
|
||||
early_stopping_patience: 10
|
||||
model_dir: "models/enhanced_cnn" # Ultra-fast scalping weights (500x leverage)
|
||||
timeframe_importance:
|
||||
"1s": 0.60 # Primary scalping signal
|
||||
"1m": 0.20 # Short-term confirmation
|
||||
"1h": 0.15 # Medium-term trend
|
||||
"1d": 0.05 # Long-term direction (minimal)
|
||||
|
||||
# Enhanced RL Agent Configuration
|
||||
rl:
|
||||
state_size: 100 # Will be calculated dynamically based on features
|
||||
action_space: 3 # BUY, HOLD, SELL
|
||||
hidden_size: 256
|
||||
epsilon: 1.0
|
||||
epsilon_decay: 0.995
|
||||
epsilon_min: 0.01
|
||||
learning_rate: 0.0001
|
||||
gamma: 0.99
|
||||
memory_size: 10000
|
||||
batch_size: 64
|
||||
target_update_freq: 1000
|
||||
buffer_size: 10000
|
||||
model_dir: "models/enhanced_rl"
|
||||
# Market regime adaptation
|
||||
market_regime_weights:
|
||||
trending: 1.2 # Higher confidence in trending markets
|
||||
ranging: 0.8 # Lower confidence in ranging markets
|
||||
volatile: 0.6 # Much lower confidence in volatile markets
|
||||
# Prioritized experience replay
|
||||
replay_alpha: 0.6 # Priority exponent
|
||||
replay_beta: 0.4 # Importance sampling exponent
|
||||
|
||||
# Enhanced Orchestrator Settings
|
||||
orchestrator:
|
||||
# Model weights for decision combination
|
||||
cnn_weight: 0.7 # Weight for CNN predictions
|
||||
rl_weight: 0.3 # Weight for RL decisions
|
||||
confidence_threshold: 0.20 # Lowered from 0.35 for low-volatility markets
|
||||
confidence_threshold_close: 0.10 # Lowered from 0.15 for easier exits
|
||||
decision_frequency: 30 # Seconds between decisions (faster)
|
||||
|
||||
# Multi-symbol coordination
|
||||
symbol_correlation_matrix:
|
||||
"ETH/USDT-BTC/USDT": 0.85 # ETH-BTC correlation
|
||||
|
||||
# Perfect move marking
|
||||
perfect_move_threshold: 0.02 # 2% price change to mark as significant
|
||||
perfect_move_buffer_size: 10000
|
||||
|
||||
# RL evaluation settings
|
||||
evaluation_delay: 3600 # Evaluate actions after 1 hour
|
||||
reward_calculation:
|
||||
success_multiplier: 10 # Reward for correct predictions
|
||||
failure_penalty: 5 # Penalty for wrong predictions
|
||||
confidence_scaling: true # Scale rewards by confidence
|
||||
|
||||
# Training Configuration
|
||||
training:
|
||||
learning_rate: 0.001
|
||||
batch_size: 32
|
||||
epochs: 100
|
||||
validation_split: 0.2
|
||||
early_stopping_patience: 10
|
||||
|
||||
# CNN specific training
|
||||
cnn_training_interval: 3600 # Train CNN every hour (was 6 hours)
|
||||
min_perfect_moves: 50 # Reduced from 200 for faster learning
|
||||
|
||||
# RL specific training
|
||||
rl_training_interval: 300 # Train RL every 5 minutes (was 1 hour)
|
||||
min_experiences: 50 # Reduced from 100 for faster learning
|
||||
training_steps_per_cycle: 20 # Increased from 10 for more learning
|
||||
|
||||
model_type: "optimized_short_term"
|
||||
use_realtime: true
|
||||
use_ticks: true
|
||||
checkpoint_dir: "NN/models/saved/realtime_ticks_checkpoints"
|
||||
save_best_model: true
|
||||
save_final_model: false # We only want to keep the best performing model
|
||||
|
||||
# Continuous learning settings
|
||||
continuous_learning: true
|
||||
learning_from_trades: true
|
||||
pattern_recognition: true
|
||||
retrospective_learning: true
|
||||
|
||||
# Trading Execution
|
||||
trading:
|
||||
max_position_size: 0.05 # Maximum position size (5% of balance)
|
||||
stop_loss: 0.02 # 2% stop loss
|
||||
take_profit: 0.05 # 5% take profit
|
||||
trading_fee: 0.0005 # 0.05% trading fee (MEXC taker fee - fallback)
|
||||
|
||||
# MEXC Fee Structure (asymmetrical) - Updated 2025-05-28
|
||||
trading_fees:
|
||||
maker: 0.0000 # 0.00% maker fee (adds liquidity)
|
||||
taker: 0.0005 # 0.05% taker fee (takes liquidity)
|
||||
default: 0.0005 # Default fallback fee (taker rate)
|
||||
|
||||
# Risk management
|
||||
max_daily_trades: 20 # Maximum trades per day
|
||||
max_concurrent_positions: 2 # Max positions across symbols
|
||||
position_sizing:
|
||||
confidence_scaling: true # Scale position by confidence
|
||||
base_size: 0.02 # 2% base position
|
||||
max_size: 0.05 # 5% maximum position
|
||||
|
||||
# MEXC Trading API Configuration
|
||||
mexc_trading:
|
||||
enabled: true
|
||||
trading_mode: simulation # simulation, testnet, live
|
||||
|
||||
# FIXED: Meaningful position sizes for learning
|
||||
base_position_usd: 25.0 # $25 base position (was $1)
|
||||
max_position_value_usd: 50.0 # $50 max position (was $1)
|
||||
min_position_value_usd: 10.0 # $10 min position (was $0.10)
|
||||
|
||||
# Risk management
|
||||
max_daily_trades: 100
|
||||
max_daily_loss_usd: 200.0
|
||||
max_concurrent_positions: 3
|
||||
min_trade_interval_seconds: 30
|
||||
|
||||
# Order configuration
|
||||
order_type: market # market or limit
|
||||
|
||||
# Enhanced fee structure for better calculation
|
||||
trading_fees:
|
||||
maker_fee: 0.0002 # 0.02% maker fee
|
||||
taker_fee: 0.0006 # 0.06% taker fee
|
||||
default_fee: 0.0006 # Default to taker fee
|
||||
|
||||
# Memory Management
|
||||
memory:
|
||||
total_limit_gb: 28.0 # Total system memory limit
|
||||
model_limit_gb: 4.0 # Per-model memory limit
|
||||
cleanup_interval: 1800 # Memory cleanup every 30 minutes
|
||||
|
||||
# Real-time RL COB Trader Configuration
|
||||
realtime_rl:
|
||||
# Model parameters for 400M parameter network (faster startup)
|
||||
model:
|
||||
input_size: 2000 # COB feature dimensions
|
||||
hidden_size: 2048 # Optimized hidden layer size for 400M params
|
||||
num_layers: 8 # Efficient transformer layers for faster training
|
||||
learning_rate: 0.0001 # Higher learning rate for faster convergence
|
||||
weight_decay: 0.00001 # Balanced L2 regularization
|
||||
|
||||
# Inference configuration
|
||||
inference_interval_ms: 200 # Inference every 200ms
|
||||
min_confidence_threshold: 0.7 # Minimum confidence for signal accumulation
|
||||
required_confident_predictions: 3 # Need 3 confident predictions for trade
|
||||
|
||||
# Training configuration
|
||||
training_interval_s: 1.0 # Train every second
|
||||
batch_size: 32 # Training batch size
|
||||
replay_buffer_size: 1000 # Store last 1000 predictions for training
|
||||
|
||||
# Signal accumulation
|
||||
signal_buffer_size: 10 # Buffer size for signal accumulation
|
||||
consensus_threshold: 3 # Need 3 signals in same direction
|
||||
|
||||
# Model checkpointing
|
||||
model_checkpoint_dir: "models/realtime_rl_cob"
|
||||
save_interval_s: 300 # Save models every 5 minutes
|
||||
|
||||
# COB integration
|
||||
symbols: ["BTC/USDT", "ETH/USDT"] # Symbols to trade
|
||||
cob_feature_normalization: "robust" # Feature normalization method
|
||||
|
||||
# Reward engineering for RL
|
||||
reward_structure:
|
||||
correct_direction_base: 1.0 # Base reward for correct prediction
|
||||
confidence_scaling: true # Scale reward by confidence
|
||||
magnitude_bonus: 0.5 # Bonus for predicting magnitude accurately
|
||||
overconfidence_penalty: 1.5 # Penalty multiplier for wrong high-confidence predictions
|
||||
trade_execution_multiplier: 10.0 # Higher weight for actual trade outcomes
|
||||
|
||||
# Performance monitoring
|
||||
statistics_interval_s: 60 # Print stats every minute
|
||||
detailed_logging: true # Enable detailed performance logging
|
||||
|
||||
# Web Dashboard
|
||||
web:
|
||||
host: "127.0.0.1"
|
||||
port: 8050
|
||||
debug: false
|
||||
update_interval: 500 # Milliseconds
|
||||
chart_history: 200 # Number of candles to show
|
||||
|
||||
# Enhanced dashboard features
|
||||
show_timeframe_analysis: true
|
||||
show_confidence_scores: true
|
||||
show_perfect_moves: true
|
||||
show_rl_metrics: true
|
||||
|
||||
# Logging
|
||||
logging:
|
||||
level: "INFO"
|
||||
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
file: "logs/enhanced_trading.log"
|
||||
max_size: 10485760 # 10MB
|
||||
backup_count: 5
|
||||
|
||||
# Component-specific logging
|
||||
orchestrator_level: "INFO"
|
||||
cnn_level: "INFO"
|
||||
rl_level: "INFO"
|
||||
training_level: "INFO"
|
||||
|
||||
# Model Directories
|
||||
model_dir: "models"
|
||||
data_dir: "data"
|
||||
cache_dir: "cache"
|
||||
logs_dir: "logs"
|
||||
|
||||
# GPU/Performance
|
||||
gpu:
|
||||
enabled: true
|
||||
memory_fraction: 0.8 # Use 80% of GPU memory
|
||||
allow_growth: true # Allow dynamic memory allocation
|
||||
|
||||
# Monitoring and Alerting
|
||||
monitoring:
|
||||
tensorboard_enabled: true
|
||||
tensorboard_log_dir: "logs/tensorboard"
|
||||
metrics_interval: 300 # Log metrics every 5 minutes
|
||||
performance_alerts: true
|
||||
|
||||
# Performance thresholds
|
||||
min_confidence_threshold: 0.3
|
||||
max_memory_usage: 0.9 # 90% of available memory
|
||||
max_decision_latency: 10 # 10 seconds max per decision
|
||||
|
||||
# Backtesting (for future implementation)
|
||||
backtesting:
|
||||
start_date: "2024-01-01"
|
||||
end_date: "2024-12-31"
|
||||
initial_balance: 10000
|
||||
commission: 0.0002
|
||||
slippage: 0.0001
|
||||
|
||||
model_paths:
|
||||
realtime_model: "NN/models/saved/optimized_short_term_model_realtime_best.pt"
|
||||
ticks_model: "NN/models/saved/optimized_short_term_model_ticks_best.pt"
|
||||
backup_model: "NN/models/saved/realtime_ticks_checkpoints/checkpoint_epoch_50449_backup/model.pt"
|
@ -27,7 +27,6 @@ try:
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.common.exceptions import TimeoutException, WebDriverException
|
||||
from webdriver_manager.chrome import ChromeDriverManager
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
except ImportError:
|
||||
print("Please install selenium and webdriver-manager:")
|
||||
print("pip install selenium webdriver-manager")
|
||||
@ -67,73 +66,74 @@ class MEXCRequestInterceptor:
|
||||
self.requests_file = f"mexc_requests_{self.timestamp}.json"
|
||||
self.cookies_file = f"mexc_cookies_{self.timestamp}.json"
|
||||
|
||||
def setup_chrome_with_logging(self) -> webdriver.Chrome:
|
||||
"""Setup Chrome with performance logging enabled"""
|
||||
logger.info("Setting up ChromeDriver with request interception...")
|
||||
|
||||
# Chrome options
|
||||
chrome_options = Options()
|
||||
|
||||
def setup_browser(self):
|
||||
"""Setup Chrome browser with necessary options"""
|
||||
chrome_options = webdriver.ChromeOptions()
|
||||
# Enable headless mode if needed
|
||||
if self.headless:
|
||||
chrome_options.add_argument("--headless")
|
||||
logger.info("Running in headless mode")
|
||||
chrome_options.add_argument('--headless')
|
||||
chrome_options.add_argument('--disable-gpu')
|
||||
chrome_options.add_argument('--window-size=1920,1080')
|
||||
chrome_options.add_argument('--disable-extensions')
|
||||
|
||||
# Essential options for automation
|
||||
chrome_options.add_argument("--no-sandbox")
|
||||
chrome_options.add_argument("--disable-dev-shm-usage")
|
||||
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
|
||||
chrome_options.add_argument("--disable-web-security")
|
||||
chrome_options.add_argument("--allow-running-insecure-content")
|
||||
chrome_options.add_argument("--disable-features=VizDisplayCompositor")
|
||||
# Set up Chrome options with a user data directory to persist session
|
||||
user_data_base_dir = os.path.join(os.getcwd(), 'chrome_user_data')
|
||||
os.makedirs(user_data_base_dir, exist_ok=True)
|
||||
|
||||
# User agent to avoid detection
|
||||
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
chrome_options.add_argument(f"--user-agent={user_agent}")
|
||||
# Check for existing session directories
|
||||
session_dirs = [d for d in os.listdir(user_data_base_dir) if d.startswith('session_')]
|
||||
session_dirs.sort(reverse=True) # Sort descending to get the most recent first
|
||||
|
||||
# Disable automation flags
|
||||
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
||||
chrome_options.add_experimental_option('useAutomationExtension', False)
|
||||
user_data_dir = None
|
||||
if session_dirs:
|
||||
use_existing = input(f"Found {len(session_dirs)} existing sessions. Use an existing session? (y/n): ").lower().strip() == 'y'
|
||||
if use_existing:
|
||||
print("Available sessions:")
|
||||
for i, session in enumerate(session_dirs[:5], 1): # Show up to 5 most recent
|
||||
print(f"{i}. {session}")
|
||||
choice = input("Enter session number (default 1) or any other key for most recent: ")
|
||||
if choice.isdigit() and 1 <= int(choice) <= len(session_dirs):
|
||||
selected_session = session_dirs[int(choice) - 1]
|
||||
else:
|
||||
selected_session = session_dirs[0]
|
||||
user_data_dir = os.path.join(user_data_base_dir, selected_session)
|
||||
print(f"Using session: {selected_session}")
|
||||
|
||||
# Enable performance logging for network requests
|
||||
chrome_options.add_argument("--enable-logging")
|
||||
chrome_options.add_argument("--log-level=0")
|
||||
chrome_options.add_argument("--v=1")
|
||||
if user_data_dir is None:
|
||||
user_data_dir = os.path.join(user_data_base_dir, f'session_{self.timestamp}')
|
||||
os.makedirs(user_data_dir, exist_ok=True)
|
||||
print(f"Creating new session: session_{self.timestamp}")
|
||||
|
||||
# Set capabilities for performance logging
|
||||
caps = DesiredCapabilities.CHROME
|
||||
caps['goog:loggingPrefs'] = {
|
||||
'performance': 'ALL',
|
||||
'browser': 'ALL'
|
||||
}
|
||||
chrome_options.add_argument(f'--user-data-dir={user_data_dir}')
|
||||
|
||||
# Enable logging to capture JS console output and network activity
|
||||
chrome_options.set_capability('goog:loggingPrefs', {
|
||||
'browser': 'ALL',
|
||||
'performance': 'ALL'
|
||||
})
|
||||
|
||||
try:
|
||||
# Automatically download and install ChromeDriver
|
||||
logger.info("Downloading/updating ChromeDriver...")
|
||||
service = Service(ChromeDriverManager().install())
|
||||
|
||||
# Create driver
|
||||
driver = webdriver.Chrome(
|
||||
service=service,
|
||||
options=chrome_options,
|
||||
desired_capabilities=caps
|
||||
)
|
||||
|
||||
# Hide automation indicators
|
||||
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
|
||||
driver.execute_cdp_cmd('Network.setUserAgentOverride', {
|
||||
"userAgent": user_agent
|
||||
})
|
||||
|
||||
# Enable network domain for CDP
|
||||
driver.execute_cdp_cmd('Network.enable', {})
|
||||
driver.execute_cdp_cmd('Runtime.enable', {})
|
||||
|
||||
logger.info("ChromeDriver setup complete!")
|
||||
return driver
|
||||
|
||||
self.driver = webdriver.Chrome(options=chrome_options)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to setup ChromeDriver: {e}")
|
||||
raise
|
||||
print(f"Failed to start browser with session: {e}")
|
||||
print("Falling back to a new session...")
|
||||
user_data_dir = os.path.join(user_data_base_dir, f'session_{self.timestamp}_fallback')
|
||||
os.makedirs(user_data_dir, exist_ok=True)
|
||||
print(f"Creating fallback session: session_{self.timestamp}_fallback")
|
||||
chrome_options = webdriver.ChromeOptions()
|
||||
if self.headless:
|
||||
chrome_options.add_argument('--headless')
|
||||
chrome_options.add_argument('--disable-gpu')
|
||||
chrome_options.add_argument('--window-size=1920,1080')
|
||||
chrome_options.add_argument('--disable-extensions')
|
||||
chrome_options.add_argument(f'--user-data-dir={user_data_dir}')
|
||||
chrome_options.set_capability('goog:loggingPrefs', {
|
||||
'browser': 'ALL',
|
||||
'performance': 'ALL'
|
||||
})
|
||||
self.driver = webdriver.Chrome(options=chrome_options)
|
||||
|
||||
return self.driver
|
||||
|
||||
def start_monitoring(self):
|
||||
"""Start the browser and begin monitoring"""
|
||||
@ -141,7 +141,7 @@ class MEXCRequestInterceptor:
|
||||
|
||||
try:
|
||||
# Setup ChromeDriver
|
||||
self.driver = self.setup_chrome_with_logging()
|
||||
self.driver = self.setup_browser()
|
||||
|
||||
# Navigate to MEXC futures
|
||||
mexc_url = "https://www.mexc.com/en-GB/futures/ETH_USDT?type=linear_swap"
|
||||
@ -322,6 +322,27 @@ class MEXCRequestInterceptor:
|
||||
print(f"\n🚀 CAPTURED REQUEST: {request_info['method']} {url}")
|
||||
if request_info['postData']:
|
||||
print(f" 📄 POST Data: {request_info['postData'][:100]}...")
|
||||
|
||||
# Enhanced captcha detection and detailed logging
|
||||
if 'captcha' in url.lower() or 'robot' in url.lower():
|
||||
logger.info(f"CAPTCHA REQUEST DETECTED: {request_data.get('request', {}).get('method', 'UNKNOWN')} {url}")
|
||||
logger.info(f" Headers: {request_data.get('request', {}).get('headers', {})}")
|
||||
if request_data.get('request', {}).get('postData', ''):
|
||||
logger.info(f" Data: {request_data.get('request', {}).get('postData', '')}")
|
||||
# Attempt to capture related JavaScript or DOM elements (if possible)
|
||||
if self.driver is not None:
|
||||
try:
|
||||
js_snippet = self.driver.execute_script("return document.querySelector('script[src*=\"captcha\"]') ? document.querySelector('script[src*=\"captcha\"]').outerHTML : 'No captcha script found';")
|
||||
logger.info(f" Related JS Snippet: {js_snippet}")
|
||||
except Exception as e:
|
||||
logger.warning(f" Could not capture JS snippet: {e}")
|
||||
try:
|
||||
dom_element = self.driver.execute_script("return document.querySelector('div[id*=\"captcha\"]') ? document.querySelector('div[id*=\"captcha\"]').outerHTML : 'No captcha element found';")
|
||||
logger.info(f" Related DOM Element: {dom_element}")
|
||||
except Exception as e:
|
||||
logger.warning(f" Could not capture DOM element: {e}")
|
||||
else:
|
||||
logger.warning(" Driver not initialized, cannot capture JS or DOM elements")
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"Error processing request: {e}")
|
||||
@ -417,6 +438,16 @@ class MEXCRequestInterceptor:
|
||||
if self.session_cookies:
|
||||
print(f" 🍪 Cookies: {self.cookies_file}")
|
||||
|
||||
# Extract and save CAPTCHA tokens from captured requests
|
||||
captcha_tokens = self.extract_captcha_tokens()
|
||||
if captcha_tokens:
|
||||
captcha_file = f"mexc_captcha_tokens_{self.timestamp}.json"
|
||||
with open(captcha_file, 'w') as f:
|
||||
json.dump(captcha_tokens, f, indent=2)
|
||||
logger.info(f"Saved CAPTCHA tokens to {captcha_file}")
|
||||
else:
|
||||
logger.warning("No CAPTCHA tokens found in captured requests")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error saving data: {e}")
|
||||
|
||||
@ -466,6 +497,28 @@ class MEXCRequestInterceptor:
|
||||
if self.save_to_file and (self.captured_requests or self.captured_responses):
|
||||
self._save_all_data()
|
||||
logger.info("Final data save complete")
|
||||
|
||||
def extract_captcha_tokens(self):
|
||||
"""Extract CAPTCHA tokens from captured requests"""
|
||||
captcha_tokens = []
|
||||
for request in self.captured_requests:
|
||||
if 'captcha-token' in request.get('headers', {}):
|
||||
token = request['headers']['captcha-token']
|
||||
captcha_tokens.append({
|
||||
'token': token,
|
||||
'url': request.get('url', ''),
|
||||
'timestamp': request.get('timestamp', '')
|
||||
})
|
||||
elif 'captcha' in request.get('url', '').lower():
|
||||
response = request.get('response', {})
|
||||
if response and 'captcha-token' in response.get('headers', {}):
|
||||
token = response['headers']['captcha-token']
|
||||
captcha_tokens.append({
|
||||
'token': token,
|
||||
'url': request.get('url', ''),
|
||||
'timestamp': request.get('timestamp', '')
|
||||
})
|
||||
return captcha_tokens
|
||||
|
||||
def main():
|
||||
"""Main function to run the interceptor"""
|
||||
|
37
core/mexc_webclient/mexc_credentials.json
Normal file
37
core/mexc_webclient/mexc_credentials.json
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
{
|
||||
"note": "No CAPTCHA tokens were found in the latest run. Manual extraction of cookies may be required from mexc_requests_20250703_024032.json.",
|
||||
"credentials": {
|
||||
"cookies": {
|
||||
"bm_sv": "D92603BBC020E9C2CD11B2EBC8F22050~YAAQJKVf1NW5K7CXAQAAwtMVzRzHARcY60jrPVzy9G79fN3SY4z988SWHHxQlbPpyZHOj76c20AjCnS0QwveqzB08zcRoauoIe/sP3svlaIso9PIdWay0KIIVUe1XsiTJRfTm/DmS+QdrOuJb09rbfWLcEJF4/0QK7VY0UTzPTI2V3CMtxnmYjd1+tjfYsvt1R6O+Mw9mYjb7SjhRmiP/exY2UgZdLTJiqd+iWkc5Wejy5m6g5duOfRGtiA9mfs=~1",
|
||||
"bm_sz": "98D80FE4B23FE6352AE5194DA699FDDB~YAAQJKVf1GK4K7CXAQAAeQ0UzRw+aXiY5/Ujp+sZm0a4j+XAJFn6fKT4oph8YqIKF6uHSgXkFY3mBt8WWY98Y2w1QzOEFRkje8HTUYQgJsV59y5DIOTZKC6wutPD/bKdVi9ZKtk4CWbHIIRuCrnU1Nw2jqj5E0hsorhKGh8GeVsAeoao8FWovgdYD6u8Qpbr9aL5YZgVEIqJx6WmWLmcIg+wA8UFj8751Fl0B3/AGxY2pACUPjonPKNuX/UDYA5e98plOYUnYLyQMEGIapSrWKo1VXhKBDPLNedJ/Q2gOCGEGlj/u1Fs407QxxXwCvRSegL91y6modtL5JGoFucV1pYc4pgTwEAEdJfcLCEBaButTbaHI9T3SneqgCoGeatMMaqz0GHbvMD7fBQofARBqzN1L6aGlmmAISMzI3wx/SnsfXBl~3228228~3294529",
|
||||
"_abck": "0288E759712AF333A6EE15F66BC2A662~-1~YAAQJKVf1GC4K7CXAQAAeQ0UzQ77TfyX5SOWTgdW3DVqNFrTLz2fhLo2OC4I6ZHnW9qB0vwTjFDfOB65BwLSeFZoyVypVCGTtY/uL6f4zX0AxEGAU8tLg/jeO0acO4JpGrjYZSW1F56vEd9JbPU2HQPNERorgCDLQMSubMeLCfpqMp3VCW4w0Ssnk6Y4pBSs4mh0PH95v56XXDvat9k20/JPoK3Ip5kK2oKh5Vpk5rtNTVea66P0NBjVUw/EddRUuDDJpc8T4DtTLDXnD5SNDxEq8WDkrYd5kP4dNe0PtKcSOPYs2QLUbvAzfBuMvnhoSBaCjsqD15EZ3eDAoioli/LzsWSxaxetYfm0pA/s5HBXMdOEDi4V0E9b79N28rXcC8IJEHXtfdZdhJjwh1FW14lqF9iuOwER81wDEnIVtgwTwpd3ffrc35aNjb+kGiQ8W0FArFhUI/ZY2NDvPVngRjNrmRm0CsCm+6mdxxVNsGNMPKYG29mcGDi2P9HGDk45iOm0vzoaYUl1PlOh4VGq/V3QGbPYpkBsBtQUjrf/SQJe5IAbjCICTYlgxTo+/FAEjec+QdUsagTgV8YNycQfTK64A2bs1L1n+RO5tapLThU6NkxnUbqHOm6168RnT8ZRoAUpkJ5m3QpqSsuslnPRUPyxUr73v514jTBIUGsq4pUeRpXXd9FAh8Xkn4VZ9Bh3q4jP7eZ9Sv58mgnEVltNBFkeG3zsuIp5Hu69MSBU+8FD4gVlncbBinrTLNWRB8F00Gyvc03unrAznsTEyLiDq9guQf9tQNcGjxfggfnGq/Z1Gy/A7WMjiYw7pwGRVzAYnRgtcZoww9gQ/FdGkbp2Xl+oVZpaqFsHVvafWyOFr4pqQsmd353ddgKLjsEnpy/jcdUsIR/Ph3pYv++XlypXehXj0/GHL+WsosujJrYk4TuEsPKUcyHNr+r844mYUIhCYsI6XVKrq3fimdfdhmlkW8J1kZSTmFwP8QcwGlTK/mZDTJPyf8K5ugXcqOU8oIQzt5B2zfRwRYKHdhb8IUw=~-1~-1~-1",
|
||||
"RT": "\"z=1&dm=www.mexc.com&si=f5d53b58-7845-4db4-99f1-444e43d35199&ss=mcmh857q&sl=3&tt=90n&bcn=%2F%2F684dd311.akstat.io%2F&ld=1c9o\"",
|
||||
"mexc_fingerprint_visitorId": "tv1xchuZQbx9N0aBztUG",
|
||||
"_ga_L6XJCQTK75": "GS2.1.s1751492192$o1$g1$t1751492248$j4$l0$h0",
|
||||
"uc_token": "WEB66f893ede865e5d927efdea4a82e655ad5190239c247997d744ef9cd075f6f1e",
|
||||
"u_id": "WEB66f893ede865e5d927efdea4a82e655ad5190239c247997d744ef9cd075f6f1e",
|
||||
"_fbp": "fb.1.1751492193579.314807866777158389",
|
||||
"mxc_exchange_layout": "BA",
|
||||
"sensorsdata2015jssdkcross": "%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22197cd11dc751be-0dd66c04c69e96-26011f51-3686400-197cd11dc76189d%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Flogin%3Fprevious%3D%252Ffutures%252FETH_USDT%253Ftype%253Dlinear_swap%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk3Y2QxMWRjNzUxYmUtMGRkNjZjMDRjNjllOTYtMjYwMTFmNTEtMzY4NjQwMC0xOTdjZDExZGM3NjE4OWQiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22197cd11dc751be-0dd66c04c69e96-26011f51-3686400-197cd11dc76189d%22%7D",
|
||||
"mxc_theme_main": "dark",
|
||||
"mexc_fingerprint_requestId": "1751492199306.WMvKJd",
|
||||
"_ym_visorc": "b",
|
||||
"mexc_clearance_modal_show_date": "2025-07-03-undefined",
|
||||
"ak_bmsc": "35C21AA65F819E0BF9BEBDD10DCF7B70~000000000000000000000000000000~YAAQJKVf1BK2K7CXAQAAPAISzRwQdUOUs1H3HPAdl4COMFQAl+aEPzppLbdgrwA7wXbP/LZpxsYCFflUHDppYKUjzXyTZ9tIojSF3/6CW3OCiPhQo/qhf6XPbC4oQHpCNWaC9GJWEs/CGesQdfeBbhkXdfh+JpgmgCF788+x8IveDE9+9qaL/3QZRy+E7zlKjjvmMxBpahRy+ktY9/KMrCY2etyvtm91KUclr4k8HjkhtNJOlthWgUyiANXJtfbNUMgt+Hqgqa7QzSUfAEpxIXQ1CuROoY9LbU292LRN5TbtBy/uNv6qORT38rKsnpi7TGmyFSB9pj3YsoSzIuAUxYXSh4hXRgAoUQm3Yh5WdLp4ONeyZC1LIb8VCY5xXRy/VbfaHH1w7FodY1HpfHGKSiGHSNwqoiUmMPx13Rgjsgki4mE7bwFmG2H5WAilRIOZA5OkndEqGrOuiNTON7l6+g6mH0MzZ+/+3AjnfF2sXxFuV9itcs9x",
|
||||
"mxc_theme_upcolor": "upgreen",
|
||||
"_vid_t": "mQUFl49q1yLZhrL4tvOtFF38e+hGW5QoMS+eXKVD9Q4vQau6icnyipsdyGLW/FBukiO2ItK7EtzPIPMFrE5SbIeLSm1NKc/j+ZmobhX063QAlskf1x1J",
|
||||
"_ym_isad": "2",
|
||||
"_ym_d": "1751492196",
|
||||
"_ym_uid": "1751492196843266888",
|
||||
"bm_mi": "02862693F007017AEFD6639269A60D08~YAAQJKVf1Am2K7CXAQAAIf4RzRzNGqZ7Q3BC0kAAp/0sCOhHxxvEWTb7mBl8p7LUz0W6RZbw5Etz03Tvqu3H6+sb+yu1o0duU+bDflt7WLVSOfG5cA3im8Jeo6wZhqmxTu6gGXuBgxhrHw/RGCgcknxuZQiRM9cbM6LlZIAYiugFm2xzmO/1QcpjDhs4S8d880rv6TkMedlkYGwdgccAmvbaRVSmX9d5Yukm+hY+5GWuyKMeOjpatAhcgjShjpSDwYSpyQE7vVZLBp7TECIjI9uoWzR8A87YHScKYEuE08tb8YtGdG3O6g70NzasSX0JF3XTCjrVZA==~1",
|
||||
"_ga": "GA1.1.626437359.1751492192",
|
||||
"NEXT_LOCALE": "en-GB",
|
||||
"x-mxc-fingerprint": "tv1xchuZQbx9N0aBztUG",
|
||||
"CLIENT_LANG": "en-GB",
|
||||
"sajssdk_2015_cross_new_user": "1"
|
||||
},
|
||||
"captcha_token_open": "geetest eyJsb3ROdW1iZXIiOiI4NWFhM2Q3YjJkYmE0Mjk3YTQwODY0YmFhODZiMzA5NyIsImNhcHRjaGFPdXRwdXQiOiJaVkwzS3FWaWxnbEZjQWdXOENIQVgxMUVBLVVPUnE1aURQSldzcmlubDFqelBhRTNiUGlEc0VrVTJUR0xuUzRHV2k0N2JDa1hyREMwSktPWmwxX1dERkQwNWdSN1NkbFJ1Z2NDY0JmTGdLVlNBTEI0OUNrR200enZZcnZ3MUlkdnQ5RThRZURYQ2E0empLczdZMHByS3JEWV9SQW93S0d4OXltS0MxMlY0SHRzNFNYMUV1YnI1ZV9yUXZCcTZJZTZsNFVJMS1DTnc5RUhBaXRXOGU2TVZ6OFFqaGlUMndRM1F3eGxEWkpmZnF6M3VucUl5RTZXUnFSUEx1T0RQQUZkVlB3S3AzcWJTQ3JXcG5CTUFKOXFuXzV2UDlXNm1pR3FaRHZvSTY2cWRzcHlDWUMyWTV1RzJ0ZjZfRHRJaXhTTnhLWUU3cTlfcU1WR2ZJUzlHUXh6ZWg2Mkp2eG02SHZLdjFmXzJMa3FlcVkwRk94S2RxaVpyN2NkNjAxMHE5UlFJVDZLdmNZdU1Hcm04M2d4SnY1bXp4VkZCZWZFWXZfRjZGWGpnWXRMMmhWSDlQME42bHFXQkpCTUVicE1nRm0zbm1iZVBkaDYxeW12T0FUb2wyNlQ0Z2ZET2dFTVFhZTkxQlFNR2FVSFRSa2c3RGJIX2xMYXlBTHQ0TTdyYnpHSCIsInBhc3NUb2tlbiI6IjA0NmFkMGQ5ZjNiZGFmYzJhNDgwYzFiMjcyMmIzZDUzOTk5NTRmYWVlNTM1MTI1ZTQ1MjkzNzJjYWZjOGI5N2EiLCJnZW5UaW1lIjoiMTc1MTQ5ODY4NCJ9",
|
||||
"captcha_token_close": "geetest eyJsb3ROdW1iZXIiOiI5ZWVlMDQ2YTg1MmQ0MTU3YTNiYjdhM2M5MzJiNzJiYSIsImNhcHRjaGFPdXRwdXQiOiJaVkwzS3FWaWxnbEZjQWdXOENIQVgxMUVBLVVPUnE1aURQSldzcmlubDFqelBhRTNiUGlEc0VrVTJUR0xuUzRHZk9hVUhKRW1ZOS1FN0h3Q3NNV3hvbVZsNnIwZXRYZzIyWHBGdUVUdDdNS19Ud1J6NnotX2pCXzRkVDJqTnJRN0J3cExjQ25DNGZQUXQ5V040TWxrZ0NMU3p6MERNd09SeHJCZVRkVE5pSU5BdmdFRDZOMkU4a19XRmJ6SFZsYUtieElnM3dLSGVTMG9URU5DLUNaNElnMDJlS2x3UWFZY3liRnhKU2ZrWG1vekZNMDVJSHVDYUpwT0d2WXhhYS1YTWlDeGE0TnZlcVFqN2JwNk04Q09PSnNxNFlfa0pkX0Ruc2w0UW1memZCUTZseF9tenFCMnFweThxd3hKTFVYX0g3TGUyMXZ2bGtubG1KS0RSUEJtTWpUcGFiZ2F4M3Q1YzJmbHJhRjk2elhHQzVBdVVQY1FrbDIyOW0xSmlnMV83cXNfTjdpZFozd0hRcWZFZGxSYVRKQTR2U18yYnFlcGdkLblJ3Y3oxaWtOOW1RaWNOSnpSNFNhdm1Pdi1BSzhwSEF0V2lkVjhrTkVYc3dGbUdSazFKQXBEX1hVUjlEdl9sNWJJNEFnbVJhcVlGdjhfRUNvN1g2cmt2UGZuOElTcCIsInBhc3NUb2tlbiI6IjRmZDFhZmU5NzI3MTk0ZGI3MDNlMDg2NWQ0ZDZjZTIyYWzMwMzUyNzQ5NzVjMDIwNDFiNTY3Y2Y3MDdhYjM1OTMiLCJnZW5UaW1lIjoiMTc1MTQ5ODY5MiJ9"
|
||||
}
|
||||
}
|
@ -19,9 +19,22 @@ from typing import Dict, List, Optional, Any
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
from urllib.parse import urlencode
|
||||
import glob
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class MEXCSessionManager:
|
||||
def __init__(self):
|
||||
self.captcha_token = None
|
||||
|
||||
def get_captcha_token(self) -> str:
|
||||
return self.captcha_token if self.captcha_token else ""
|
||||
|
||||
def save_captcha_token(self, token: str):
|
||||
self.captcha_token = token
|
||||
logger.info("MEXC: Captcha token saved in session manager")
|
||||
|
||||
class MEXCFuturesWebClient:
|
||||
"""
|
||||
MEXC Futures Web Client that mimics browser behavior for futures trading.
|
||||
@ -30,30 +43,27 @@ class MEXCFuturesWebClient:
|
||||
the exact HTTP requests made by their web interface.
|
||||
"""
|
||||
|
||||
def __init__(self, session_cookies: Dict[str, str] = None):
|
||||
def __init__(self, api_key: str, api_secret: str, user_id: str, base_url: str = 'https://www.mexc.com', headless: bool = True):
|
||||
"""
|
||||
Initialize the MEXC Futures Web Client
|
||||
|
||||
Args:
|
||||
session_cookies: Dictionary of cookies from an authenticated browser session
|
||||
api_key: API key for authentication
|
||||
api_secret: API secret for authentication
|
||||
user_id: User ID for authentication
|
||||
base_url: Base URL for the MEXC website
|
||||
headless: Whether to run the browser in headless mode
|
||||
"""
|
||||
self.session = requests.Session()
|
||||
|
||||
# Base URLs for different endpoints
|
||||
self.base_url = "https://www.mexc.com"
|
||||
self.futures_api_url = "https://futures.mexc.com/api/v1"
|
||||
self.captcha_url = f"{self.base_url}/ucgateway/captcha_api/captcha/robot"
|
||||
|
||||
# Session state
|
||||
self.api_key = api_key
|
||||
self.api_secret = api_secret
|
||||
self.user_id = user_id
|
||||
self.base_url = base_url
|
||||
self.is_authenticated = False
|
||||
self.user_id = None
|
||||
self.auth_token = None
|
||||
self.fingerprint = None
|
||||
self.visitor_id = None
|
||||
|
||||
# Load session cookies if provided
|
||||
if session_cookies:
|
||||
self.load_session_cookies(session_cookies)
|
||||
self.headless = headless
|
||||
self.session = requests.Session()
|
||||
self.session_manager = MEXCSessionManager() # Adding session_manager attribute
|
||||
self.captcha_url = f'{base_url}/ucgateway/captcha_api'
|
||||
self.futures_api_url = "https://futures.mexc.com/api/v1"
|
||||
|
||||
# Setup default headers that mimic a real browser
|
||||
self.setup_browser_headers()
|
||||
@ -72,7 +82,12 @@ class MEXCFuturesWebClient:
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-origin',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Pragma': 'no-cache'
|
||||
'Pragma': 'no-cache',
|
||||
'Referer': f'{self.base_url}/en-GB/futures/ETH_USDT?type=linear_swap',
|
||||
'Language': 'English',
|
||||
'X-Language': 'en-GB',
|
||||
'trochilus-trace-id': f"{uuid.uuid4()}-{int(time.time() * 1000) % 10000:04d}",
|
||||
'trochilus-uid': str(self.user_id) if self.user_id is not None else ''
|
||||
})
|
||||
|
||||
def load_session_cookies(self, cookies: Dict[str, str]):
|
||||
@ -137,37 +152,73 @@ class MEXCFuturesWebClient:
|
||||
endpoint = f"robot.future.{side}.{symbol}.{leverage}"
|
||||
url = f"{self.captcha_url}/{endpoint}"
|
||||
|
||||
# Setup headers for captcha request
|
||||
# Attempt to get captcha token from session manager
|
||||
captcha_token = self.session_manager.get_captcha_token()
|
||||
if not captcha_token:
|
||||
logger.warning("MEXC: No captcha token available, attempting to fetch from browser")
|
||||
captcha_token = self._extract_captcha_token_from_browser()
|
||||
if captcha_token:
|
||||
self.session_manager.save_captcha_token(captcha_token)
|
||||
else:
|
||||
logger.error("MEXC: Failed to extract captcha token from browser")
|
||||
return False
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Language': 'en-GB',
|
||||
'Referer': f'{self.base_url}/en-GB/futures/{symbol}?type=linear_swap',
|
||||
'trochilus-uid': self.user_id,
|
||||
'trochilus-trace-id': f"{uuid.uuid4()}-{int(time.time() * 1000) % 10000:04d}"
|
||||
'trochilus-uid': self.user_id if self.user_id else '',
|
||||
'trochilus-trace-id': f"{uuid.uuid4()}-{int(time.time() * 1000) % 10000:04d}",
|
||||
'captcha-token': captcha_token
|
||||
}
|
||||
|
||||
# Add captcha token if available (this would need to be extracted from browser)
|
||||
# For now, we'll make the request without it and see what happens
|
||||
|
||||
logger.info(f"MEXC: Verifying captcha for {endpoint}")
|
||||
try:
|
||||
response = self.session.get(url, headers=headers, timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get('success') and data.get('code') == 0:
|
||||
logger.info(f"MEXC: Captcha verification successful for {side} {symbol}")
|
||||
if data.get('success'):
|
||||
logger.info(f"MEXC: Captcha verified successfully for {endpoint}")
|
||||
return True
|
||||
else:
|
||||
logger.warning(f"MEXC: Captcha verification failed: {data}")
|
||||
logger.error(f"MEXC: Captcha verification failed for {endpoint}: {data}")
|
||||
return False
|
||||
else:
|
||||
logger.error(f"MEXC: Captcha request failed with status {response.status_code}")
|
||||
logger.error(f"MEXC: Captcha verification request failed with status {response.status_code}: {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"MEXC: Captcha verification error: {e}")
|
||||
logger.error(f"MEXC: Captcha verification error for {endpoint}: {str(e)}")
|
||||
return False
|
||||
|
||||
def _extract_captcha_token_from_browser(self) -> str:
|
||||
"""
|
||||
Extract captcha token from browser session using stored cookies or requests.
|
||||
This method looks for the most recent mexc_captcha_tokens JSON file to retrieve a token.
|
||||
"""
|
||||
try:
|
||||
# Look for the most recent mexc_captcha_tokens file
|
||||
captcha_files = glob.glob("mexc_captcha_tokens_*.json")
|
||||
if not captcha_files:
|
||||
logger.error("MEXC: No CAPTCHA token files found")
|
||||
return ""
|
||||
|
||||
# Sort files by timestamp (most recent first)
|
||||
latest_file = max(captcha_files, key=os.path.getctime)
|
||||
logger.info(f"MEXC: Using CAPTCHA token file {latest_file}")
|
||||
|
||||
with open(latest_file, 'r') as f:
|
||||
captcha_data = json.load(f)
|
||||
|
||||
if captcha_data and isinstance(captcha_data, list) and len(captcha_data) > 0:
|
||||
# Return the most recent token
|
||||
return captcha_data[0].get('token', '')
|
||||
else:
|
||||
logger.error("MEXC: No valid CAPTCHA tokens found in file")
|
||||
return ""
|
||||
except Exception as e:
|
||||
logger.error(f"MEXC: Error extracting captcha token from browser data: {str(e)}")
|
||||
return ""
|
||||
|
||||
def generate_signature(self, method: str, path: str, params: Dict[str, Any],
|
||||
timestamp: int, nonce: int) -> str:
|
||||
"""
|
||||
|
346
core/mexc_webclient/test_mexc_futures_webclient.py
Normal file
346
core/mexc_webclient/test_mexc_futures_webclient.py
Normal file
@ -0,0 +1,346 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test MEXC Futures Web Client
|
||||
|
||||
This script demonstrates how to use the MEXC Futures Web Client
|
||||
for futures trading that isn't supported by their official API.
|
||||
|
||||
IMPORTANT: This requires extracting cookies from your browser session.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import uuid
|
||||
|
||||
# Add the project root to path
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from mexc_futures_client import MEXCFuturesWebClient
|
||||
from session_manager import MEXCSessionManager
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Constants
|
||||
SYMBOL = "ETH_USDT"
|
||||
LEVERAGE = 300
|
||||
CREDENTIALS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'mexc_credentials.json')
|
||||
|
||||
# Read credentials from mexc_credentials.json in JSON format
|
||||
def load_credentials():
|
||||
credentials_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'mexc_credentials.json')
|
||||
cookies = {}
|
||||
captcha_token_open = ''
|
||||
captcha_token_close = ''
|
||||
try:
|
||||
with open(credentials_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
cookies = data.get('credentials', {}).get('cookies', {})
|
||||
captcha_token_open = data.get('credentials', {}).get('captcha_token_open', '')
|
||||
captcha_token_close = data.get('credentials', {}).get('captcha_token_close', '')
|
||||
logger.info(f"Loaded credentials from {credentials_file}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading credentials: {e}")
|
||||
return cookies, captcha_token_open, captcha_token_close
|
||||
|
||||
def test_basic_connection():
|
||||
"""Test basic connection and authentication"""
|
||||
logger.info("Testing MEXC Futures Web Client")
|
||||
|
||||
# Initialize session manager
|
||||
session_manager = MEXCSessionManager()
|
||||
|
||||
# Try to load saved session first
|
||||
cookies = session_manager.load_session()
|
||||
|
||||
if not cookies:
|
||||
# Explicitly load the cookies from the file we have
|
||||
cookies_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'mexc_cookies_20250703_003625.json')
|
||||
if os.path.exists(cookies_file):
|
||||
try:
|
||||
with open(cookies_file, 'r') as f:
|
||||
cookies = json.load(f)
|
||||
logger.info(f"Loaded cookies from {cookies_file}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load cookies from {cookies_file}: {e}")
|
||||
cookies = None
|
||||
else:
|
||||
logger.error(f"Cookies file not found at {cookies_file}")
|
||||
cookies = None
|
||||
|
||||
if not cookies:
|
||||
print("\nNo saved session found. You need to extract cookies from your browser.")
|
||||
session_manager.print_cookie_extraction_guide()
|
||||
|
||||
print("\nPaste your cookie header or cURL command (or press Enter to exit):")
|
||||
user_input = input().strip()
|
||||
|
||||
if not user_input:
|
||||
print("No input provided. Exiting.")
|
||||
return False
|
||||
|
||||
# Extract cookies from user input
|
||||
if user_input.startswith('curl'):
|
||||
cookies = session_manager.extract_from_curl_command(user_input)
|
||||
else:
|
||||
cookies = session_manager.extract_cookies_from_network_tab(user_input)
|
||||
|
||||
if not cookies:
|
||||
logger.error("Failed to extract cookies from input")
|
||||
return False
|
||||
|
||||
# Validate and save session
|
||||
if session_manager.validate_session_cookies(cookies):
|
||||
session_manager.save_session(cookies)
|
||||
logger.info("Session saved for future use")
|
||||
else:
|
||||
logger.warning("Extracted cookies may be incomplete")
|
||||
|
||||
# Initialize the web client
|
||||
client = MEXCFuturesWebClient(api_key='', api_secret='', user_id='', base_url='https://www.mexc.com', headless=True)
|
||||
# Load cookies into the client's session
|
||||
for name, value in cookies.items():
|
||||
client.session.cookies.set(name, value)
|
||||
|
||||
# Update headers to include additional parameters from captured requests
|
||||
client.session.headers.update({
|
||||
'trochilus-trace-id': f"{uuid.uuid4()}-{int(time.time() * 1000) % 10000:04d}",
|
||||
'trochilus-uid': cookies.get('u_id', ''),
|
||||
'Referer': 'https://www.mexc.com/en-GB/futures/ETH_USDT?type=linear_swap',
|
||||
'Language': 'English',
|
||||
'X-Language': 'en-GB'
|
||||
})
|
||||
|
||||
if not client.is_authenticated:
|
||||
logger.error("Failed to authenticate with extracted cookies")
|
||||
return False
|
||||
|
||||
logger.info("Successfully authenticated with MEXC")
|
||||
logger.info(f"User ID: {client.user_id}")
|
||||
logger.info(f"Auth Token: {client.auth_token[:20]}..." if client.auth_token else "No auth token")
|
||||
|
||||
return True
|
||||
|
||||
def test_captcha_verification(client: MEXCFuturesWebClient):
|
||||
"""Test captcha verification system"""
|
||||
logger.info("Testing captcha verification...")
|
||||
|
||||
# Test captcha for ETH_USDT long position with 200x leverage
|
||||
success = client.verify_captcha('ETH_USDT', 'openlong', '200X')
|
||||
|
||||
if success:
|
||||
logger.info("Captcha verification successful")
|
||||
else:
|
||||
logger.warning("Captcha verification failed - this may be normal if no position is being opened")
|
||||
|
||||
return success
|
||||
|
||||
def test_position_opening(client: MEXCFuturesWebClient, dry_run: bool = True):
|
||||
"""Test opening a position (dry run by default)"""
|
||||
if dry_run:
|
||||
logger.info("DRY RUN: Testing position opening (no actual trade)")
|
||||
else:
|
||||
logger.warning("LIVE TRADING: Opening actual position!")
|
||||
|
||||
symbol = 'ETH_USDT'
|
||||
volume = 1 # Small test position
|
||||
leverage = 200
|
||||
|
||||
logger.info(f"Attempting to open long position: {symbol}, Volume: {volume}, Leverage: {leverage}x")
|
||||
|
||||
if not dry_run:
|
||||
result = client.open_long_position(symbol, volume, leverage)
|
||||
|
||||
if result['success']:
|
||||
logger.info(f"Position opened successfully!")
|
||||
logger.info(f"Order ID: {result['order_id']}")
|
||||
logger.info(f"Timestamp: {result['timestamp']}")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"Failed to open position: {result['error']}")
|
||||
return False
|
||||
else:
|
||||
logger.info("DRY RUN: Would attempt to open position here")
|
||||
# Test just the captcha verification part
|
||||
return client.verify_captcha(symbol, 'openlong', f'{leverage}X')
|
||||
|
||||
def test_position_opening_live(client):
|
||||
symbol = "ETH_USDT"
|
||||
volume = 1 # Small volume for testing
|
||||
leverage = 200
|
||||
|
||||
logger.info(f"LIVE TRADING: Opening actual position!")
|
||||
logger.info(f"Attempting to open long position: {symbol}, Volume: {volume}, Leverage: {leverage}x")
|
||||
|
||||
result = client.open_long_position(symbol, volume, leverage)
|
||||
if result.get('success'):
|
||||
logger.info(f"Successfully opened position: {result}")
|
||||
else:
|
||||
logger.error(f"Failed to open position: {result.get('error', 'Unknown error')}")
|
||||
|
||||
def interactive_menu(client: MEXCFuturesWebClient):
|
||||
"""Interactive menu for testing different functions"""
|
||||
while True:
|
||||
print("\n" + "="*50)
|
||||
print("MEXC Futures Web Client Test Menu")
|
||||
print("="*50)
|
||||
print("1. Test captcha verification")
|
||||
print("2. Test position opening (DRY RUN)")
|
||||
print("3. Test position opening (LIVE - BE CAREFUL!)")
|
||||
print("4. Test position closing (DRY RUN)")
|
||||
print("5. Show session info")
|
||||
print("6. Refresh session")
|
||||
print("0. Exit")
|
||||
|
||||
choice = input("\nEnter choice (0-6): ").strip()
|
||||
|
||||
if choice == "1":
|
||||
test_captcha_verification(client)
|
||||
|
||||
elif choice == "2":
|
||||
test_position_opening(client, dry_run=True)
|
||||
|
||||
elif choice == "3":
|
||||
test_position_opening_live(client)
|
||||
|
||||
elif choice == "4":
|
||||
logger.info("DRY RUN: Position closing test")
|
||||
success = client.verify_captcha('ETH_USDT', 'closelong', '200X')
|
||||
if success:
|
||||
logger.info("DRY RUN: Would close position here")
|
||||
else:
|
||||
logger.warning("Captcha verification failed for position closing")
|
||||
|
||||
elif choice == "5":
|
||||
print(f"\nSession Information:")
|
||||
print(f"Authenticated: {client.is_authenticated}")
|
||||
print(f"User ID: {client.user_id}")
|
||||
print(f"Auth Token: {client.auth_token[:20]}..." if client.auth_token else "None")
|
||||
print(f"Fingerprint: {client.fingerprint}")
|
||||
print(f"Visitor ID: {client.visitor_id}")
|
||||
|
||||
elif choice == "6":
|
||||
session_manager = MEXCSessionManager()
|
||||
session_manager.print_cookie_extraction_guide()
|
||||
|
||||
elif choice == "0":
|
||||
print("Goodbye!")
|
||||
break
|
||||
|
||||
else:
|
||||
print("Invalid choice. Please try again.")
|
||||
|
||||
def main():
|
||||
"""Main test function"""
|
||||
print("MEXC Futures Web Client Test")
|
||||
print("WARNING: This is experimental software for futures trading")
|
||||
print("Use at your own risk and test with small amounts first!")
|
||||
|
||||
# Load cookies and tokens
|
||||
cookies, captcha_token_open, captcha_token_close = load_credentials()
|
||||
if not cookies:
|
||||
logger.error("Failed to load cookies from credentials file")
|
||||
sys.exit(1)
|
||||
|
||||
# Initialize client with loaded cookies and tokens
|
||||
client = MEXCFuturesWebClient(api_key='', api_secret='', user_id='')
|
||||
# Load cookies into the client's session
|
||||
for name, value in cookies.items():
|
||||
client.session.cookies.set(name, value)
|
||||
# Set captcha tokens
|
||||
client.captcha_token_open = captcha_token_open
|
||||
client.captcha_token_close = captcha_token_close
|
||||
|
||||
# Try to load credentials from the new JSON file
|
||||
try:
|
||||
with open(CREDENTIALS_FILE, 'r') as f:
|
||||
credentials_data = json.load(f)
|
||||
cookies = credentials_data['credentials']['cookies']
|
||||
captcha_token_open = credentials_data['credentials']['captcha_token_open']
|
||||
captcha_token_close = credentials_data['credentials']['captcha_token_close']
|
||||
client.load_session_cookies(cookies)
|
||||
client.session_manager.save_captcha_token(captcha_token_open) # Assuming this is for opening
|
||||
|
||||
except FileNotFoundError:
|
||||
logger.error(f"Credentials file not found at {CREDENTIALS_FILE}")
|
||||
return False
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Error loading credentials: {e}")
|
||||
return False
|
||||
except KeyError as e:
|
||||
logger.error(f"Missing key in credentials file: {e}")
|
||||
return False
|
||||
|
||||
if not client.is_authenticated:
|
||||
logger.error("Client not authenticated. Please ensure valid cookies and tokens are in mexc_credentials.json")
|
||||
return False
|
||||
|
||||
# Test connection and authentication
|
||||
logger.info("Successfully authenticated with MEXC")
|
||||
|
||||
# Set leverage
|
||||
leverage_response = client.update_leverage(symbol=SYMBOL, leverage=LEVERAGE)
|
||||
if leverage_response and leverage_response.get('code') == 200:
|
||||
logger.info(f"Leverage set to {LEVERAGE}x for {SYMBOL}")
|
||||
else:
|
||||
logger.error(f"Failed to set leverage: {leverage_response}")
|
||||
sys.exit(1)
|
||||
|
||||
# Get current price
|
||||
ticker = client.get_ticker_data(symbol=SYMBOL)
|
||||
if ticker and ticker.get('code') == 200:
|
||||
current_price = float(ticker['data']['last'])
|
||||
logger.info(f"Current {SYMBOL} price: {current_price}")
|
||||
else:
|
||||
logger.error(f"Failed to get ticker data: {ticker}")
|
||||
sys.exit(1)
|
||||
|
||||
# Calculate order size for a small test trade (e.g., $10 worth)
|
||||
trade_usdt = 10.0
|
||||
order_qty = round((trade_usdt / current_price) * LEVERAGE, 3)
|
||||
logger.info(f"Calculated order quantity: {order_qty} {SYMBOL} for ~${trade_usdt} at {LEVERAGE}x")
|
||||
|
||||
# Test 1: Open LONG position
|
||||
logger.info(f"Opening LONG position for {SYMBOL} at {current_price} with qty {order_qty}")
|
||||
open_long_order = client.create_order(
|
||||
symbol=SYMBOL,
|
||||
side=1, # 1 for BUY
|
||||
position_side=1, # 1 for LONG
|
||||
order_type=1, # 1 for LIMIT
|
||||
price=current_price,
|
||||
vol=order_qty
|
||||
)
|
||||
if open_long_order and open_long_order.get('code') == 200:
|
||||
logger.info(f"✅ Successfully opened LONG position: {open_long_order['data']}")
|
||||
else:
|
||||
logger.error(f"❌ Failed to open LONG position: {open_long_order}")
|
||||
sys.exit(1)
|
||||
|
||||
# Test 2: Close LONG position
|
||||
logger.info(f"Closing LONG position for {SYMBOL}")
|
||||
close_long_order = client.create_order(
|
||||
symbol=SYMBOL,
|
||||
side=2, # 2 for SELL
|
||||
position_side=1, # 1 for LONG
|
||||
order_type=1, # 1 for LIMIT
|
||||
price=current_price,
|
||||
vol=order_qty,
|
||||
reduce_only=True
|
||||
)
|
||||
if close_long_order and close_long_order.get('code') == 200:
|
||||
logger.info(f"✅ Successfully closed LONG position: {close_long_order['data']}")
|
||||
else:
|
||||
logger.error(f"❌ Failed to close LONG position: {close_long_order}")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info("All tests completed successfully!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -3,6 +3,9 @@ Trading Executor for MEXC API Integration
|
||||
|
||||
This module handles the execution of trading signals through the MEXC exchange API.
|
||||
It includes position management, risk controls, and safety features.
|
||||
|
||||
https://github.com/mexcdevelop/mexc-api-postman/blob/main/MEXC%20V3.postman_collection.json
|
||||
MEXC V3.postman_collection.json
|
||||
"""
|
||||
|
||||
import logging
|
||||
@ -89,7 +92,8 @@ class TradingExecutor:
|
||||
self.exchange = MEXCInterface(
|
||||
api_key=api_key,
|
||||
api_secret=api_secret,
|
||||
test_mode=exchange_test_mode
|
||||
test_mode=exchange_test_mode,
|
||||
trading_mode=trading_mode
|
||||
)
|
||||
|
||||
# Trading state
|
||||
@ -101,6 +105,8 @@ class TradingExecutor:
|
||||
self.trading_enabled = self.mexc_config.get('enabled', False)
|
||||
self.trading_mode = trading_mode
|
||||
|
||||
logger.debug(f"TRADING EXECUTOR: Initial trading_enabled state from config: {self.trading_enabled}")
|
||||
|
||||
# Legacy compatibility (deprecated)
|
||||
self.dry_run = self.simulation_mode
|
||||
|
||||
@ -109,7 +115,12 @@ class TradingExecutor:
|
||||
|
||||
# Connect to exchange
|
||||
if self.trading_enabled:
|
||||
self._connect_exchange()
|
||||
logger.info("TRADING EXECUTOR: Attempting to connect to exchange...")
|
||||
if not self._connect_exchange():
|
||||
logger.error("TRADING EXECUTOR: Failed initial exchange connection. Trading will be disabled.")
|
||||
self.trading_enabled = False
|
||||
else:
|
||||
logger.info("TRADING EXECUTOR: Trading is explicitly disabled in config.")
|
||||
|
||||
logger.info(f"Trading Executor initialized - Mode: {self.trading_mode}, Enabled: {self.trading_enabled}")
|
||||
|
||||
@ -143,17 +154,20 @@ class TradingExecutor:
|
||||
def _connect_exchange(self) -> bool:
|
||||
"""Connect to the MEXC exchange"""
|
||||
try:
|
||||
logger.debug("TRADING EXECUTOR: Calling self.exchange.connect()...")
|
||||
connected = self.exchange.connect()
|
||||
logger.debug(f"TRADING EXECUTOR: self.exchange.connect() returned: {connected}")
|
||||
if connected:
|
||||
logger.info("Successfully connected to MEXC exchange")
|
||||
return True
|
||||
else:
|
||||
logger.error("Failed to connect to MEXC exchange")
|
||||
logger.error("Failed to connect to MEXC exchange: Connection returned False.")
|
||||
if not self.dry_run:
|
||||
logger.info("TRADING EXECUTOR: Setting trading_enabled to False due to connection failure.")
|
||||
self.trading_enabled = False
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Error connecting to MEXC exchange: {e}")
|
||||
logger.error(f"Error connecting to MEXC exchange: {e}. Setting trading_enabled to False.")
|
||||
self.trading_enabled = False
|
||||
return False
|
||||
|
||||
@ -170,8 +184,9 @@ class TradingExecutor:
|
||||
Returns:
|
||||
bool: True if trade executed successfully
|
||||
"""
|
||||
logger.debug(f"TRADING EXECUTOR: execute_signal called. trading_enabled: {self.trading_enabled}")
|
||||
if not self.trading_enabled:
|
||||
logger.info(f"Trading disabled - Signal: {action} {symbol} (confidence: {confidence:.2f})")
|
||||
logger.info(f"Trading disabled - Signal: {action} {symbol} (confidence: {confidence:.2f}) - Reason: Trading executor is not enabled.")
|
||||
return False
|
||||
|
||||
if action == 'HOLD':
|
||||
@ -181,23 +196,51 @@ class TradingExecutor:
|
||||
if not self._check_safety_conditions(symbol, action):
|
||||
return False
|
||||
|
||||
# Get current price if not provided
|
||||
# Get current price if not provided
|
||||
if current_price is None:
|
||||
ticker = self.exchange.get_ticker(symbol)
|
||||
if not ticker:
|
||||
logger.error(f"Failed to get current price for {symbol}")
|
||||
if not ticker or 'last' not in ticker:
|
||||
logger.error(f"Failed to get current price for {symbol} or ticker is malformed.")
|
||||
return False
|
||||
current_price = ticker['last']
|
||||
|
||||
# Assert that current_price is not None for type checking
|
||||
assert current_price is not None, "current_price should not be None at this point"
|
||||
|
||||
# --- Balance check before executing trade ---
|
||||
# Only perform balance check for BUY actions or SHORT (initial sell) actions
|
||||
if action == 'BUY' or (action == 'SELL' and symbol not in self.positions) or (action == 'SHORT'):
|
||||
# Determine the quote asset (e.g., USDT, USDC) from the symbol
|
||||
if '/' in symbol:
|
||||
quote_asset = symbol.split('/')[1].upper() # Assuming symbol is like ETH/USDT
|
||||
else:
|
||||
# Fallback for symbols like ETHUSDT (assuming last 4 chars are quote)
|
||||
quote_asset = symbol[-4:].upper()
|
||||
|
||||
# Calculate required capital for the trade
|
||||
# If we are selling (to open a short position), we need collateral based on the position size
|
||||
# For simplicity, assume required capital is the full position value in USD
|
||||
required_capital = self._calculate_position_size(confidence, current_price)
|
||||
|
||||
# Get available balance for the quote asset
|
||||
available_balance = self.exchange.get_balance(quote_asset)
|
||||
|
||||
logger.info(f"BALANCE CHECK: Symbol: {symbol}, Action: {action}, Required: ${required_capital:.2f} {quote_asset}, Available: ${available_balance:.2f} {quote_asset}")
|
||||
|
||||
if available_balance < required_capital:
|
||||
logger.warning(f"Trade blocked for {symbol} {action}: Insufficient {quote_asset} balance. "
|
||||
f"Required: ${required_capital:.2f}, Available: ${available_balance:.2f}")
|
||||
return False
|
||||
# --- End Balance check ---
|
||||
|
||||
with self.lock:
|
||||
try:
|
||||
if action == 'BUY':
|
||||
return self._execute_buy(symbol, confidence, current_price)
|
||||
elif action == 'SELL':
|
||||
return self._execute_sell(symbol, confidence, current_price)
|
||||
elif action == 'SHORT': # Explicitly handle SHORT if it's a direct signal
|
||||
return self._execute_short(symbol, confidence, current_price)
|
||||
else:
|
||||
logger.warning(f"Unknown action: {action}")
|
||||
return False
|
||||
|
45
docs/MEXC_CAPTCHA_HANDLING.md
Normal file
45
docs/MEXC_CAPTCHA_HANDLING.md
Normal file
@ -0,0 +1,45 @@
|
||||
# MEXC CAPTCHA Handling Documentation
|
||||
|
||||
## Overview
|
||||
This document outlines the mechanism implemented in the `gogo2` trading dashboard project to handle CAPTCHA challenges encountered during automated trading on the MEXC platform. The goal is to enable seamless trading operations without manual intervention by capturing and integrating CAPTCHA tokens.
|
||||
|
||||
## CAPTCHA Handling Mechanism
|
||||
|
||||
### 1. Browser Automation with `MEXCBrowserAutomation`
|
||||
- The `MEXCBrowserAutomation` class in `core/mexc_webclient/auto_browser.py` is responsible for launching a browser session using Selenium WebDriver.
|
||||
- It navigates to the MEXC futures trading page and captures HTTP requests and responses, including those related to CAPTCHA challenges.
|
||||
- When a CAPTCHA request is detected (e.g., requests to `gcaptcha4.geetest.com` or specific MEXC CAPTCHA endpoints), the relevant token is extracted from the request headers or response data.
|
||||
- These tokens are saved to JSON files named `mexc_captcha_tokens_YYYYMMDD_HHMMSS.json` in the project root directory for later use.
|
||||
|
||||
### 2. Integration with `MEXCFuturesWebClient`
|
||||
- The `MEXCFuturesWebClient` class in `core/mexc_webclient/mexc_futures_client.py` is updated to handle CAPTCHA challenges during API requests.
|
||||
- A `MEXCSessionManager` class manages session data, including cookies and CAPTCHA tokens, by reading the latest token from the saved JSON files.
|
||||
- When a request fails due to a CAPTCHA challenge, the client retrieves the latest token and includes it in the request headers under `captcha-token`.
|
||||
|
||||
### 3. Manual Testing and Data Capture
|
||||
- The script `run_mexc_browser.py` provides an interactive way to test the `MEXCFuturesWebClient` and capture CAPTCHA tokens.
|
||||
- Users can run this script to perform test trades, monitor requests, and save captured data, including tokens, to files.
|
||||
- The captured tokens are used in subsequent API calls to authenticate trading actions like opening or closing positions.
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
### Running Browser Automation
|
||||
1. Execute `python run_mexc_browser.py` to start the browser automation.
|
||||
2. Choose options like 'Perform test trade (manual)' to simulate trading actions and capture CAPTCHA tokens.
|
||||
3. The script saves tokens to a JSON file, which can be used by `MEXCFuturesWebClient` for automated trading.
|
||||
|
||||
### Automated Trading with CAPTCHA Tokens
|
||||
- Ensure that the `MEXCFuturesWebClient` is configured to use the latest CAPTCHA token file. This is handled automatically by the `MEXCSessionManager` class, which looks for the most recent file matching the pattern `mexc_captcha_tokens_*.json`.
|
||||
- If a CAPTCHA challenge is encountered during trading, the client will attempt to use the saved token to proceed with the request.
|
||||
|
||||
## Limitations and Notes
|
||||
- **Token Validity**: CAPTCHA tokens have a limited validity period. If the saved token is outdated, a new browser session may be required to capture fresh tokens.
|
||||
- **Automation**: Currently, token capture requires manual initiation via `run_mexc_browser.py`. Future enhancements may include background automation for continuous token updates.
|
||||
- **Windows Compatibility**: All scripts and file operations are designed to work on Windows systems, adhering to project rules for compatibility.
|
||||
|
||||
## Troubleshooting
|
||||
- If trades fail due to CAPTCHA issues, check if a recent token file exists and contains valid tokens.
|
||||
- Run `run_mexc_browser.py` to capture new tokens if necessary.
|
||||
- Verify that file paths and permissions are correct for reading/writing token files on Windows.
|
||||
|
||||
For further assistance or to report issues, refer to the project's main documentation or contact the development team.
|
12
mexc_captcha_tokens_20250703_022428.json
Normal file
12
mexc_captcha_tokens_20250703_022428.json
Normal file
@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"token": "geetest eyJsb3ROdW1iZXIiOiI4NWFhM2Q3YjJkYmE0Mjk3YTQwODY0YmFhODZiMzA5NyIsImNhcHRjaGFPdXRwdXQiOiJaVkwzS3FWaWxnbEZjQWdXOENIQVgxMUVBLVVPUnE1aURQSldzcmlubDFqelBhRTNiUGlEc0VrVTJUR0xuUzRHV2k0N2JDa1hyREMwSktPWmwxX1dERkQwNWdSN1NkbFJ1Z2NDY0JmTGdLVlNBTEI0OUNrR200enZZcnZ3MUlkdnQ5RThRZURYQ2E0empLczdZMHByS3JEWV9SQW93S0d4OXltS0MxMlY0SHRzNFNYMUV1YnI1ZV9yUXZCcTZJZTZsNFVJMS1DTnc5RUhBaXRXOGU2TVZ6OFFqaGlUMndRM1F3eGxEWkpmZnF6M3VucUl5RTZXUnFSUEx1T0RQQUZkVlB3S3AzcWJTQ3JXcG5CTUFKOXFuXzV2UDlXNm1pR3FaRHZvSTY2cWRzcHlDWUMyWTV1RzJ0ZjZfRHRJaXhTTnhLWUU3cTlfcU1WR2ZJUzlHUXh6ZWg2Mkp2eG02SHZLdjFmXzJMa3FlcVkwRk94S2RxaVpyN2NkNjAxMHE5UlFJVDZLdmNZdU1Hcm04M2d4SnY1bXp4VkZCZWZFWXZfRjZGWFpnWXRMMmhWSDlQME42bHFXQkpCTUVicE1nRm0zbm1iZVBkaDYxeW12T0FUb2wyNlQ0Z2ZET2dFTVFhZTkxQlFNR2FVSFRSa2c3RGJIX2xMYXlBTHQ0TTdyYnpHSCIsInBhc3NUb2tlbiI6IjA0NmFkMGQ5ZjNiZGFmYzJhNDgwYzFiMjcyMmIzZDUzOTk5NTRmYWVlNTM1MTI1ZTQ1MjkzNzJjYWZjOGI5N2EiLCJnZW5UaW1lIjoiMTc1MTQ5ODY4NCJ9",
|
||||
"url": "https://www.mexc.com/ucgateway/captcha_api/captcha/robot/robot.future.openlong.ETH_USDT.300X",
|
||||
"timestamp": "2025-07-03T02:24:51.150716"
|
||||
},
|
||||
{
|
||||
"token": "geetest eyJsb3ROdW1iZXIiOiI5ZWVlMDQ2YTg1MmQ0MTU3YTNiYjdhM2M5MzJiNzJiYSIsImNhcHRjaGFPdXRwdXQiOiJaVkwzS3FWaWxnbEZjQWdXOENIQVgxMUVBLVVPUnE1aURQSldzcmlubDFqelBhRTNiUGlEc0VrVTJUR0xuUzRHZk9hVUhKRW1ZOS1FN0h3Q3NNV3hvbVZsNnIwZXRYZzIyWHBGdUVUdDdNS19Ud1J6NnotX2pCXzRkVDJqTnJRN0J3cExjQ25DNGZQUXQ5V040TWxrZ0NMU3p6MERNd09SeHJCZVRkVE5pSU5BdmdFRDZOMkU4a19XRmJ6SFZsYUtieElnM3dLSGVTMG9URU5DLUNaNElnMDJlS2x3UWFZY3liRnhKU2ZrWG1vekZNMDVJSHVDYUpwT0d2WXhhYS1YTWlDeGE0TnZlcVFqN2JwNk04Q09PSnNxNFlfa0pkX0Ruc2w0UW1memZCUTZseF9tenFCMnFweThxd3hKTFVYX0g3TGUyMXZ2bGtubG1KS0RSUEJtTWpUcGFiZ2F4M3Q1YzJmbHJhRjk2elhHQzVBdVVQY1FrbDIyOW0xSmlnMV83cXNfTjdpZFozd0hRcWZFZGxSYVRKQTR2U18yYnFlcGdLblJ3Y3oxaWtOOW1RaWNOSnpSNFNhdm1Pdi1BSzhwSEF0V2lkVjhrTkVYc3dGbUdSazFKQXBEX1hVUjlEdl9sNWJJNEFnbVJhcVlGdjhfRUNvN1g2cmt2UGZuOElTcCIsInBhc3NUb2tlbiI6IjRmZDFhZmU5NzI3MTk0ZGI3MDNlMDg2NWQ0ZDZjZTIyYzMwMzUyNzQ5NzVjMDIwNDFiNTY3Y2Y3MDdhYjM1OTMiLCJnZW5UaW1lIjoiMTc1MTQ5ODY5MiJ9",
|
||||
"url": "https://www.mexc.com/ucgateway/captcha_api/captcha/robot/robot.future.closelong.ETH_USDT.300X",
|
||||
"timestamp": "2025-07-03T02:24:57.885947"
|
||||
}
|
||||
]
|
29
mexc_cookies_20250703_003625.json
Normal file
29
mexc_cookies_20250703_003625.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"bm_sv": "D92603BBC020E9C2CD11B2EBC8F22050~YAAQJKVf1NW5K7CXAQAAwtMVzRzHARcY60jrPVzy9G79fN3SY4z988SWHHxQlbPpyZHOj76c20AjCnS0QwveqzB08zcRoauoIe/sP3svlaIso9PIdWay0KIIVUe1XsiTJRfTm/DmS+QdrOuJb09rbfWLcEJF4/0QK7VY0UTzPTI2V3CMtxnmYjd1+tjfYsvt1R6O+Mw9mYjb7SjhRmiP/exY2UgZdLTJiqd+iWkc5Wejy5m6g5duOfRGtiA9mfs=~1",
|
||||
"bm_sz": "98D80FE4B23FE6352AE5194DA699FDDB~YAAQJKVf1GK4K7CXAQAAeQ0UzRw+aXiY5/Ujp+sZm0a4j+XAJFn6fKT4oph8YqIKF6uHSgXkFY3mBt8WWY98Y2w1QzOEFRkje8HTUYQgJsV59y5DIOTZKC6wutPD/bKdVi9ZKtk4CWbHIIRuCrnU1Nw2jqj5E0hsorhKGh8GeVsAeoao8FWovgdYD6u8Qpbr9aL5YZgVEIqJx6WmWLmcIg+wA8UFj8751Fl0B3/AGxY2pACUPjonPKNuX/UDYA5e98plOYUnYLyQMEGIapSrWKo1VXhKBDPLNedJ/Q2gOCGEGlj/u1Fs407QxxXwCvRSegL91y6modtL5JGoFucV1pYc4pgTwEAEdJfcLCEBaButTbaHI9T3SneqgCoGeatMMaqz0GHbvMD7fBQofARBqzN1L6aGlmmAISMzI3wx/SnsfXBl~3228228~3294529",
|
||||
"_abck": "0288E759712AF333A6EE15F66BC2A662~-1~YAAQJKVf1GC4K7CXAQAAeQ0UzQ77TfyX5SOWTgdW3DVqNFrTLz2fhLo2OC4I6ZHnW9qB0vwTjFDfOB65BwLSeFZoyVypVCGTtY/uL6f4zX0AxEGAU8tLg/jeO0acO4JpGrjYZSW1F56vEd9JbPU2HQPNERorgCDLQMSubMeLCfpqMp3VCW4w0Ssnk6Y4pBSs4mh0PH95v56XXDvat9k20/JPoK3Ip5kK2oKh5Vpk5rtNTVea66P0NBjVUw/EddRUuDDJpc8T4DtTLDXnD5SNDxEq8WDkrYd5kP4dNe0PtKcSOPYs2QLUbvAzfBuMvnhoSBaCjsqD15EZ3eDAoioli/LzsWSxaxetYfm0pA/s5HBXMdOEDi4V0E9b79N28rXcC8IJEHXtfdZdhJjwh1FW14lqF9iuOwER81wDEnIVtgwTwpd3ffrc35aNjb+kGiQ8W0FArFhUI/ZY2NDvPVngRjNrmRm0CsCm+6mdxxVNsGNMPKYG29mcGDi2P9HGDk45iOm0vzoaYUl1PlOh4VGq/V3QGbPYpkBsBtQUjrf/SQJe5IAbjCICTYlgxTo+/FAEjec+QdUsagTgV8YNycQfTK64A2bs1L1n+RO5tapLThU6NkxnUbqHOm6168RnT8ZRoAUpkJ5m3QpqSsuslnPRUPyxUr73v514jTBIUGsq4pUeRpXXd9FAh8Xkn4VZ9Bh3q4jP7eZ9Sv58mgnEVltNBFkeG3zsuIp5Hu69MSBU+8FD4gVlncbBinrTLNWRB8F00Gyvc03unrAznsTEyLiDq9guQf9tQNcGjxfggfnGq/Z1Gy/A7WMjiYw7pwGRVzAYnRgtcZoww9gQ/FdGkbp2Xl+oVZpaqFsHVvafWyOFr4pqQsmd353ddgKLjsEnpy/jcdUsIR/Ph3pYv++XlypXehXj0/GHL+WsosujJrYk4TuEsPKUcyHNr+r844mYUIhCYsI6XVKrq3fimdfdhmlkW8J1kZSTmFwP8QcwGlTK/mZDTJPyf8K5ugXcqOU8oIQzt5B2zfRwRYKHdhb8IUw=~-1~-1~-1",
|
||||
"RT": "\"z=1&dm=www.mexc.com&si=f5d53b58-7845-4db4-99f1-444e43d35199&ss=mcmh857q&sl=3&tt=90n&bcn=%2F%2F684dd311.akstat.io%2F&ld=1c9o\"",
|
||||
"mexc_fingerprint_visitorId": "tv1xchuZQbx9N0aBztUG",
|
||||
"_ga_L6XJCQTK75": "GS2.1.s1751492192$o1$g1$t1751492248$j4$l0$h0",
|
||||
"uc_token": "WEB66f893ede865e5d927efdea4a82e655ad5190239c247997d744ef9cd075f6f1e",
|
||||
"u_id": "WEB66f893ede865e5d927efdea4a82e655ad5190239c247997d744ef9cd075f6f1e",
|
||||
"_fbp": "fb.1.1751492193579.314807866777158389",
|
||||
"mxc_exchange_layout": "BA",
|
||||
"sensorsdata2015jssdkcross": "%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22197cd11dc751be-0dd66c04c69e96-26011f51-3686400-197cd11dc76189d%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Flogin%3Fprevious%3D%252Ffutures%252FETH_USDT%253Ftype%253Dlinear_swap%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk3Y2QxMWRjNzUxYmUtMGRkNjZjMDRjNjllOTYtMjYwMTFmNTEtMzY4NjQwMC0xOTdjZDExZGM3NjE4OWQiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIyMWE4NzI4OTkwYjg0ZjRmYTNhZTY0YzgwMDRiNGFhYSJ9%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22197cd11dc751be-0dd66c04c69e96-26011f51-3686400-197cd11dc76189d%22%7D",
|
||||
"mxc_theme_main": "dark",
|
||||
"mexc_fingerprint_requestId": "1751492199306.WMvKJd",
|
||||
"_ym_visorc": "b",
|
||||
"mexc_clearance_modal_show_date": "2025-07-03-undefined",
|
||||
"ak_bmsc": "35C21AA65F819E0BF9BEBDD10DCF7B70~000000000000000000000000000000~YAAQJKVf1BK2K7CXAQAAPAISzRwQdUOUs1H3HPAdl4COMFQAl+aEPzppLbdgrwA7wXbP/LZpxsYCFflUHDppYKUjzXyTZ9tIojSF3/6CW3OCiPhQo/qhf6XPbC4oQHpCNWaC9GJWEs/CGesQdfeBbhkXdfh+JpgmgCF788+x8IveDE9+9qaL/3QZRy+E7zlKjjvmMxBpahRy+ktY9/KMrCY2etyvtm91KUclr4k8HjkhtNJOlthWgUyiANXJtfbNUMgt+Hqgqa7QzSUfAEpxIXQ1CuROoY9LbU292LRN5TbtBy/uNv6qORT38rKsnpi7TGmyFSB9pj3YsoSzIuAUxYXSh4hXRgAoUQm3Yh5WdLp4ONeyZC1LIb8VCY5xXRy/VbfaHH1w7FodY1HpfHGKSiGHSNwqoiUmMPx13Rgjsgki4mE7bwFmG2H5WAilRIOZA5OkndEqGrOuiNTON7l6+g6mH0MzZ+/+3AjnfF2sXxFuV9itcs9x",
|
||||
"mxc_theme_upcolor": "upgreen",
|
||||
"_vid_t": "mQUFl49q1yLZhrL4tvOtFF38e+hGW5QoMS+eXKVD9Q4vQau6icnyipsdyGLW/FBukiO2ItK7EtzPIPMFrE5SbIeLSm1NKc/j+ZmobhX063QAlskf1x1J",
|
||||
"_ym_isad": "2",
|
||||
"_ym_d": "1751492196",
|
||||
"_ym_uid": "1751492196843266888",
|
||||
"bm_mi": "02862693F007017AEFD6639269A60D08~YAAQJKVf1Am2K7CXAQAAIf4RzRzNGqZ7Q3BC0kAAp/0sCOhHxxvEWTb7mBl8p7LUz0W6RZbw5Etz03Tvqu3H6+sb+yu1o0duU+bDflt7WLVSOfG5cA3im8Jeo6wZhqmxTu6gGXuBgxhrHw/RGCgcknxuZQiRM9cbM6LlZIAYiugFm2xzmO/1QcpjDhs4S8d880rv6TkMedlkYGwdgccAmvbaRVSmX9d5Yukm+hY+5GWuyKMeOjpatAhcgjShjpSDwYSpyQE7vVZLBp7TECIjI9uoWzR8A87YHScKYEuE08tb8YtGdG3O6g70NzasSX0JF3XTCjrVZA==~1",
|
||||
"_ga": "GA1.1.626437359.1751492192",
|
||||
"NEXT_LOCALE": "en-GB",
|
||||
"x-mxc-fingerprint": "tv1xchuZQbx9N0aBztUG",
|
||||
"CLIENT_LANG": "en-GB",
|
||||
"sajssdk_2015_cross_new_user": "1"
|
||||
}
|
28
mexc_cookies_20250703_010352.json
Normal file
28
mexc_cookies_20250703_010352.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"bm_sv": "5C10B638DC36B596422995FAFA8535C5~YAAQJKVf1MfUK7CXAQAA8NktzRwthLouCzg1Sqsm2yBQhAdvw8KbTCYRe0bzUrYEsQEahTebrBcYQoRF3+HyIAggj7MIsbFBANUqLcKJ66lD3QbuA3iU3MhUts/ZhA2dLaSoH5IbgdwiAd98s4bjsb3MSaNwI3nCEzWkLH2CZDyGJK6mhwHlA5VU6OXRLTVz+dfeh2n2fD0SbtcppFL2j9jqopWyKLaxQxYAg+Rs5g3xAo2BTa6/zmQ2YoxZR/w=~1",
|
||||
"bm_sz": "11FB853E475F9672ADEDFBC783F7487B~YAAQJKVf1G7UK7CXAQAAcY8tzRy3rXBghQVq4e094ZpjhvYRjSatbOxmR/iHhc0aV6NMJkhTwCOnCDsKjeU6sgcdpYgxkpgfhbvTgm5dQ7fEQ5cgmJtfNPmEisDQxZQIOXlI4yhgq7cks4jek9T9pxBx+iLtsZYy5LqIl7mqXc7R7MxMaWvDBfSVU1T0hY9DD0U3P4fxstSIVbGdRzcX2mvGNMcdTj3JMB1y9mXzKB44Prglw0zWa7BZT4imuh5OTQTY4OLNQM7gg5ERUHI7RTcxz+CAltGtBeMHTmWa+Jat/Cw9/DOP7Rud8fESZ7pmhmRE4Fe3Vp2/C+CW3qRnoptViXYOWr/sfKIKSlxIx+QF4Tw58tE5r2XbUVzAF0rQ2mLz9ASi5FnAgJi/DBRULeKhUMVPxsPhMWX5R25J3Gj5QnIED7PjttEt~3294770~3491121",
|
||||
"_abck": "F5684DE447CDB1B381EABA9AB94E79B7~-1~YAAQJKVf1GzUK7CXAQAAcY8tzQ60GFr2A1gYL72t6F06CTbh+67guEB40t7OXrDJpLYousPo1UKwE9/z804ie8unZxI7iZhwZO/AJfavIw2JHsMnYOhg8S8U/P+hTMOu0KvFYhMfmbSVSHEMInpzJlFPnFHcbYX1GtPn0US/FI8NeDxamlefbV4vHAYxQCWXp1RUVflOukD/ix7BGIvVqNdTQJDMfDY3UmNyu9JC88T8gFDUBxpTJvHNAzafWV7HTpSzLUmYzkFMp0Py39ZVOkVKgEwI9M15xseSNIzVBm6hm6DHwN9Z6ogDuaNsMkY3iJhL9+h75OTq2If9wNMiehwa5XeLHGfSYizXzUFJhuHdcEI1EZAowl2JKq4iGynNIom1/0v3focwlDFi93wxzpCXhCZBKnIRiIYGgS47zjS6kCZpYvuoBRnNvFx7tdJHMMkQQvx6+pk5UzmT4n3jUjS2WUTRoDuwiEvs5NDiO/Z2r4zHlpZnskDdpsDXT2SxvtMo1J451PCPSzt0merJ8vHZD5eLYE0tDBJaLMPzpW9MPHgW/OqrRc5QjcsdhHxNBnMGfhV2U0aHxVsuSuguZRPz7hGDRQJJXepAU8UzDM/d9KSYdMxUvSfcIk+48e3HHyodrKrfXh/0yIaeamsLeYE2na321B0DUoWe28DKbAIY3WdeYfH3WsGJ/LNrM43HeAe8Ng5Bw+5M0rO8m6MqGbaROvdt4JwBheY8g1jMcyXmXJWBAN0in+5F/sXph1sFdPxiiCc2uKQbyuBA34glvFz1JsbPGATEbicRvW0w88JlY3Ki8yNkEYxyFDv3n2C6R3I7Z/ZjdSJLVmS47sWnow1K6YAa31a3A8eVVFItran2v7S2QJBVmS7zb89yVO7oUq16z9a7o+0K5setv8d/jPkPIn9jgWcFOfVh7osl2g0vB/ZTmLoMvES5VxkWZPP3Uo9oIEyIaFzGq7ppYJ24SLj9I6wo9m5Xq9pup33F0Cpn2GyRzoxLpMm7bV/2EJ5eLBjJ3YFQRZxYf2NU1k2CJifFCfSQYOlhu7qCBxNWryWjQQgz9uvGqoKs~-1~-1~-1",
|
||||
"RT": "\"z=1&dm=www.mexc.com&si=5943fd2a-6403-43d4-87aa-b4ac4403c94f&ss=mcmi7gg2&sl=3&tt=6d5&bcn=%2F%2F02179916.akstat.io%2F&ld=2fhr\"",
|
||||
"mexc_fingerprint_visitorId": "tv1xchuZQbx9N0aBztUG",
|
||||
"_ga_L6XJCQTK75": "GS2.1.s1751493837$o1$g1$t1751493945$j59$l0$h0",
|
||||
"uc_token": "WEB3756d4bd507f4dc9e5c6732b16d40aa668a2e3aea55107801a42f40389c39b9c",
|
||||
"u_id": "WEB3756d4bd507f4dc9e5c6732b16d40aa668a2e3aea55107801a42f40389c39b9c",
|
||||
"_fbp": "fb.1.1751493843684.307329583674408195",
|
||||
"mxc_exchange_layout": "BA",
|
||||
"sensorsdata2015jssdkcross": "%7B%22distinct_id%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%2C%22first_id%22%3A%22197cd2b02f56f6-08b72b0d8e14ee-26011f51-3686400-197cd2b02f6b59%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_landing_page%22%3A%22https%3A%2F%2Fwww.mexc.com%2Fen-GB%2Flogin%3Fprevious%3D%252Ffutures%252FETH_USDT%253Ftype%253Dlinear_swap%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTk3Y2QyYjAyZjU2ZjYtMDhiNzJiMGQ4ZTE0ZWUtMjYwMTFmNTEtMzY4NjQwMC0xOTdjZDJiMDJmNmI1OSIsIiRpZGVudGl0eV9sb2dpbl9pZCI6IjIxYTg3Mjg5OTBiODRmNGZhM2FlNjRjODAwNGI0YWFhIn0%3D%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%2221a8728990b84f4fa3ae64c8004b4aaa%22%7D%2C%22%24device_id%22%3A%22197cd2b02f56f6-08b72b0d8e14ee-26011f51-3686400-197cd2b02f6b59%22%7D",
|
||||
"mxc_theme_main": "dark",
|
||||
"mexc_fingerprint_requestId": "1751493848491.aXJWxX",
|
||||
"ak_bmsc": "10B7B90E8C6CA0B2242A59C6BE9D5D09~000000000000000000000000000000~YAAQJKVf1BnQK7CXAQAAJwsrzRyGc8OCIHU9sjkSsoX2E9ZroYaoxZCEToLh8uS5k28z0rzxl4Oi8eXg1oKxdWZslNQCj4/PExgD4O1++Wfi2KNovx4cUehcmbtiR3a28w+gNaiVpWAUPjPnUTaHLAr7cgVU/IOdoOC0cdvxaHThWtwIbVu+YsGazlnHiND1w3u7V0Yc1irC6ZONXqD2rIIZlntEOFiJGPTs8egY3xMLeSpI0tZYp8CASAKzxp/v96ugcPBMehwZ03ue6s6bi8qGYgF1IuOgVTFW9lPVzxCYjvH+ASlmppbLm/vrCUSPjtzJcTz/ySfvtMYaai8cv3CwCf/Ke51plRXJo0wIzGOpBzzJG5/GMA924kx1EQiBTgJptG0i7ZrgrfhqtBjjB2sU0ZBofFqmVu/VXLV6iOCQBHFtpZeI60oFARGoZFP2mYbfxeIKG8ERrQ==",
|
||||
"mexc_clearance_modal_show_date": "2025-07-03-undefined",
|
||||
"_ym_isad": "2",
|
||||
"_vid_t": "hRsGoNygvD+rX1A4eY/XZLO5cGWlpbA3XIXKtYTjDPFdunb5ACYp5eKitX9KQSQj/YXpG2PcnbPZDIpAVQ0AGjaUpR058ahvxYptRHKSGwPghgfLZQ==",
|
||||
"_ym_visorc": "b",
|
||||
"_ym_d": "1751493846",
|
||||
"_ym_uid": "1751493846425437427",
|
||||
"mxc_theme_upcolor": "upgreen",
|
||||
"NEXT_LOCALE": "en-GB",
|
||||
"x-mxc-fingerprint": "tv1xchuZQbx9N0aBztUG",
|
||||
"CLIENT_LANG": "en-GB",
|
||||
"_ga": "GA1.1.1034661072.1751493838",
|
||||
"sajssdk_2015_cross_new_user": "1"
|
||||
}
|
16883
mexc_requests_20250703_003625.json
Normal file
16883
mexc_requests_20250703_003625.json
Normal file
File diff suppressed because it is too large
Load Diff
20612
mexc_requests_20250703_010352.json
Normal file
20612
mexc_requests_20250703_010352.json
Normal file
File diff suppressed because it is too large
Load Diff
9351
mexc_requests_20250703_015321.json
Normal file
9351
mexc_requests_20250703_015321.json
Normal file
File diff suppressed because it is too large
Load Diff
15618
mexc_requests_20250703_021049.json
Normal file
15618
mexc_requests_20250703_021049.json
Normal file
File diff suppressed because it is too large
Load Diff
8072
mexc_requests_20250703_022428.json
Normal file
8072
mexc_requests_20250703_022428.json
Normal file
File diff suppressed because it is too large
Load Diff
6811
mexc_requests_20250703_023536.json
Normal file
6811
mexc_requests_20250703_023536.json
Normal file
File diff suppressed because it is too large
Load Diff
8243
mexc_requests_20250703_024032.json
Normal file
8243
mexc_requests_20250703_024032.json
Normal file
File diff suppressed because it is too large
Load Diff
64
tests/test_mexc_account_private.py
Normal file
64
tests/test_mexc_account_private.py
Normal file
@ -0,0 +1,64 @@
|
||||
import os
|
||||
import logging
|
||||
import sys
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Add project root to path
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from NN.exchanges.mexc_interface import MEXCInterface
|
||||
from core.config import get_config
|
||||
|
||||
def test_mexc_private_api():
|
||||
"""Test MEXC private API endpoints"""
|
||||
# Load configuration
|
||||
config = get_config('config.yaml')
|
||||
mexc_config = config.get('mexc_trading', {})
|
||||
|
||||
# Get API credentials
|
||||
api_key = os.getenv('MEXC_API_KEY', mexc_config.get('api_key', ''))
|
||||
api_secret = os.getenv('MEXC_SECRET_KEY', mexc_config.get('api_secret', ''))
|
||||
|
||||
if not api_key or not api_secret:
|
||||
logger.error("API key or secret not found. Please set MEXC_API_KEY and MEXC_SECRET_KEY environment variables.")
|
||||
return
|
||||
|
||||
# Initialize MEXC interface in test mode
|
||||
mexc = MEXCInterface(api_key=api_key, api_secret=api_secret, test_mode=True, trading_mode='simulation')
|
||||
|
||||
# Test connection
|
||||
if not mexc.connect():
|
||||
logger.error("Failed to connect to MEXC API")
|
||||
return
|
||||
|
||||
# Test getting account information
|
||||
logger.info("Testing account information retrieval...")
|
||||
account_info = mexc.get_account_info()
|
||||
if account_info:
|
||||
logger.info(f"Account info retrieved: {account_info}")
|
||||
else:
|
||||
logger.error("Failed to retrieve account info")
|
||||
|
||||
# Test getting balance for a specific asset
|
||||
asset = "USDT"
|
||||
logger.info(f"Testing balance retrieval for {asset}...")
|
||||
balance = mexc.get_balance(asset)
|
||||
logger.info(f"Balance for {asset}: {balance}")
|
||||
|
||||
# Test placing a simulated order (in test mode)
|
||||
symbol = "ETH/USDT"
|
||||
side = "buy"
|
||||
order_type = "market"
|
||||
quantity = 0.01 # Small quantity for testing
|
||||
logger.info(f"Testing order placement for {symbol} ({side}, {order_type}, qty: {quantity})...")
|
||||
order_result = mexc.place_order(symbol=symbol, side=side, order_type=order_type, quantity=quantity)
|
||||
if order_result:
|
||||
logger.info(f"Order placed successfully: {order_result}")
|
||||
else:
|
||||
logger.error("Failed to place order")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_mexc_private_api()
|
59
tests/test_mexc_account_privte.py
Normal file
59
tests/test_mexc_account_privte.py
Normal file
@ -0,0 +1,59 @@
|
||||
import logging
|
||||
import os
|
||||
from NN.exchanges.mexc_interface import MEXCInterface
|
||||
|
||||
# Set up logging to see debug info
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
# Load API credentials from environment variables or a configuration file
|
||||
# For testing, prioritize environment variables for CI/CD or sensitive data
|
||||
# Fallback to a placeholder or configuration reading if env vars are not set
|
||||
api_key = os.getenv('MEXC_API_KEY', '')
|
||||
api_secret = os.getenv('MEXC_SECRET_KEY', '')
|
||||
|
||||
# If using a config file, you might do something like:
|
||||
# from core.config import get_config
|
||||
# config = get_config('config.yaml')
|
||||
# mexc_config = config.get('mexc_trading', {})
|
||||
# api_key = mexc_config.get('api_key', api_key)
|
||||
# api_secret = mexc_config.get('api_secret', api_secret)
|
||||
|
||||
if not api_key or not api_secret:
|
||||
logging.error("API keys are not set. Please set MEXC_API_KEY and MEXC_SECRET_KEY environment variables or configure config.yaml")
|
||||
exit(1)
|
||||
|
||||
# Create interface with API credentials
|
||||
mexc = MEXCInterface(
|
||||
api_key=api_key,
|
||||
api_secret=api_secret,
|
||||
trading_mode='simulation'
|
||||
)
|
||||
|
||||
print('MEXC Interface created successfully')
|
||||
|
||||
# Test signature generation
|
||||
import time
|
||||
timestamp = int(time.time() * 1000)
|
||||
test_params = 'quantity=1&price=11&symbol=BTCUSDT&side=BUY&type=LIMIT×tamp=' + str(timestamp)
|
||||
signature = mexc._generate_signature(timestamp, test_params)
|
||||
print(f'Generated signature: {signature}')
|
||||
|
||||
# Test account info
|
||||
print('Testing account info...')
|
||||
account_info = mexc.get_account_info()
|
||||
print(f'Account info result: {account_info}')
|
||||
|
||||
# Test ticker data
|
||||
print('Testing ticker data...')
|
||||
ticker = mexc.get_ticker('ETH/USDT')
|
||||
print(f'ETH/USDT ticker: {ticker}')
|
||||
|
||||
# Test balance retrieval
|
||||
print('Testing balance retrieval...')
|
||||
usdt_balance = mexc.get_balance('USDT')
|
||||
print(f'USDT balance: {usdt_balance}')
|
||||
|
||||
# Test a small order placement (simulation mode)
|
||||
print('Testing order placement in simulation mode...')
|
||||
order_result = mexc.place_order('ETH/USDT', 'buy', 'market', 0.001)
|
||||
print(f'Order result: {order_result}')
|
@ -1,208 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test MEXC Futures Web Client
|
||||
|
||||
This script demonstrates how to use the MEXC Futures Web Client
|
||||
for futures trading that isn't supported by their official API.
|
||||
|
||||
IMPORTANT: This requires extracting cookies from your browser session.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
# Add the project root to path
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from core.mexc_webclient import MEXCFuturesWebClient
|
||||
from core.mexc_webclient.session_manager import MEXCSessionManager
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def test_basic_connection():
|
||||
"""Test basic connection and authentication"""
|
||||
logger.info("Testing MEXC Futures Web Client")
|
||||
|
||||
# Initialize session manager
|
||||
session_manager = MEXCSessionManager()
|
||||
|
||||
# Try to load saved session first
|
||||
cookies = session_manager.load_session()
|
||||
|
||||
if not cookies:
|
||||
print("\nNo saved session found. You need to extract cookies from your browser.")
|
||||
session_manager.print_cookie_extraction_guide()
|
||||
|
||||
print("\nPaste your cookie header or cURL command (or press Enter to exit):")
|
||||
user_input = input().strip()
|
||||
|
||||
if not user_input:
|
||||
print("No input provided. Exiting.")
|
||||
return False
|
||||
|
||||
# Extract cookies from user input
|
||||
if user_input.startswith('curl'):
|
||||
cookies = session_manager.extract_from_curl_command(user_input)
|
||||
else:
|
||||
cookies = session_manager.extract_cookies_from_network_tab(user_input)
|
||||
|
||||
if not cookies:
|
||||
logger.error("Failed to extract cookies from input")
|
||||
return False
|
||||
|
||||
# Validate and save session
|
||||
if session_manager.validate_session_cookies(cookies):
|
||||
session_manager.save_session(cookies)
|
||||
logger.info("Session saved for future use")
|
||||
else:
|
||||
logger.warning("Extracted cookies may be incomplete")
|
||||
|
||||
# Initialize the web client
|
||||
client = MEXCFuturesWebClient(cookies)
|
||||
|
||||
if not client.is_authenticated:
|
||||
logger.error("Failed to authenticate with extracted cookies")
|
||||
return False
|
||||
|
||||
logger.info("Successfully authenticated with MEXC")
|
||||
logger.info(f"User ID: {client.user_id}")
|
||||
logger.info(f"Auth Token: {client.auth_token[:20]}..." if client.auth_token else "No auth token")
|
||||
|
||||
return True
|
||||
|
||||
def test_captcha_verification(client: MEXCFuturesWebClient):
|
||||
"""Test captcha verification system"""
|
||||
logger.info("Testing captcha verification...")
|
||||
|
||||
# Test captcha for ETH_USDT long position with 200x leverage
|
||||
success = client.verify_captcha('ETH_USDT', 'openlong', '200X')
|
||||
|
||||
if success:
|
||||
logger.info("Captcha verification successful")
|
||||
else:
|
||||
logger.warning("Captcha verification failed - this may be normal if no position is being opened")
|
||||
|
||||
return success
|
||||
|
||||
def test_position_opening(client: MEXCFuturesWebClient, dry_run: bool = True):
|
||||
"""Test opening a position (dry run by default)"""
|
||||
if dry_run:
|
||||
logger.info("DRY RUN: Testing position opening (no actual trade)")
|
||||
else:
|
||||
logger.warning("LIVE TRADING: Opening actual position!")
|
||||
|
||||
symbol = 'ETH_USDT'
|
||||
volume = 1 # Small test position
|
||||
leverage = 200
|
||||
|
||||
logger.info(f"Attempting to open long position: {symbol}, Volume: {volume}, Leverage: {leverage}x")
|
||||
|
||||
if not dry_run:
|
||||
result = client.open_long_position(symbol, volume, leverage)
|
||||
|
||||
if result['success']:
|
||||
logger.info(f"Position opened successfully!")
|
||||
logger.info(f"Order ID: {result['order_id']}")
|
||||
logger.info(f"Timestamp: {result['timestamp']}")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"Failed to open position: {result['error']}")
|
||||
return False
|
||||
else:
|
||||
logger.info("DRY RUN: Would attempt to open position here")
|
||||
# Test just the captcha verification part
|
||||
return client.verify_captcha(symbol, 'openlong', f'{leverage}X')
|
||||
|
||||
def interactive_menu(client: MEXCFuturesWebClient):
|
||||
"""Interactive menu for testing different functions"""
|
||||
while True:
|
||||
print("\n" + "="*50)
|
||||
print("MEXC Futures Web Client Test Menu")
|
||||
print("="*50)
|
||||
print("1. Test captcha verification")
|
||||
print("2. Test position opening (DRY RUN)")
|
||||
print("3. Test position opening (LIVE - BE CAREFUL!)")
|
||||
print("4. Test position closing (DRY RUN)")
|
||||
print("5. Show session info")
|
||||
print("6. Refresh session")
|
||||
print("0. Exit")
|
||||
|
||||
choice = input("\nEnter choice (0-6): ").strip()
|
||||
|
||||
if choice == "1":
|
||||
test_captcha_verification(client)
|
||||
|
||||
elif choice == "2":
|
||||
test_position_opening(client, dry_run=True)
|
||||
|
||||
elif choice == "3":
|
||||
confirm = input("Are you sure you want to open a LIVE position? (type 'YES' to confirm): ")
|
||||
if confirm == "YES":
|
||||
test_position_opening(client, dry_run=False)
|
||||
else:
|
||||
print("Cancelled live trading")
|
||||
|
||||
elif choice == "4":
|
||||
logger.info("DRY RUN: Position closing test")
|
||||
success = client.verify_captcha('ETH_USDT', 'closelong', '200X')
|
||||
if success:
|
||||
logger.info("DRY RUN: Would close position here")
|
||||
else:
|
||||
logger.warning("Captcha verification failed for position closing")
|
||||
|
||||
elif choice == "5":
|
||||
print(f"\nSession Information:")
|
||||
print(f"Authenticated: {client.is_authenticated}")
|
||||
print(f"User ID: {client.user_id}")
|
||||
print(f"Auth Token: {client.auth_token[:20]}..." if client.auth_token else "None")
|
||||
print(f"Fingerprint: {client.fingerprint}")
|
||||
print(f"Visitor ID: {client.visitor_id}")
|
||||
|
||||
elif choice == "6":
|
||||
session_manager = MEXCSessionManager()
|
||||
session_manager.print_cookie_extraction_guide()
|
||||
|
||||
elif choice == "0":
|
||||
print("Goodbye!")
|
||||
break
|
||||
|
||||
else:
|
||||
print("Invalid choice. Please try again.")
|
||||
|
||||
def main():
|
||||
"""Main test function"""
|
||||
print("MEXC Futures Web Client Test")
|
||||
print("WARNING: This is experimental software for futures trading")
|
||||
print("Use at your own risk and test with small amounts first!")
|
||||
|
||||
# Test basic connection
|
||||
if not test_basic_connection():
|
||||
logger.error("Failed to establish connection. Exiting.")
|
||||
return
|
||||
|
||||
# Create client with loaded session
|
||||
session_manager = MEXCSessionManager()
|
||||
cookies = session_manager.load_session()
|
||||
|
||||
if not cookies:
|
||||
logger.error("No valid session available")
|
||||
return
|
||||
|
||||
client = MEXCFuturesWebClient(cookies)
|
||||
|
||||
if not client.is_authenticated:
|
||||
logger.error("Authentication failed")
|
||||
return
|
||||
|
||||
# Show interactive menu
|
||||
interactive_menu(client)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -87,7 +87,7 @@ class CheckpointManager:
|
||||
performance_score = self._calculate_performance_score(performance_metrics)
|
||||
|
||||
if not force_save and not self._should_save_checkpoint(model_name, performance_score):
|
||||
logger.info(f"Skipping checkpoint save for {model_name} - performance not improved")
|
||||
logger.debug(f"Skipping checkpoint save for {model_name} - performance not improved")
|
||||
return None
|
||||
|
||||
success = self._save_model_file(model, checkpoint_path, model_type)
|
||||
@ -140,10 +140,11 @@ class CheckpointManager:
|
||||
best_checkpoint = max(self.checkpoints[model_name], key=lambda x: x.performance_score)
|
||||
|
||||
if not Path(best_checkpoint.file_path).exists():
|
||||
logger.error(f"Best checkpoint file not found: {best_checkpoint.file_path}")
|
||||
# temporary disable logging to avoid spam
|
||||
# logger.error(f"Best checkpoint file not found: {best_checkpoint.file_path}")
|
||||
return None
|
||||
|
||||
logger.info(f"Loading best checkpoint for {model_name}: {best_checkpoint.checkpoint_id}")
|
||||
logger.debug(f"Loading best checkpoint for {model_name}: {best_checkpoint.checkpoint_id}")
|
||||
return best_checkpoint.file_path, best_checkpoint
|
||||
|
||||
except Exception as e:
|
||||
|
@ -5474,7 +5474,7 @@ class CleanTradingDashboard:
|
||||
if training_samples > 0:
|
||||
avg_loss_info = f", avg_loss={total_loss/loss_count:.6f}" if loss_count > 0 else ""
|
||||
performance_score = 100 / (1 + (total_loss/loss_count)) if loss_count > 0 else 0.1
|
||||
logger.info(f"DECISION TRAINING: Processed {training_samples} decision fusion samples{avg_loss_info}, perf_score={performance_score:.4f}")
|
||||
logger.debug(f"DECISION TRAINING: Processed {training_samples} decision fusion samples{avg_loss_info}, perf_score={performance_score:.4f}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in real decision fusion training: {e}")
|
||||
|
||||
|
Reference in New Issue
Block a user