Files
quantumbotx/core/data/fetch.py
T
Reynov Christian 78737b6835 Refactor and update bot management and routing
- Update `.gitignore` to include `lab/` for backtesting and raw data.
- Refactor `app.py` to improve error handling and logging.
- Remove deprecated files: `core/bot_logic.py`, `core/bots/base_bot.py`, `core/bots/manager.py`, `core/db/database.py`, `core/routes/api_analysis.py`, `core/routes/api_bots_analysis.py`, `core/strategies/logic_ma.py`, `core/strategies/logic_rsi.py`.
- Modify multiple files to enhance bot management, routing, and strategy handling.
- Update JavaScript and HTML templates for better UI and functionality.
2025-07-31 21:33:44 +08:00

114 lines
4.2 KiB
Python

# /core/data/fetch.py
import MetaTrader5 as mt5
import pandas as pd
import logging
from datetime import datetime, timedelta
# Gunakan logger yang konsisten dengan seluruh aplikasi
logger = logging.getLogger(__name__)
# =================================================
# FUNGSI-FUNGSI PENGAMBILAN DATA DARI METATRADER 5
# =================================================
def get_rates(symbol: str, timeframe: int, count: int = 100):
"""
Mengambil data harga historis (rates) dari MT5 dalam bentuk DataFrame.
Ini adalah fungsi utama untuk semua analisis strategi.
"""
try:
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, count)
# Periksa apakah data berhasil diambil
if rates is None or len(rates) == 0:
logger.warning(f"Gagal mengambil data harga untuk {symbol} (Timeframe: {timeframe}). MT5 mengembalikan None atau data kosong.")
return None
# Konversi ke DataFrame
df = pd.DataFrame(rates)
# Konversi kolom 'time' dari Unix timestamp menjadi objek datetime
df['time'] = pd.to_datetime(df['time'], unit='s')
return df
except Exception as e:
logger.error(f"Error tak terduga saat get_rates untuk {symbol}: {e}", exc_info=True)
return None
def get_mt5_account_info():
"""Mengambil informasi akun (saldo, equity, profit) dari MT5."""
try:
info = mt5.account_info()
# Periksa apakah info berhasil diambil sebelum mengubahnya menjadi dictionary
if info:
return info._asdict()
else:
logger.error(f"Gagal mengambil info akun. MT5 mengembalikan None. Error: {mt5.last_error()}")
return None
except Exception as e:
logger.error(f"Error tak terduga saat get_mt5_account_info: {e}", exc_info=True)
return None
def get_todays_profit():
"""Menghitung total profit dari histori trading hari ini."""
try:
from_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
to_date = datetime.now()
deals = mt5.history_deals_get(from_date, to_date)
if deals is None:
return 0.0
# Gunakan generator expression untuk efisiensi
return sum(d.profit for d in deals if d.entry == 1)
except Exception as e:
logger.error(f"Error tak terduga saat get_todays_profit: {e}", exc_info=True)
return 0.0
def get_trade_history_from_mt5(days_history: int = 30):
"""Mengambil riwayat transaksi yang sudah ditutup dari MT5."""
try:
from_date = datetime.now() - timedelta(days=days_history)
to_date = datetime.now()
deals = mt5.history_deals_get(from_date, to_date)
if deals is None:
logger.warning("Gagal mengambil histori deals dari MT5. MT5 mengembalikan None.")
return []
# Proses data hanya jika ada deals yang ditemukan
if len(deals) > 0:
df = pd.DataFrame(list(deals), columns=deals[0]._asdict().keys())
df['time'] = pd.to_datetime(df['time'], unit='s')
closed = df[df['entry'] == 1].sort_values(by='time', ascending=False)
return closed.to_dict('records')
return []
except Exception as e:
logger.error(f"Error tak terduga saat get_trade_history: {e}", exc_info=True)
return []
def get_open_positions_from_mt5():
"""Mengambil semua posisi trading yang sedang terbuka dari akun MT5."""
try:
positions = mt5.positions_get()
if positions is None:
# Tidak perlu print error jika hanya tidak ada posisi
return []
# Ubah tuple of objects menjadi list of dictionaries
return [
{
"ticket": pos.ticket, "symbol": pos.symbol, "type": pos.type,
"volume": pos.volume, "price_open": pos.price_open,
"price_current": pos.price_current, "profit": pos.profit,
"magic": pos.magic,
"time": datetime.fromtimestamp(pos.time).isoformat()
}
for pos in positions
]
except Exception as e:
logger.error(f"Error tak terduga saat get_open_positions: {e}", exc_info=True)
return []