sync fees from API. usdc. trade works

This commit is contained in:
Dobromir Popov
2025-05-28 11:18:07 +03:00
parent dd86d21854
commit d6a71c2b1a
15 changed files with 2638 additions and 109 deletions

320
core/config_sync.py Normal file
View File

@ -0,0 +1,320 @@
"""
Config Synchronization Module
This module handles automatic synchronization of trading fees and other
parameters between the MEXC API and the local configuration files.
"""
import logging
import os
import yaml
import json
from datetime import datetime, timedelta
from typing import Dict, Any, Optional
from pathlib import Path
import time
logger = logging.getLogger(__name__)
class ConfigSynchronizer:
"""Handles automatic synchronization of config parameters with MEXC API"""
def __init__(self, config_path: str = "config.yaml", mexc_interface=None):
"""Initialize the config synchronizer
Args:
config_path: Path to the main config file
mexc_interface: MEXCInterface instance for API calls
"""
self.config_path = config_path
self.mexc_interface = mexc_interface
self.last_sync_time = None
self.sync_interval = 3600 # Sync every hour by default
self.backup_enabled = True
# Track sync history
self.sync_history_path = "logs/config_sync_history.json"
self.sync_history = self._load_sync_history()
def _load_sync_history(self) -> list:
"""Load sync history from file"""
try:
if os.path.exists(self.sync_history_path):
with open(self.sync_history_path, 'r') as f:
return json.load(f)
except Exception as e:
logger.warning(f"Could not load sync history: {e}")
return []
def _save_sync_history(self, sync_record: Dict[str, Any]):
"""Save sync record to history"""
try:
self.sync_history.append(sync_record)
# Keep only last 100 sync records
self.sync_history = self.sync_history[-100:]
# Ensure logs directory exists
os.makedirs(os.path.dirname(self.sync_history_path), exist_ok=True)
with open(self.sync_history_path, 'w') as f:
json.dump(self.sync_history, f, indent=2, default=str)
except Exception as e:
logger.error(f"Could not save sync history: {e}")
def _load_config(self) -> Dict[str, Any]:
"""Load current configuration from file"""
try:
with open(self.config_path, 'r') as f:
return yaml.safe_load(f)
except Exception as e:
logger.error(f"Error loading config from {self.config_path}: {e}")
return {}
def _save_config(self, config: Dict[str, Any], backup: bool = True) -> bool:
"""Save configuration to file with optional backup
Args:
config: Configuration dictionary to save
backup: Whether to create a backup of the existing config
Returns:
bool: True if save successful, False otherwise
"""
try:
# Create backup if requested
if backup and self.backup_enabled and os.path.exists(self.config_path):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{self.config_path}.backup_{timestamp}"
import shutil
shutil.copy2(self.config_path, backup_path)
logger.info(f"Created config backup: {backup_path}")
# Save new config
with open(self.config_path, 'w') as f:
yaml.dump(config, f, indent=2, default_flow_style=False)
logger.info(f"Config saved successfully to {self.config_path}")
return True
except Exception as e:
logger.error(f"Error saving config to {self.config_path}: {e}")
return False
def sync_trading_fees(self, force: bool = False) -> Dict[str, Any]:
"""Sync trading fees from MEXC API to config
Args:
force: Force sync even if last sync was recent
Returns:
dict: Sync result with status and details
"""
sync_record = {
'timestamp': datetime.now().isoformat(),
'type': 'trading_fees',
'status': 'started',
'changes': {},
'api_response': {},
'errors': []
}
try:
# Check if sync is needed
if not force and self.last_sync_time:
time_since_sync = datetime.now() - self.last_sync_time
if time_since_sync.total_seconds() < self.sync_interval:
sync_record['status'] = 'skipped'
sync_record['reason'] = f'Last sync was {time_since_sync.total_seconds():.0f}s ago'
logger.info(f"CONFIG SYNC: Skipping sync, last sync was recent")
return sync_record
if not self.mexc_interface:
sync_record['status'] = 'error'
sync_record['errors'].append('No MEXC interface available')
logger.error("CONFIG SYNC: No MEXC interface available for fee sync")
return sync_record
# Get current fees from MEXC API
logger.info("CONFIG SYNC: Fetching trading fees from MEXC API")
api_fees = self.mexc_interface.get_trading_fees()
sync_record['api_response'] = api_fees
if api_fees.get('source') == 'fallback':
sync_record['status'] = 'warning'
sync_record['errors'].append('API returned fallback values')
logger.warning("CONFIG SYNC: API returned fallback fee values")
# Load current config
config = self._load_config()
if not config:
sync_record['status'] = 'error'
sync_record['errors'].append('Could not load current config')
return sync_record
# Update trading fees in config
changes_made = False
# Ensure trading fees section exists
if 'trading' not in config:
config['trading'] = {}
if 'trading_fees' not in config['trading']:
config['trading']['trading_fees'] = {}
# Check and update maker fee
current_maker = config['trading']['trading_fees'].get('maker', 0.0)
new_maker = api_fees.get('maker_rate', current_maker)
if abs(current_maker - new_maker) > 0.000001: # Significant difference
config['trading']['trading_fees']['maker'] = new_maker
sync_record['changes']['maker_fee'] = {
'old': current_maker,
'new': new_maker,
'change_percent': f"{((new_maker - current_maker) / max(current_maker, 0.000001)) * 100:.2f}%"
}
changes_made = True
logger.info(f"CONFIG SYNC: Updated maker fee: {current_maker:.4f} -> {new_maker:.4f}")
# Check and update taker fee
current_taker = config['trading']['trading_fees'].get('taker', 0.0005)
new_taker = api_fees.get('taker_rate', current_taker)
if abs(current_taker - new_taker) > 0.000001: # Significant difference
config['trading']['trading_fees']['taker'] = new_taker
sync_record['changes']['taker_fee'] = {
'old': current_taker,
'new': new_taker,
'change_percent': f"{((new_taker - current_taker) / max(current_taker, 0.000001)) * 100:.2f}%"
}
changes_made = True
logger.info(f"CONFIG SYNC: Updated taker fee: {current_taker:.4f} -> {new_taker:.4f}")
# Update default fee to match taker fee
current_default = config['trading']['trading_fees'].get('default', 0.0005)
if abs(current_default - new_taker) > 0.000001:
config['trading']['trading_fees']['default'] = new_taker
sync_record['changes']['default_fee'] = {
'old': current_default,
'new': new_taker
}
changes_made = True
logger.info(f"CONFIG SYNC: Updated default fee: {current_default:.4f} -> {new_taker:.4f}")
# Add sync metadata
if 'fee_sync_metadata' not in config['trading']:
config['trading']['fee_sync_metadata'] = {}
config['trading']['fee_sync_metadata'] = {
'last_sync': datetime.now().isoformat(),
'api_source': 'mexc',
'sync_enabled': True,
'api_commission_rates': {
'maker': api_fees.get('maker_commission', 0),
'taker': api_fees.get('taker_commission', 0)
}
}
# Save config if changes were made
if changes_made or 'fee_sync_metadata' not in config.get('trading', {}):
if self._save_config(config, backup=True):
sync_record['status'] = 'success'
sync_record['changes_made'] = changes_made
self.last_sync_time = datetime.now()
logger.info(f"CONFIG SYNC: Successfully synced trading fees")
if changes_made:
logger.info(f"CONFIG SYNC: Changes made: {list(sync_record['changes'].keys())}")
else:
logger.info("CONFIG SYNC: No fee changes needed, metadata updated")
else:
sync_record['status'] = 'error'
sync_record['errors'].append('Failed to save updated config')
else:
sync_record['status'] = 'no_changes'
sync_record['changes_made'] = False
self.last_sync_time = datetime.now()
logger.info("CONFIG SYNC: No changes needed, fees are already current")
except Exception as e:
sync_record['status'] = 'error'
sync_record['errors'].append(str(e))
logger.error(f"CONFIG SYNC: Error during fee sync: {e}")
finally:
# Save sync record to history
self._save_sync_history(sync_record)
return sync_record
def auto_sync_fees(self) -> bool:
"""Automatically sync fees if conditions are met
Returns:
bool: True if sync was performed, False if skipped
"""
try:
# Check if auto-sync is enabled in config
config = self._load_config()
auto_sync_enabled = config.get('trading', {}).get('fee_sync_metadata', {}).get('sync_enabled', True)
if not auto_sync_enabled:
logger.debug("CONFIG SYNC: Auto-sync is disabled")
return False
# Perform sync
result = self.sync_trading_fees(force=False)
return result.get('status') in ['success', 'no_changes']
except Exception as e:
logger.error(f"CONFIG SYNC: Error in auto-sync: {e}")
return False
def get_sync_status(self) -> Dict[str, Any]:
"""Get current sync status and history
Returns:
dict: Sync status information
"""
try:
config = self._load_config()
metadata = config.get('trading', {}).get('fee_sync_metadata', {})
# Get latest sync from history
latest_sync = self.sync_history[-1] if self.sync_history else None
return {
'sync_enabled': metadata.get('sync_enabled', True),
'last_sync': metadata.get('last_sync'),
'api_source': metadata.get('api_source'),
'sync_interval_seconds': self.sync_interval,
'latest_sync_result': latest_sync,
'total_syncs': len(self.sync_history),
'mexc_interface_available': self.mexc_interface is not None
}
except Exception as e:
logger.error(f"Error getting sync status: {e}")
return {'error': str(e)}
def enable_auto_sync(self, enabled: bool = True):
"""Enable or disable automatic fee synchronization
Args:
enabled: Whether to enable auto-sync
"""
try:
config = self._load_config()
if 'trading' not in config:
config['trading'] = {}
if 'fee_sync_metadata' not in config['trading']:
config['trading']['fee_sync_metadata'] = {}
config['trading']['fee_sync_metadata']['sync_enabled'] = enabled
if self._save_config(config, backup=False):
logger.info(f"CONFIG SYNC: Auto-sync {'enabled' if enabled else 'disabled'}")
else:
logger.error("CONFIG SYNC: Failed to update auto-sync setting")
except Exception as e:
logger.error(f"CONFIG SYNC: Error updating auto-sync setting: {e}")

View File

@ -355,7 +355,7 @@ class DataProvider:
except Exception as e:
logger.error(f"❌ MEXC: Error fetching data: {e}")
return None
def _fetch_from_binance(self, symbol: str, timeframe: str, limit: int) -> Optional[pd.DataFrame]:
"""Fetch data from Binance API (primary data source)"""
try:

View File

@ -19,6 +19,7 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'NN'))
from NN.exchanges import MEXCInterface
from .config import get_config
from .config_sync import ConfigSynchronizer
logger = logging.getLogger(__name__)
@ -109,6 +110,35 @@ class TradingExecutor:
# Connect to exchange
if self.trading_enabled:
self._connect_exchange()
logger.info(f"Trading Executor initialized - Mode: {self.trading_mode}, Enabled: {self.trading_enabled}")
# Initialize config synchronizer for automatic fee updates
self.config_synchronizer = ConfigSynchronizer(
config_path=config_path,
mexc_interface=self.exchange if self.trading_enabled else None
)
# Perform initial fee sync on startup if trading is enabled
if self.trading_enabled and self.exchange:
try:
logger.info("TRADING EXECUTOR: Performing initial fee synchronization with MEXC API")
sync_result = self.config_synchronizer.sync_trading_fees(force=True)
if sync_result.get('status') == 'success':
logger.info("TRADING EXECUTOR: Fee synchronization completed successfully")
if sync_result.get('changes_made'):
logger.info(f"TRADING EXECUTOR: Fee changes applied: {list(sync_result['changes'].keys())}")
# Reload config to get updated fees
self.config = get_config(config_path)
self.mexc_config = self.config.get('mexc_trading', {})
elif sync_result.get('status') == 'warning':
logger.warning("TRADING EXECUTOR: Fee sync completed with warnings")
else:
logger.warning(f"TRADING EXECUTOR: Fee sync failed: {sync_result.get('status')}")
except Exception as e:
logger.warning(f"TRADING EXECUTOR: Initial fee sync failed: {e}")
logger.info(f"Trading Executor initialized - Mode: {self.trading_mode}, Enabled: {self.trading_enabled}")
def _connect_exchange(self) -> bool:
"""Connect to the MEXC exchange"""
@ -242,12 +272,22 @@ class TradingExecutor:
return True
try:
# Place market buy order
# Get order type from config
order_type = self.mexc_config.get('order_type', 'market').lower()
# For limit orders, set price slightly above market for immediate execution
limit_price = None
if order_type == 'limit':
# Set buy price slightly above market to ensure immediate execution
limit_price = current_price * 1.001 # 0.1% above market
# Place buy order
order = self.exchange.place_order(
symbol=symbol,
side='buy',
order_type='market',
quantity=quantity
order_type=order_type,
quantity=quantity,
price=limit_price
)
if order:
@ -317,18 +357,28 @@ class TradingExecutor:
return True
try:
# Place market sell order
# Get order type from config
order_type = self.mexc_config.get('order_type', 'market').lower()
# For limit orders, set price slightly below market for immediate execution
limit_price = None
if order_type == 'limit':
# Set sell price slightly below market to ensure immediate execution
limit_price = current_price * 0.999 # 0.1% below market
# Place sell order
order = self.exchange.place_order(
symbol=symbol,
side='sell',
order_type='market',
quantity=position.quantity
order_type=order_type,
quantity=position.quantity,
price=limit_price
)
if order:
# Calculate P&L
pnl = position.calculate_pnl(current_price)
fees = current_price * position.quantity * self.mexc_config.get('trading_fee', 0.0002)
fees = self._calculate_trading_fee(order, symbol, position.quantity, current_price)
# Create trade record
trade_record = TradeRecord(
@ -389,19 +439,58 @@ class TradingExecutor:
return self.trade_history.copy()
def get_daily_stats(self) -> Dict[str, Any]:
"""Get daily trading statistics"""
"""Get daily trading statistics with enhanced fee analysis"""
total_pnl = sum(trade.pnl for trade in self.trade_history)
total_fees = sum(trade.fees for trade in self.trade_history)
gross_pnl = total_pnl + total_fees # P&L before fees
winning_trades = len([t for t in self.trade_history if t.pnl > 0])
losing_trades = len([t for t in self.trade_history if t.pnl < 0])
total_trades = len(self.trade_history)
# Calculate average trade values
avg_trade_pnl = total_pnl / max(1, total_trades)
avg_trade_fee = total_fees / max(1, total_trades)
avg_winning_trade = sum(t.pnl for t in self.trade_history if t.pnl > 0) / max(1, winning_trades)
avg_losing_trade = sum(t.pnl for t in self.trade_history if t.pnl < 0) / max(1, losing_trades)
# Enhanced fee analysis from config
fee_structure = self.mexc_config.get('trading_fees', {})
maker_fee_rate = fee_structure.get('maker', 0.0000)
taker_fee_rate = fee_structure.get('taker', 0.0005)
default_fee_rate = fee_structure.get('default', 0.0005)
# Calculate fee efficiency
total_volume = sum(trade.quantity * trade.exit_price for trade in self.trade_history)
effective_fee_rate = (total_fees / max(0.01, total_volume)) if total_volume > 0 else 0
fee_impact_on_pnl = (total_fees / max(0.01, abs(gross_pnl))) * 100 if gross_pnl != 0 else 0
return {
'daily_trades': self.daily_trades,
'daily_loss': self.daily_loss,
'total_pnl': total_pnl,
'gross_pnl': gross_pnl,
'total_fees': total_fees,
'winning_trades': winning_trades,
'losing_trades': losing_trades,
'win_rate': winning_trades / max(1, len(self.trade_history)),
'positions_count': len(self.positions)
'total_trades': total_trades,
'win_rate': winning_trades / max(1, total_trades),
'avg_trade_pnl': avg_trade_pnl,
'avg_trade_fee': avg_trade_fee,
'avg_winning_trade': avg_winning_trade,
'avg_losing_trade': avg_losing_trade,
'positions_count': len(self.positions),
'fee_rates': {
'maker': f"{maker_fee_rate*100:.3f}%",
'taker': f"{taker_fee_rate*100:.3f}%",
'default': f"{default_fee_rate*100:.3f}%",
'effective': f"{effective_fee_rate*100:.3f}%" # Actual rate based on trades
},
'fee_analysis': {
'total_volume': total_volume,
'fee_impact_percent': fee_impact_on_pnl,
'is_fee_efficient': fee_impact_on_pnl < 5.0, # Less than 5% impact is good
'fee_savings_vs_market': (0.001 - effective_fee_rate) * total_volume if effective_fee_rate < 0.001 else 0
}
}
def emergency_stop(self):
@ -467,3 +556,250 @@ class TradingExecutor:
except Exception as e:
logger.error(f"Error getting account balance: {e}")
return {}
def _calculate_trading_fee(self, order_result: Dict[str, Any], symbol: str,
quantity: float, price: float) -> float:
"""Calculate trading fee based on order execution details with enhanced MEXC API support
Args:
order_result: Order result from exchange API
symbol: Trading symbol
quantity: Order quantity
price: Execution price
Returns:
float: Trading fee amount in quote currency
"""
try:
# 1. Try to get actual fee from API response (most accurate)
# MEXC API can return fees in different formats depending on the endpoint
# Check for 'fills' array (most common for filled orders)
if order_result and 'fills' in order_result:
total_commission = 0.0
commission_asset = None
for fill in order_result['fills']:
commission = float(fill.get('commission', 0))
commission_asset = fill.get('commissionAsset', '')
total_commission += commission
if total_commission > 0:
logger.info(f"Using actual API fee from fills: {total_commission} {commission_asset}")
# If commission is in different asset, we might need conversion
# For now, assume it's in quote currency (USDC/USDT)
return total_commission
# 2. Check if order result has fee information directly
fee_fields = ['fee', 'commission', 'tradeFee', 'fees']
for field in fee_fields:
if order_result and field in order_result:
fee = float(order_result[field])
if fee > 0:
logger.info(f"Using API fee field '{field}': {fee}")
return fee
# 3. Check for executedQty and cummulativeQuoteQty for more accurate calculation
if order_result and 'executedQty' in order_result and 'cummulativeQuoteQty' in order_result:
executed_qty = float(order_result['executedQty'])
executed_value = float(order_result['cummulativeQuoteQty'])
if executed_qty > 0 and executed_value > 0:
# Use executed values instead of provided price/quantity
quantity = executed_qty
price = executed_value / executed_qty
logger.info(f"Using executed order data: {quantity} @ {price:.6f}")
# 4. Fall back to config-based fee calculation with enhanced logic
trading_fees = self.mexc_config.get('trading_fees', {})
# Determine if this was a maker or taker trade
order_type = order_result.get('type', 'MARKET') if order_result else 'MARKET'
order_status = order_result.get('status', 'UNKNOWN') if order_result else 'UNKNOWN'
time_in_force = order_result.get('timeInForce', 'GTC') if order_result else 'GTC'
# Enhanced maker/taker detection logic
if order_type.upper() == 'LIMIT':
# For limit orders, check execution speed and market conditions
if order_status == 'FILLED':
# If it's an IOC (Immediate or Cancel) order, it's likely a taker
if time_in_force == 'IOC' or time_in_force == 'FOK':
fee_rate = trading_fees.get('taker', 0.0005)
logger.info(f"Using taker fee rate for {time_in_force} limit order: {fee_rate*100:.3f}%")
else:
# For GTC orders, assume taker if aggressive pricing is used
# This is a heuristic based on our trading strategy
fee_rate = trading_fees.get('taker', 0.0005)
logger.info(f"Using taker fee rate for aggressive limit order: {fee_rate*100:.3f}%")
else:
# If not immediately filled, likely a maker (though we don't usually reach here)
fee_rate = trading_fees.get('maker', 0.0000)
logger.info(f"Using maker fee rate for pending/partial limit order: {fee_rate*100:.3f}%")
elif order_type.upper() == 'LIMIT_MAKER':
# LIMIT_MAKER orders are guaranteed to be makers
fee_rate = trading_fees.get('maker', 0.0000)
logger.info(f"Using maker fee rate for LIMIT_MAKER order: {fee_rate*100:.3f}%")
else:
# Market orders and other types are always takers
fee_rate = trading_fees.get('taker', 0.0005)
logger.info(f"Using taker fee rate for {order_type} order: {fee_rate*100:.3f}%")
# Calculate fee amount
trade_value = quantity * price
fee_amount = trade_value * fee_rate
logger.info(f"Calculated fee: ${fee_amount:.6f} ({fee_rate*100:.3f}% of ${trade_value:.2f})")
return fee_amount
except Exception as e:
logger.warning(f"Error calculating trading fee: {e}")
# Ultimate fallback using default rate
default_fee_rate = self.mexc_config.get('trading_fees', {}).get('default', 0.0005)
fallback_rate = self.mexc_config.get('trading_fee', default_fee_rate) # Legacy support
fee_amount = quantity * price * fallback_rate
logger.info(f"Using fallback fee: ${fee_amount:.6f} ({fallback_rate*100:.3f}%)")
return fee_amount
def get_fee_analysis(self) -> Dict[str, Any]:
"""Get detailed fee analysis and statistics
Returns:
Dict with fee breakdowns, rates, and impact analysis
"""
try:
fee_structure = self.mexc_config.get('trading_fees', {})
maker_rate = fee_structure.get('maker', 0.0000)
taker_rate = fee_structure.get('taker', 0.0005)
default_rate = fee_structure.get('default', 0.0005)
# Calculate total fees paid
total_fees = sum(trade.fees for trade in self.trade_history)
total_volume = sum(trade.quantity * trade.exit_price for trade in self.trade_history)
# Estimate fee breakdown (since we don't track maker vs taker separately)
# Assume most of our limit orders are takers due to our pricing strategy
estimated_taker_volume = total_volume * 0.9 # 90% taker assumption
estimated_maker_volume = total_volume * 0.1 # 10% maker assumption
estimated_taker_fees = estimated_taker_volume * taker_rate
estimated_maker_fees = estimated_maker_volume * maker_rate
# Fee impact analysis
total_pnl = sum(trade.pnl for trade in self.trade_history)
gross_pnl = total_pnl + total_fees
fee_impact_percent = (total_fees / max(1, abs(gross_pnl))) * 100 if gross_pnl != 0 else 0
return {
'fee_rates': {
'maker': {
'rate': maker_rate,
'rate_percent': f"{maker_rate*100:.3f}%"
},
'taker': {
'rate': taker_rate,
'rate_percent': f"{taker_rate*100:.3f}%"
},
'default': {
'rate': default_rate,
'rate_percent': f"{default_rate*100:.3f}%"
}
},
'total_fees': total_fees,
'total_volume': total_volume,
'estimated_breakdown': {
'taker_fees': estimated_taker_fees,
'maker_fees': estimated_maker_fees,
'taker_volume': estimated_taker_volume,
'maker_volume': estimated_maker_volume
},
'impact_analysis': {
'fee_impact_percent': fee_impact_percent,
'pnl_after_fees': total_pnl,
'pnl_before_fees': gross_pnl,
'avg_fee_per_trade': total_fees / max(1, len(self.trade_history))
},
'fee_efficiency': {
'volume_to_fee_ratio': total_volume / max(0.01, total_fees),
'is_efficient': fee_impact_percent < 5.0 # Less than 5% impact is good
}
}
except Exception as e:
logger.error(f"Error calculating fee analysis: {e}")
return {
'error': str(e),
'fee_rates': {
'maker': {'rate': 0.0000, 'rate_percent': '0.000%'},
'taker': {'rate': 0.0005, 'rate_percent': '0.050%'}
}
}
def sync_fees_with_api(self, force: bool = False) -> Dict[str, Any]:
"""Manually trigger fee synchronization with MEXC API
Args:
force: Force sync even if last sync was recent
Returns:
dict: Sync result with status and details
"""
if not self.config_synchronizer:
return {
'status': 'error',
'error': 'Config synchronizer not initialized'
}
try:
logger.info("TRADING EXECUTOR: Manual fee sync requested")
sync_result = self.config_synchronizer.sync_trading_fees(force=force)
# If fees were updated, reload config
if sync_result.get('changes_made'):
logger.info("TRADING EXECUTOR: Reloading config after fee sync")
self.config = get_config(self.config_synchronizer.config_path)
self.mexc_config = self.config.get('mexc_trading', {})
return sync_result
except Exception as e:
logger.error(f"TRADING EXECUTOR: Error in manual fee sync: {e}")
return {
'status': 'error',
'error': str(e)
}
def auto_sync_fees_if_needed(self) -> bool:
"""Automatically sync fees if needed (called periodically)
Returns:
bool: True if sync was performed successfully
"""
if not self.config_synchronizer:
return False
try:
return self.config_synchronizer.auto_sync_fees()
except Exception as e:
logger.error(f"TRADING EXECUTOR: Error in auto fee sync: {e}")
return False
def get_fee_sync_status(self) -> Dict[str, Any]:
"""Get current fee synchronization status
Returns:
dict: Fee sync status and history
"""
if not self.config_synchronizer:
return {
'sync_available': False,
'error': 'Config synchronizer not initialized'
}
try:
status = self.config_synchronizer.get_sync_status()
status['sync_available'] = True
return status
except Exception as e:
logger.error(f"TRADING EXECUTOR: Error getting sync status: {e}")
return {
'sync_available': False,
'error': str(e)
}