dash closed trades history
This commit is contained in:
384
test_mexc_trading_integration.py
Normal file
384
test_mexc_trading_integration.py
Normal file
@ -0,0 +1,384 @@
|
||||
"""
|
||||
Test MEXC Trading Integration
|
||||
|
||||
This script tests the integration between the enhanced orchestrator and MEXC trading executor.
|
||||
It verifies that trading signals can be executed through the MEXC API with proper risk management.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
# Add core directory to path
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'core'))
|
||||
|
||||
from core.trading_executor import TradingExecutor, Position, TradeRecord
|
||||
from core.enhanced_orchestrator import EnhancedTradingOrchestrator
|
||||
from core.data_provider import DataProvider
|
||||
from core.config import get_config
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler("test_mexc_trading.log"),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger("mexc_trading_test")
|
||||
|
||||
class TradingIntegrationTest:
|
||||
"""Test class for MEXC trading integration"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the test environment"""
|
||||
self.config = get_config()
|
||||
self.data_provider = DataProvider()
|
||||
self.orchestrator = EnhancedTradingOrchestrator(self.data_provider)
|
||||
self.trading_executor = TradingExecutor()
|
||||
|
||||
# Test configuration
|
||||
self.test_symbol = 'ETH/USDT'
|
||||
self.test_confidence = 0.75
|
||||
|
||||
def test_trading_executor_initialization(self):
|
||||
"""Test that the trading executor initializes correctly"""
|
||||
logger.info("Testing trading executor initialization...")
|
||||
|
||||
try:
|
||||
# Check configuration
|
||||
assert self.trading_executor.mexc_config is not None
|
||||
logger.info("✅ MEXC configuration loaded")
|
||||
|
||||
# Check dry run mode
|
||||
assert self.trading_executor.dry_run == True
|
||||
logger.info("✅ Dry run mode enabled for safety")
|
||||
|
||||
# Check position limits
|
||||
max_position_value = self.trading_executor.mexc_config.get('max_position_value_usd', 1.0)
|
||||
assert max_position_value == 1.0
|
||||
logger.info(f"✅ Max position value set to ${max_position_value}")
|
||||
|
||||
# Check safety features
|
||||
assert self.trading_executor.mexc_config.get('emergency_stop', False) == False
|
||||
logger.info("✅ Emergency stop not active")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Trading executor initialization test failed: {e}")
|
||||
return False
|
||||
|
||||
def test_exchange_connection(self):
|
||||
"""Test connection to MEXC exchange"""
|
||||
logger.info("Testing MEXC exchange connection...")
|
||||
|
||||
try:
|
||||
# Test ticker retrieval
|
||||
ticker = self.trading_executor.exchange.get_ticker(self.test_symbol)
|
||||
|
||||
if ticker:
|
||||
logger.info(f"✅ Successfully retrieved ticker for {self.test_symbol}")
|
||||
logger.info(f" Current price: ${ticker['last']:.2f}")
|
||||
logger.info(f" Bid: ${ticker['bid']:.2f}, Ask: ${ticker['ask']:.2f}")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"❌ Failed to retrieve ticker for {self.test_symbol}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Exchange connection test failed: {e}")
|
||||
return False
|
||||
|
||||
def test_position_size_calculation(self):
|
||||
"""Test position size calculation with different confidence levels"""
|
||||
logger.info("Testing position size calculation...")
|
||||
|
||||
try:
|
||||
test_price = 2500.0
|
||||
test_cases = [
|
||||
(0.5, "Medium confidence"),
|
||||
(0.75, "High confidence"),
|
||||
(0.9, "Very high confidence"),
|
||||
(0.3, "Low confidence")
|
||||
]
|
||||
|
||||
for confidence, description in test_cases:
|
||||
position_value = self.trading_executor._calculate_position_size(confidence, test_price)
|
||||
quantity = position_value / test_price
|
||||
|
||||
logger.info(f" {description} ({confidence:.1f}): ${position_value:.2f} = {quantity:.6f} ETH")
|
||||
|
||||
# Verify position value is within limits
|
||||
max_value = self.trading_executor.mexc_config.get('max_position_value_usd', 1.0)
|
||||
min_value = self.trading_executor.mexc_config.get('min_position_value_usd', 0.1)
|
||||
|
||||
assert min_value <= position_value <= max_value
|
||||
|
||||
logger.info("✅ Position size calculation working correctly")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Position size calculation test failed: {e}")
|
||||
return False
|
||||
|
||||
def test_dry_run_trading(self):
|
||||
"""Test dry run trading execution"""
|
||||
logger.info("Testing dry run trading execution...")
|
||||
|
||||
try:
|
||||
# Get current price
|
||||
ticker = self.trading_executor.exchange.get_ticker(self.test_symbol)
|
||||
if not ticker:
|
||||
logger.error("Cannot get current price for testing")
|
||||
return False
|
||||
|
||||
current_price = ticker['last']
|
||||
|
||||
# Test BUY signal
|
||||
logger.info(f"Testing BUY signal for {self.test_symbol} at ${current_price:.2f}")
|
||||
buy_success = self.trading_executor.execute_signal(
|
||||
symbol=self.test_symbol,
|
||||
action='BUY',
|
||||
confidence=self.test_confidence,
|
||||
current_price=current_price
|
||||
)
|
||||
|
||||
if buy_success:
|
||||
logger.info("✅ BUY signal executed successfully in dry run mode")
|
||||
|
||||
# Check position was created
|
||||
positions = self.trading_executor.get_positions()
|
||||
assert self.test_symbol in positions
|
||||
position = positions[self.test_symbol]
|
||||
logger.info(f" Position created: {position.side} {position.quantity:.6f} @ ${position.entry_price:.2f}")
|
||||
else:
|
||||
logger.error("❌ BUY signal execution failed")
|
||||
return False
|
||||
|
||||
# Wait a moment
|
||||
time.sleep(1)
|
||||
|
||||
# Test SELL signal
|
||||
logger.info(f"Testing SELL signal for {self.test_symbol}")
|
||||
sell_success = self.trading_executor.execute_signal(
|
||||
symbol=self.test_symbol,
|
||||
action='SELL',
|
||||
confidence=self.test_confidence,
|
||||
current_price=current_price * 1.01 # Simulate 1% price increase
|
||||
)
|
||||
|
||||
if sell_success:
|
||||
logger.info("✅ SELL signal executed successfully in dry run mode")
|
||||
|
||||
# Check position was closed
|
||||
positions = self.trading_executor.get_positions()
|
||||
assert self.test_symbol not in positions
|
||||
|
||||
# Check trade history
|
||||
trade_history = self.trading_executor.get_trade_history()
|
||||
assert len(trade_history) > 0
|
||||
|
||||
last_trade = trade_history[-1]
|
||||
logger.info(f" Trade completed: P&L ${last_trade.pnl:.2f}")
|
||||
else:
|
||||
logger.error("❌ SELL signal execution failed")
|
||||
return False
|
||||
|
||||
logger.info("✅ Dry run trading test completed successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Dry run trading test failed: {e}")
|
||||
return False
|
||||
|
||||
def test_safety_conditions(self):
|
||||
"""Test safety condition checks"""
|
||||
logger.info("Testing safety condition checks...")
|
||||
|
||||
try:
|
||||
# Test symbol allowlist
|
||||
disallowed_symbol = 'DOGE/USDT'
|
||||
result = self.trading_executor._check_safety_conditions(disallowed_symbol, 'BUY')
|
||||
if disallowed_symbol not in self.trading_executor.mexc_config.get('allowed_symbols', []):
|
||||
assert result == False
|
||||
logger.info("✅ Symbol allowlist check working")
|
||||
|
||||
# Test trade interval
|
||||
# First trade should succeed
|
||||
current_price = 2500.0
|
||||
self.trading_executor.execute_signal(self.test_symbol, 'BUY', 0.7, current_price)
|
||||
|
||||
# Immediate second trade should fail due to interval
|
||||
result = self.trading_executor._check_safety_conditions(self.test_symbol, 'BUY')
|
||||
# Note: This might pass if interval is very short, which is fine for testing
|
||||
|
||||
logger.info("✅ Safety condition checks working")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Safety condition test failed: {e}")
|
||||
return False
|
||||
|
||||
def test_daily_statistics(self):
|
||||
"""Test daily statistics tracking"""
|
||||
logger.info("Testing daily statistics tracking...")
|
||||
|
||||
try:
|
||||
stats = self.trading_executor.get_daily_stats()
|
||||
|
||||
required_keys = ['daily_trades', 'daily_loss', 'total_pnl', 'winning_trades',
|
||||
'losing_trades', 'win_rate', 'positions_count']
|
||||
|
||||
for key in required_keys:
|
||||
assert key in stats
|
||||
|
||||
logger.info("✅ Daily statistics structure correct")
|
||||
logger.info(f" Daily trades: {stats['daily_trades']}")
|
||||
logger.info(f" Total P&L: ${stats['total_pnl']:.2f}")
|
||||
logger.info(f" Win rate: {stats['win_rate']:.1%}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Daily statistics test failed: {e}")
|
||||
return False
|
||||
|
||||
async def test_orchestrator_integration(self):
|
||||
"""Test integration with enhanced orchestrator"""
|
||||
logger.info("Testing orchestrator integration...")
|
||||
|
||||
try:
|
||||
# Test that orchestrator can make decisions
|
||||
decisions = await self.orchestrator.make_coordinated_decisions()
|
||||
|
||||
logger.info(f"✅ Orchestrator made decisions for {len(decisions)} symbols")
|
||||
|
||||
for symbol, decision in decisions.items():
|
||||
if decision:
|
||||
logger.info(f" {symbol}: {decision.action} (confidence: {decision.confidence:.3f})")
|
||||
|
||||
# Test executing the decision through trading executor
|
||||
if decision.action != 'HOLD':
|
||||
success = self.trading_executor.execute_signal(
|
||||
symbol=symbol,
|
||||
action=decision.action,
|
||||
confidence=decision.confidence,
|
||||
current_price=decision.price
|
||||
)
|
||||
|
||||
if success:
|
||||
logger.info(f" ✅ Successfully executed {decision.action} for {symbol}")
|
||||
else:
|
||||
logger.info(f" ⚠️ Trade execution blocked by safety conditions for {symbol}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Orchestrator integration test failed: {e}")
|
||||
return False
|
||||
|
||||
def test_emergency_stop(self):
|
||||
"""Test emergency stop functionality"""
|
||||
logger.info("Testing emergency stop functionality...")
|
||||
|
||||
try:
|
||||
# Create a test position first
|
||||
current_price = 2500.0
|
||||
self.trading_executor.execute_signal(self.test_symbol, 'BUY', 0.7, current_price)
|
||||
|
||||
# Verify position exists
|
||||
positions_before = self.trading_executor.get_positions()
|
||||
logger.info(f" Positions before emergency stop: {len(positions_before)}")
|
||||
|
||||
# Trigger emergency stop
|
||||
self.trading_executor.emergency_stop()
|
||||
|
||||
# Verify trading is disabled
|
||||
assert self.trading_executor.trading_enabled == False
|
||||
logger.info("✅ Trading disabled after emergency stop")
|
||||
|
||||
# In dry run mode, positions should still be closed
|
||||
positions_after = self.trading_executor.get_positions()
|
||||
logger.info(f" Positions after emergency stop: {len(positions_after)}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Emergency stop test failed: {e}")
|
||||
return False
|
||||
|
||||
async def run_all_tests(self):
|
||||
"""Run all integration tests"""
|
||||
logger.info("🚀 Starting MEXC Trading Integration Tests")
|
||||
logger.info("=" * 60)
|
||||
|
||||
tests = [
|
||||
("Trading Executor Initialization", self.test_trading_executor_initialization),
|
||||
("Exchange Connection", self.test_exchange_connection),
|
||||
("Position Size Calculation", self.test_position_size_calculation),
|
||||
("Dry Run Trading", self.test_dry_run_trading),
|
||||
("Safety Conditions", self.test_safety_conditions),
|
||||
("Daily Statistics", self.test_daily_statistics),
|
||||
("Orchestrator Integration", self.test_orchestrator_integration),
|
||||
("Emergency Stop", self.test_emergency_stop),
|
||||
]
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
for test_name, test_func in tests:
|
||||
logger.info(f"\n📋 Running test: {test_name}")
|
||||
logger.info("-" * 40)
|
||||
|
||||
try:
|
||||
if asyncio.iscoroutinefunction(test_func):
|
||||
result = await test_func()
|
||||
else:
|
||||
result = test_func()
|
||||
|
||||
if result:
|
||||
passed += 1
|
||||
logger.info(f"✅ {test_name} PASSED")
|
||||
else:
|
||||
failed += 1
|
||||
logger.error(f"❌ {test_name} FAILED")
|
||||
|
||||
except Exception as e:
|
||||
failed += 1
|
||||
logger.error(f"❌ {test_name} FAILED with exception: {e}")
|
||||
|
||||
logger.info("\n" + "=" * 60)
|
||||
logger.info("🏁 Test Results Summary")
|
||||
logger.info(f"✅ Passed: {passed}")
|
||||
logger.info(f"❌ Failed: {failed}")
|
||||
logger.info(f"📊 Success Rate: {passed/(passed+failed)*100:.1f}%")
|
||||
|
||||
if failed == 0:
|
||||
logger.info("🎉 All tests passed! MEXC trading integration is ready.")
|
||||
else:
|
||||
logger.warning(f"⚠️ {failed} test(s) failed. Please review and fix issues before live trading.")
|
||||
|
||||
return failed == 0
|
||||
|
||||
async def main():
|
||||
"""Main test function"""
|
||||
test_runner = TradingIntegrationTest()
|
||||
success = await test_runner.run_all_tests()
|
||||
|
||||
if success:
|
||||
logger.info("\n🔧 Next Steps:")
|
||||
logger.info("1. Set up your MEXC API keys in .env file")
|
||||
logger.info("2. Update config.yaml to enable trading (mexc_trading.enabled: true)")
|
||||
logger.info("3. Consider disabling dry_run_mode for live trading")
|
||||
logger.info("4. Start with small position sizes for initial live testing")
|
||||
logger.info("5. Monitor the system closely during initial live trading")
|
||||
|
||||
return success
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
Reference in New Issue
Block a user