From 9c56ea238e9260200a056e57f466cd8b40cef296 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Sun, 20 Jul 2025 18:08:37 +0300 Subject: [PATCH] dynamic profitabiliy reward --- core/orchestrator.py | 47 +++++ core/trading_executor.py | 107 +++++++++- test_profitability_reward_system.py | 294 ++++++++++++++++++++++++++++ web/clean_dashboard.py | 19 +- web/layout_manager.py | 1 + 5 files changed, 465 insertions(+), 3 deletions(-) create mode 100644 test_profitability_reward_system.py diff --git a/core/orchestrator.py b/core/orchestrator.py index 469b85b..2666ea5 100644 --- a/core/orchestrator.py +++ b/core/orchestrator.py @@ -1985,6 +1985,53 @@ class TradingOrchestrator: self.trading_executor = trading_executor logger.info("Trading executor set for position tracking and P&L feedback") + def get_profitability_reward_multiplier(self) -> float: + """Get the current profitability reward multiplier from trading executor + + Returns: + float: Current profitability reward multiplier (0.0 to 2.0) + """ + try: + if self.trading_executor and hasattr(self.trading_executor, 'get_profitability_reward_multiplier'): + multiplier = self.trading_executor.get_profitability_reward_multiplier() + logger.debug(f"Current profitability reward multiplier: {multiplier:.2f}") + return multiplier + return 0.0 + except Exception as e: + logger.error(f"Error getting profitability reward multiplier: {e}") + return 0.0 + + def calculate_enhanced_reward(self, base_pnl: float, confidence: float = 1.0) -> float: + """Calculate enhanced reward with profitability multiplier + + Args: + base_pnl: Base P&L from the trade + confidence: Confidence level of the prediction (0.0 to 1.0) + + Returns: + float: Enhanced reward with profitability multiplier applied + """ + try: + # Get the dynamic profitability multiplier + profitability_multiplier = self.get_profitability_reward_multiplier() + + # Base reward is the P&L + base_reward = base_pnl + + # Apply profitability multiplier only to positive P&L (profitable trades) + if base_pnl > 0 and profitability_multiplier > 0: + # Enhance profitable trades with the multiplier + enhanced_reward = base_pnl * (1.0 + profitability_multiplier) + logger.debug(f"Enhanced reward: ${base_pnl:.2f} โ†’ ${enhanced_reward:.2f} (multiplier: {profitability_multiplier:.2f})") + return enhanced_reward + else: + # No enhancement for losing trades or when multiplier is 0 + return base_reward + + except Exception as e: + logger.error(f"Error calculating enhanced reward: {e}") + return base_pnl + def _check_signal_confirmation(self, symbol: str, signal_data: Dict) -> Optional[str]: """Check if we have enough signal confirmations for trend confirmation with rate limiting""" try: diff --git a/core/trading_executor.py b/core/trading_executor.py index 174e3c7..b04a047 100644 --- a/core/trading_executor.py +++ b/core/trading_executor.py @@ -176,13 +176,25 @@ class TradingExecutor: self.simulation_balance = self.trading_config.get('simulation_account_usd', 100.0) self.simulation_positions = {} # symbol -> position data with real entry prices - # Trading fees configuration (0.1% for both open and close) + # Trading fees configuration (0.1% for both open and close - REVERTED TO NORMAL) self.trading_fees = { 'open_fee_percent': 0.001, # 0.1% fee when opening position 'close_fee_percent': 0.001, # 0.1% fee when closing position 'total_round_trip_fee': 0.002 # 0.2% total for round trip } + # Dynamic profitability reward parameter - starts at 0, adjusts based on success rate + self.profitability_reward_multiplier = 0.0 # Starts at 0, can be increased + self.min_profitability_multiplier = 0.0 # Minimum value + self.max_profitability_multiplier = 2.0 # Maximum 2x multiplier + self.profitability_adjustment_step = 0.1 # Adjust by 0.1 each time + + # Success rate tracking for profitability adjustment + self.recent_trades_window = 20 # Look at last 20 trades + self.success_rate_increase_threshold = 0.60 # Increase multiplier if >60% success + self.success_rate_decrease_threshold = 0.51 # Decrease multiplier if <51% success + self.last_profitability_adjustment = datetime.now() + logger.info(f"TradingExecutor initialized - Trading: {self.trading_enabled}, Mode: {self.trading_mode}") logger.info(f"Simulation balance: ${self.simulation_balance:.2f}") @@ -622,6 +634,83 @@ class TradingExecutor: logger.error(f"Error cancelling open orders for {symbol}: {e}") return 0 + def _calculate_recent_success_rate(self) -> float: + """Calculate success rate of recent closed trades + + Returns: + float: Success rate (0.0 to 1.0) of recent trades + """ + try: + if len(self.trade_records) < 5: # Need at least 5 trades + return 0.0 + + # Get recent trades (up to the window size) + recent_trades = self.trade_records[-self.recent_trades_window:] + + if not recent_trades: + return 0.0 + + # Count winning trades (net PnL > 0) + winning_trades = sum(1 for trade in recent_trades if trade.net_pnl > 0) + success_rate = winning_trades / len(recent_trades) + + logger.debug(f"Recent success rate: {success_rate:.2%} ({winning_trades}/{len(recent_trades)} trades)") + return success_rate + + except Exception as e: + logger.error(f"Error calculating success rate: {e}") + return 0.0 + + def _adjust_profitability_reward_multiplier(self): + """Adjust profitability reward multiplier based on recent success rate""" + try: + # Only adjust every 5 minutes to avoid too frequent changes + current_time = datetime.now() + time_since_last_adjustment = (current_time - self.last_profitability_adjustment).total_seconds() + + if time_since_last_adjustment < 300: # 5 minutes + return + + success_rate = self._calculate_recent_success_rate() + + # Only adjust if we have enough trades + if len(self.trade_records) < 10: + return + + old_multiplier = self.profitability_reward_multiplier + + # Increase multiplier if success rate > 60% + if success_rate > self.success_rate_increase_threshold: + self.profitability_reward_multiplier = min( + self.max_profitability_multiplier, + self.profitability_reward_multiplier + self.profitability_adjustment_step + ) + logger.info(f"๐ŸŽฏ SUCCESS RATE HIGH ({success_rate:.1%}) - Increased profitability multiplier: {old_multiplier:.1f} โ†’ {self.profitability_reward_multiplier:.1f}") + + # Decrease multiplier if success rate < 51% + elif success_rate < self.success_rate_decrease_threshold: + self.profitability_reward_multiplier = max( + self.min_profitability_multiplier, + self.profitability_reward_multiplier - self.profitability_adjustment_step + ) + logger.info(f"โš ๏ธ SUCCESS RATE LOW ({success_rate:.1%}) - Decreased profitability multiplier: {old_multiplier:.1f} โ†’ {self.profitability_reward_multiplier:.1f}") + + else: + logger.debug(f"Success rate {success_rate:.1%} in acceptable range - keeping multiplier at {self.profitability_reward_multiplier:.1f}") + + self.last_profitability_adjustment = current_time + + except Exception as e: + logger.error(f"Error adjusting profitability reward multiplier: {e}") + + def get_profitability_reward_multiplier(self) -> float: + """Get current profitability reward multiplier + + Returns: + float: Current profitability reward multiplier + """ + return self.profitability_reward_multiplier + def _can_reenable_live_trading(self) -> bool: """Check if trading performance has improved enough to re-enable live trading @@ -1198,7 +1287,11 @@ class TradingExecutor: ) self.trade_history.append(trade_record) + self.trade_records.append(trade_record) # Add to trade records for success rate tracking self.daily_loss += max(0, -pnl) # Add to daily loss if negative + + # Adjust profitability reward multiplier based on recent performance + self._adjust_profitability_reward_multiplier() # Update consecutive losses if pnl < -0.001: # A losing trade @@ -1289,8 +1382,12 @@ class TradingExecutor: ) self.trade_history.append(trade_record) + self.trade_records.append(trade_record) # Add to trade records for success rate tracking self.daily_loss += max(0, -(pnl - fees)) # Add to daily loss if negative + # Adjust profitability reward multiplier based on recent performance + self._adjust_profitability_reward_multiplier() + # Update consecutive losses if pnl < -0.001: # A losing trade self.consecutive_losses += 1 @@ -1356,7 +1453,11 @@ class TradingExecutor: ) self.trade_history.append(trade_record) + self.trade_records.append(trade_record) # Add to trade records for success rate tracking self.daily_loss += max(0, -pnl) # Add to daily loss if negative + + # Adjust profitability reward multiplier based on recent performance + self._adjust_profitability_reward_multiplier() # Update consecutive losses if pnl < -0.001: # A losing trade @@ -1428,8 +1529,12 @@ class TradingExecutor: ) self.trade_history.append(trade_record) + self.trade_records.append(trade_record) # Add to trade records for success rate tracking self.daily_loss += max(0, -(pnl - fees)) # Add to daily loss if negative + # Adjust profitability reward multiplier based on recent performance + self._adjust_profitability_reward_multiplier() + # Update consecutive losses if pnl < -0.001: # A losing trade self.consecutive_losses += 1 diff --git a/test_profitability_reward_system.py b/test_profitability_reward_system.py new file mode 100644 index 0000000..06b53c8 --- /dev/null +++ b/test_profitability_reward_system.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python3 +""" +Test script for the dynamic profitability reward system + +This script tests: +1. Fee reversion to normal 0.1% (0.001) +2. Dynamic profitability reward multiplier adjustment +3. Success rate calculation +4. Integration with dashboard display +""" + +import sys +import os +import time +from datetime import datetime, timedelta + +# Add project root to path +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from core.trading_executor import TradingExecutor, TradeRecord +from core.orchestrator import TradingOrchestrator +from core.data_provider import DataProvider + +def test_fee_configuration(): + """Test that fees are reverted to normal 0.1%""" + print("=" * 60) + print("๐Ÿงช TESTING FEE CONFIGURATION") + print("=" * 60) + + executor = TradingExecutor() + + # Check fee configuration + expected_open_fee = 0.001 # 0.1% + expected_close_fee = 0.001 # 0.1% + expected_total_fee = 0.002 # 0.2% + + actual_open_fee = executor.trading_fees['open_fee_percent'] + actual_close_fee = executor.trading_fees['close_fee_percent'] + actual_total_fee = executor.trading_fees['total_round_trip_fee'] + + print(f"Expected Open Fee: {expected_open_fee} (0.1%)") + print(f"Actual Open Fee: {actual_open_fee} (0.1%)") + print(f"โœ… Open Fee: {'PASS' if actual_open_fee == expected_open_fee else 'FAIL'}") + print() + + print(f"Expected Close Fee: {expected_close_fee} (0.1%)") + print(f"Actual Close Fee: {actual_close_fee} (0.1%)") + print(f"โœ… Close Fee: {'PASS' if actual_close_fee == expected_close_fee else 'FAIL'}") + print() + + print(f"Expected Total Fee: {expected_total_fee} (0.2%)") + print(f"Actual Total Fee: {actual_total_fee} (0.2%)") + print(f"โœ… Total Fee: {'PASS' if actual_total_fee == expected_total_fee else 'FAIL'}") + print() + + return actual_open_fee == expected_open_fee and actual_close_fee == expected_close_fee + +def test_profitability_multiplier_initialization(): + """Test profitability multiplier initialization""" + print("=" * 60) + print("๐Ÿงช TESTING PROFITABILITY MULTIPLIER INITIALIZATION") + print("=" * 60) + + executor = TradingExecutor() + + # Check initial values + initial_multiplier = executor.profitability_reward_multiplier + min_multiplier = executor.min_profitability_multiplier + max_multiplier = executor.max_profitability_multiplier + adjustment_step = executor.profitability_adjustment_step + + print(f"Initial Multiplier: {initial_multiplier} (should be 0.0)") + print(f"Min Multiplier: {min_multiplier} (should be 0.0)") + print(f"Max Multiplier: {max_multiplier} (should be 2.0)") + print(f"Adjustment Step: {adjustment_step} (should be 0.1)") + print() + + # Check thresholds + increase_threshold = executor.success_rate_increase_threshold + decrease_threshold = executor.success_rate_decrease_threshold + trades_window = executor.recent_trades_window + + print(f"Increase Threshold: {increase_threshold:.1%} (should be 60%)") + print(f"Decrease Threshold: {decrease_threshold:.1%} (should be 51%)") + print(f"Trades Window: {trades_window} (should be 20)") + print() + + # Test getter method + multiplier_from_getter = executor.get_profitability_reward_multiplier() + print(f"Multiplier via getter: {multiplier_from_getter}") + print(f"โœ… Getter method: {'PASS' if multiplier_from_getter == initial_multiplier else 'FAIL'}") + + return (initial_multiplier == 0.0 and + min_multiplier == 0.0 and + max_multiplier == 2.0 and + adjustment_step == 0.1) + +def simulate_trades_and_test_adjustment(executor, winning_trades, total_trades): + """Simulate trades and test multiplier adjustment""" + print(f"๐Ÿ“Š Simulating {winning_trades}/{total_trades} winning trades ({winning_trades/total_trades:.1%} success rate)") + + # Clear existing trade records + executor.trade_records = [] + + # Create simulated trade records + base_time = datetime.now() - timedelta(hours=1) + + for i in range(total_trades): + # Create winning or losing trade based on ratio + is_winning = i < winning_trades + pnl = 10.0 if is_winning else -5.0 # $10 profit or $5 loss + + trade_record = TradeRecord( + symbol="ETH/USDT", + side="LONG", + quantity=0.01, + entry_price=3000.0, + exit_price=3010.0 if is_winning else 2995.0, + entry_time=base_time + timedelta(minutes=i*2), + exit_time=base_time + timedelta(minutes=i*2+1), + pnl=pnl, + fees=2.0, + confidence=0.8, + net_pnl=pnl - 2.0 # After fees + ) + + executor.trade_records.append(trade_record) + + # Force adjustment by setting last adjustment time to past + executor.last_profitability_adjustment = datetime.now() - timedelta(minutes=10) + + # Get initial multiplier + initial_multiplier = executor.get_profitability_reward_multiplier() + + # Calculate success rate + success_rate = executor._calculate_recent_success_rate() + print(f"Calculated success rate: {success_rate:.1%}") + + # Trigger adjustment + executor._adjust_profitability_reward_multiplier() + + # Get new multiplier + new_multiplier = executor.get_profitability_reward_multiplier() + + print(f"Initial multiplier: {initial_multiplier:.1f}") + print(f"New multiplier: {new_multiplier:.1f}") + + # Determine expected change + if success_rate > executor.success_rate_increase_threshold: + expected_change = "increase" + expected_new = min(executor.max_profitability_multiplier, initial_multiplier + executor.profitability_adjustment_step) + elif success_rate < executor.success_rate_decrease_threshold: + expected_change = "decrease" + expected_new = max(executor.min_profitability_multiplier, initial_multiplier - executor.profitability_adjustment_step) + else: + expected_change = "no change" + expected_new = initial_multiplier + + print(f"Expected change: {expected_change}") + print(f"Expected new value: {expected_new:.1f}") + + success = abs(new_multiplier - expected_new) < 0.01 + print(f"โœ… Adjustment: {'PASS' if success else 'FAIL'}") + print() + + return success + +def test_orchestrator_integration(): + """Test orchestrator integration with profitability multiplier""" + print("=" * 60) + print("๐Ÿงช TESTING ORCHESTRATOR INTEGRATION") + print("=" * 60) + + # Create components + data_provider = DataProvider() + executor = TradingExecutor() + orchestrator = TradingOrchestrator(data_provider=data_provider) + + # Connect executor to orchestrator + orchestrator.set_trading_executor(executor) + + # Set a test multiplier + executor.profitability_reward_multiplier = 1.5 + + # Test getting multiplier through orchestrator + multiplier = orchestrator.get_profitability_reward_multiplier() + print(f"Multiplier via orchestrator: {multiplier}") + print(f"โœ… Orchestrator getter: {'PASS' if multiplier == 1.5 else 'FAIL'}") + + # Test enhanced reward calculation + base_pnl = 100.0 # $100 profit + confidence = 0.8 + + enhanced_reward = orchestrator.calculate_enhanced_reward(base_pnl, confidence) + expected_enhanced = base_pnl * (1.0 + 1.5) # 100 * 2.5 = 250 + + print(f"Base P&L: ${base_pnl:.2f}") + print(f"Enhanced reward: ${enhanced_reward:.2f}") + print(f"Expected: ${expected_enhanced:.2f}") + print(f"โœ… Enhanced reward: {'PASS' if abs(enhanced_reward - expected_enhanced) < 0.01 else 'FAIL'}") + + # Test with losing trade (should not be enhanced) + losing_pnl = -50.0 + enhanced_losing = orchestrator.calculate_enhanced_reward(losing_pnl, confidence) + print(f"Losing P&L: ${losing_pnl:.2f}") + print(f"Enhanced losing: ${enhanced_losing:.2f}") + print(f"โœ… No enhancement for losses: {'PASS' if enhanced_losing == losing_pnl else 'FAIL'}") + + return multiplier == 1.5 and abs(enhanced_reward - expected_enhanced) < 0.01 + +def main(): + """Run all tests""" + print("๐Ÿš€ DYNAMIC PROFITABILITY REWARD SYSTEM TEST") + print("Testing fee reversion and dynamic reward adjustment") + print() + + all_tests_passed = True + + # Test 1: Fee configuration + try: + fee_test_passed = test_fee_configuration() + all_tests_passed = all_tests_passed and fee_test_passed + except Exception as e: + print(f"โŒ Fee configuration test failed: {e}") + all_tests_passed = False + + # Test 2: Profitability multiplier initialization + try: + init_test_passed = test_profitability_multiplier_initialization() + all_tests_passed = all_tests_passed and init_test_passed + except Exception as e: + print(f"โŒ Initialization test failed: {e}") + all_tests_passed = False + + # Test 3: Multiplier adjustment scenarios + print("=" * 60) + print("๐Ÿงช TESTING MULTIPLIER ADJUSTMENT SCENARIOS") + print("=" * 60) + + executor = TradingExecutor() + + try: + # Scenario 1: High success rate (should increase multiplier) + print("Scenario 1: High success rate (65% - should increase)") + high_success_test = simulate_trades_and_test_adjustment(executor, 13, 20) # 65% + all_tests_passed = all_tests_passed and high_success_test + + # Scenario 2: Low success rate (should decrease multiplier) + print("Scenario 2: Low success rate (45% - should decrease)") + low_success_test = simulate_trades_and_test_adjustment(executor, 9, 20) # 45% + all_tests_passed = all_tests_passed and low_success_test + + # Scenario 3: Medium success rate (should not change) + print("Scenario 3: Medium success rate (55% - should not change)") + medium_success_test = simulate_trades_and_test_adjustment(executor, 11, 20) # 55% + all_tests_passed = all_tests_passed and medium_success_test + + except Exception as e: + print(f"โŒ Adjustment scenario tests failed: {e}") + all_tests_passed = False + + # Test 4: Orchestrator integration + try: + orchestrator_test_passed = test_orchestrator_integration() + all_tests_passed = all_tests_passed and orchestrator_test_passed + except Exception as e: + print(f"โŒ Orchestrator integration test failed: {e}") + all_tests_passed = False + + # Final results + print("=" * 60) + print("๐Ÿ“‹ TEST RESULTS SUMMARY") + print("=" * 60) + + if all_tests_passed: + print("๐ŸŽ‰ ALL TESTS PASSED!") + print("โœ… Fees reverted to normal 0.1%") + print("โœ… Dynamic profitability multiplier working") + print("โœ… Success rate calculation accurate") + print("โœ… Orchestrator integration functional") + print() + print("๐Ÿš€ System ready for trading with dynamic profitability rewards!") + print("๐Ÿ“ˆ The model will learn to prioritize more profitable trades over time") + print("๐ŸŽฏ Success rate >60% โ†’ increase reward multiplier") + print("โš ๏ธ Success rate <51% โ†’ decrease reward multiplier") + else: + print("โŒ SOME TESTS FAILED!") + print("Please check the error messages above and fix issues before trading.") + + return all_tests_passed + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/web/clean_dashboard.py b/web/clean_dashboard.py index e048ccc..3074796 100644 --- a/web/clean_dashboard.py +++ b/web/clean_dashboard.py @@ -496,6 +496,7 @@ class CleanTradingDashboard: Output('current-position', 'children'), Output('trade-count', 'children'), Output('portfolio-value', 'children'), + Output('profitability-multiplier', 'children'), Output('mexc-status', 'children')], [Input('interval-component', 'n_intervals')] ) @@ -600,6 +601,20 @@ class CleanTradingDashboard: portfolio_value = current_balance + total_session_pnl # Live balance + unrealized P&L portfolio_str = f"${portfolio_value:.2f}" + # Profitability multiplier - get from trading executor + profitability_multiplier = 0.0 + success_rate = 0.0 + if self.trading_executor and hasattr(self.trading_executor, 'get_profitability_reward_multiplier'): + profitability_multiplier = self.trading_executor.get_profitability_reward_multiplier() + if hasattr(self.trading_executor, '_calculate_recent_success_rate'): + success_rate = self.trading_executor._calculate_recent_success_rate() + + # Format profitability multiplier display + if profitability_multiplier > 0: + multiplier_str = f"+{profitability_multiplier:.1f}x ({success_rate:.0%})" + else: + multiplier_str = f"0.0x ({success_rate:.0%})" if success_rate > 0 else "0.0x" + # MEXC status - enhanced with sync status mexc_status = "SIM" if self.trading_executor: @@ -607,11 +622,11 @@ class CleanTradingDashboard: if hasattr(self.trading_executor, 'simulation_mode') and not self.trading_executor.simulation_mode: mexc_status = "LIVE+SYNC" # Indicate live trading with position sync - return price_str, session_pnl_str, position_str, trade_str, portfolio_str, mexc_status + return price_str, session_pnl_str, position_str, trade_str, portfolio_str, multiplier_str, mexc_status except Exception as e: logger.error(f"Error updating metrics: {e}") - return "Error", "$0.00", "Error", "0", "$100.00", "ERROR" + return "Error", "$0.00", "Error", "0", "$100.00", "0.0x", "ERROR" @self.app.callback( Output('recent-decisions', 'children'), diff --git a/web/layout_manager.py b/web/layout_manager.py index 172cb2a..302105e 100644 --- a/web/layout_manager.py +++ b/web/layout_manager.py @@ -93,6 +93,7 @@ class DashboardLayoutManager: # ("leverage-info", "Leverage", "text-primary"), ("trade-count", "Trades", "text-warning"), ("portfolio-value", "Portfolio", "text-secondary"), + ("profitability-multiplier", "Profit Boost", "text-primary"), ("mexc-status", f"{exchange_name} API", "text-info") ]