position history with fees

This commit is contained in:
Dobromir Popov
2025-05-28 11:17:41 +03:00
parent f8681447e3
commit dd86d21854
11 changed files with 880 additions and 58 deletions

View File

@ -144,9 +144,14 @@ class DataProvider:
logger.info(f"Preloading 300s of data for {symbol} {timeframe}")
df = self._preload_300s_data(symbol, timeframe)
else:
# Fetch from API with requested limit
# Fetch from API with requested limit (Binance primary, MEXC fallback)
logger.info(f"Fetching historical data for {symbol} {timeframe}")
df = self._fetch_from_binance(symbol, timeframe, limit)
# Fallback to MEXC if Binance fails
if df is None or df.empty:
logger.info(f"Binance failed, trying MEXC fallback for {symbol}")
df = self._fetch_from_mexc(symbol, timeframe, limit)
if df is not None and not df.empty:
# Add technical indicators
@ -217,9 +222,14 @@ class DataProvider:
logger.info(f"Preloading {candles_needed} candles for {symbol} {timeframe} (300s worth)")
# Fetch the data
# Fetch the data (Binance primary, MEXC fallback)
df = self._fetch_from_binance(symbol, timeframe, candles_needed)
# Fallback to MEXC if Binance fails
if df is None or df.empty:
logger.info(f"Binance failed, trying MEXC fallback for preload {symbol}")
df = self._fetch_from_mexc(symbol, timeframe, candles_needed)
if df is not None and not df.empty:
logger.info(f"Successfully preloaded {len(df)} candles for {symbol} {timeframe}")
return df
@ -289,8 +299,65 @@ class DataProvider:
logger.error(f"Error in preload_all_symbols_data: {e}")
return {}
def _fetch_from_mexc(self, symbol: str, timeframe: str, limit: int) -> Optional[pd.DataFrame]:
"""Fetch data from MEXC API (fallback data source when Binance is unavailable)"""
try:
# MEXC doesn't support 1s intervals
if timeframe == '1s':
logger.warning(f"MEXC doesn't support 1s intervals, skipping {symbol}")
return None
# Convert symbol format
mexc_symbol = symbol.replace('/', '').upper()
# Convert timeframe for MEXC (excluding 1s)
timeframe_map = {
'1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m',
'1h': '1h', '4h': '4h', '1d': '1d'
}
mexc_timeframe = timeframe_map.get(timeframe)
if mexc_timeframe is None:
logger.warning(f"MEXC doesn't support timeframe {timeframe}, skipping {symbol}")
return None
# MEXC API request
url = "https://api.mexc.com/api/v3/klines"
params = {
'symbol': mexc_symbol,
'interval': mexc_timeframe,
'limit': limit
}
response = requests.get(url, params=params)
response.raise_for_status()
data = response.json()
# Convert to DataFrame (MEXC uses 8 columns vs Binance's 12)
df = pd.DataFrame(data, columns=[
'timestamp', 'open', 'high', 'low', 'close', 'volume',
'close_time', 'quote_volume'
])
# Process columns
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
for col in ['open', 'high', 'low', 'close', 'volume']:
df[col] = df[col].astype(float)
# Keep only OHLCV columns
df = df[['timestamp', 'open', 'high', 'low', 'close', 'volume']]
df = df.sort_values('timestamp').reset_index(drop=True)
logger.info(f"✅ MEXC: Fetched {len(df)} candles for {symbol} {timeframe}")
return df
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"""
"""Fetch data from Binance API (primary data source)"""
try:
# Convert symbol format
binance_symbol = symbol.replace('/', '').upper()
@ -331,7 +398,7 @@ class DataProvider:
df = df[['timestamp', 'open', 'high', 'low', 'close', 'volume']]
df = df.sort_values('timestamp').reset_index(drop=True)
logger.info(f"Fetched {len(df)} candles for {symbol} {timeframe}")
logger.info(f"Binance: Fetched {len(df)} candles for {symbol} {timeframe}")
return df
except Exception as e:

View File

@ -67,10 +67,28 @@ class TradingExecutor:
api_key = os.getenv('MEXC_API_KEY', self.mexc_config.get('api_key', ''))
api_secret = os.getenv('MEXC_SECRET_KEY', self.mexc_config.get('api_secret', ''))
# Determine trading mode from unified config
trading_mode = self.mexc_config.get('trading_mode', 'simulation')
# Map trading mode to exchange test_mode and execution mode
if trading_mode == 'simulation':
exchange_test_mode = True
self.simulation_mode = True
elif trading_mode == 'testnet':
exchange_test_mode = True
self.simulation_mode = False
elif trading_mode == 'live':
exchange_test_mode = False
self.simulation_mode = False
else:
logger.warning(f"Unknown trading_mode '{trading_mode}', defaulting to simulation")
exchange_test_mode = True
self.simulation_mode = True
self.exchange = MEXCInterface(
api_key=api_key,
api_secret=api_secret,
test_mode=self.mexc_config.get('test_mode', True)
test_mode=exchange_test_mode
)
# Trading state
@ -80,7 +98,10 @@ class TradingExecutor:
self.daily_loss = 0.0
self.last_trade_time = {}
self.trading_enabled = self.mexc_config.get('enabled', False)
self.dry_run = self.mexc_config.get('dry_run_mode', True)
self.trading_mode = trading_mode
# Legacy compatibility (deprecated)
self.dry_run = self.simulation_mode
# Thread safety
self.lock = Lock()
@ -98,7 +119,8 @@ class TradingExecutor:
return True
else:
logger.error("Failed to connect to MEXC exchange")
self.trading_enabled = False
if not self.dry_run:
self.trading_enabled = False
return False
except Exception as e:
logger.error(f"Error connecting to MEXC exchange: {e}")
@ -204,8 +226,8 @@ class TradingExecutor:
logger.info(f"Executing BUY: {quantity:.6f} {symbol} at ${current_price:.2f} "
f"(value: ${position_value:.2f}, confidence: {confidence:.2f})")
if self.dry_run:
logger.info("DRY RUN MODE - Trade logged but not executed")
if self.simulation_mode:
logger.info(f"SIMULATION MODE ({self.trading_mode.upper()}) - Trade logged but not executed")
# Create mock position for tracking
self.positions[symbol] = Position(
symbol=symbol,
@ -213,7 +235,7 @@ class TradingExecutor:
quantity=quantity,
entry_price=current_price,
entry_time=datetime.now(),
order_id=f"dry_run_{int(time.time())}"
order_id=f"sim_{int(time.time())}"
)
self.last_trade_time[symbol] = datetime.now()
self.daily_trades += 1
@ -264,8 +286,8 @@ class TradingExecutor:
logger.info(f"Executing SELL: {position.quantity:.6f} {symbol} at ${current_price:.2f} "
f"(confidence: {confidence:.2f})")
if self.dry_run:
logger.info("DRY RUN MODE - Trade logged but not executed")
if self.simulation_mode:
logger.info(f"SIMULATION MODE ({self.trading_mode.upper()}) - Trade logged but not executed")
# Calculate P&L
pnl = position.calculate_pnl(current_price)
@ -444,4 +466,4 @@ class TradingExecutor:
except Exception as e:
logger.error(f"Error getting account balance: {e}")
return {}
return {}