mirror of
https://github.com/chrisnov-it/quantumbotx.git
synced 2026-07-29 03:37:45 +00:00
Refactor and update forex and stock modal handling in JavaScript
Refactor the JavaScript code to use a single modal for both forex and stock profiles. Update error handling to display server error messages. Remove unused app.py and fetch.py files. Adjust various routes and strategies for consistency.
This commit is contained in:
+65
-55
@@ -5,83 +5,92 @@ from core.strategies.strategy_map import STRATEGY_MAP
|
||||
def run_backtest(strategy_id, params, historical_data_df):
|
||||
"""
|
||||
Menjalankan simulasi backtesting untuk strategi tertentu pada data historis.
|
||||
VERSI OPTIMIZED: Indikator dihitung sekali di awal.
|
||||
"""
|
||||
strategy_class = STRATEGY_MAP.get(strategy_id)
|
||||
if not strategy_class:
|
||||
return {"error": "Strategi tidak ditemukan"}
|
||||
|
||||
# Inisialisasi state backtesting
|
||||
trades = []
|
||||
in_position = False
|
||||
initial_capital = 10000 # Modal awal virtual $10,000
|
||||
capital = initial_capital
|
||||
equity_curve = [initial_capital]
|
||||
peak_equity = initial_capital
|
||||
max_drawdown = 0.0
|
||||
|
||||
position_type = None
|
||||
entry_price = 0.0
|
||||
sl_pips = params.get('sl_pips', 100)
|
||||
tp_pips = params.get('tp_pips', 200)
|
||||
|
||||
# Asumsi pip value sederhana untuk backtesting, bisa disempurnakan nanti
|
||||
# Untuk pair JPY, point adalah 0.001, untuk yang lain 0.00001
|
||||
point = 0.001 if 'JPY' in historical_data_df.columns[0].upper() else 0.00001
|
||||
# Asumsi nilai per pip untuk 0.01 lot
|
||||
# Ini adalah penyederhanaan besar, tapi cukup untuk backtesting awal
|
||||
value_per_pip = 0.1 # $0.10 per pip
|
||||
|
||||
pip_value = 10 * point
|
||||
|
||||
# Mock bot object untuk strategi
|
||||
# --- LANGKAH 1: Hitung semua indikator SEKALI di awal ---
|
||||
class MockBot:
|
||||
def __init__(self):
|
||||
self.market_for_mt5 = "BACKTEST"
|
||||
self.timeframe = "H1"
|
||||
self.tf_map = {}
|
||||
|
||||
# Inisialisasi strategi dengan parameter yang diberikan
|
||||
strategy_instance = strategy_class(bot_instance=MockBot(), params=params)
|
||||
|
||||
# Loop melalui setiap bar data historis
|
||||
for i in range(1, len(historical_data_df)):
|
||||
# Buat DataFrame "seolah-olah" ini adalah data real-time hingga bar saat ini
|
||||
# PERBAIKAN: Gunakan .copy() untuk membuat salinan eksplisit dari slice.
|
||||
# Ini akan menghilangkan SettingWithCopyWarning di semua strategi.
|
||||
current_market_data = historical_data_df.iloc[:i].copy()
|
||||
|
||||
analysis = strategy_instance.analyze(current_market_data)
|
||||
signal = analysis.get("signal")
|
||||
current_price = historical_data_df.iloc[i]['close']
|
||||
# Panggil metode baru 'analyze_df' untuk pra-perhitungan indikator
|
||||
df_with_indicators = strategy_instance.analyze_df(historical_data_df.copy())
|
||||
|
||||
if df_with_indicators.empty:
|
||||
return {"error": "Gagal menghasilkan data indikator. Periksa panjang data input."}
|
||||
|
||||
strategy_name = strategy_instance.name
|
||||
|
||||
# --- LANGKAH 2: Inisialisasi state backtesting ---
|
||||
trades = []
|
||||
in_position = False
|
||||
initial_capital = 10000
|
||||
capital = initial_capital
|
||||
equity_curve = [initial_capital]
|
||||
peak_equity = initial_capital
|
||||
max_drawdown = 0.0
|
||||
position_type = None
|
||||
entry_price = 0.0
|
||||
sl_pips = params.get('sl_pips', 100)
|
||||
tp_pips = params.get('tp_pips', 200)
|
||||
|
||||
symbol_name = historical_data_df.columns[0].upper()
|
||||
pip_size = 0.0001 # Default untuk Forex standar
|
||||
if 'JPY' in symbol_name:
|
||||
pip_size = 0.01
|
||||
elif 'XAU' in symbol_name or 'XAG' in symbol_name: # Emas atau Perak
|
||||
pip_size = 0.01
|
||||
|
||||
# Tentukan nilai per pip berdasarkan simbol (untuk lot 0.01)
|
||||
if 'XAU' in symbol_name or 'XAG' in symbol_name: # Emas atau Perak
|
||||
# Untuk 0.01 lot (1 oz), pergerakan harga $0.01 = profit/loss $0.01
|
||||
value_per_pip = 0.01
|
||||
else:
|
||||
# Untuk Forex (misal EURUSD), 0.01 lot, pergerakan 1 pip = profit/loss $0.1
|
||||
# Ini adalah asumsi umum, untuk JPY pairs nilainya bisa sedikit berbeda
|
||||
value_per_pip = 0.1
|
||||
|
||||
# --- LANGKAH 3: Loop melalui data yang sudah ada indikatornya ---
|
||||
for i in range(1, len(df_with_indicators)):
|
||||
current_bar = df_with_indicators.iloc[i]
|
||||
signal = current_bar.get("signal", "HOLD")
|
||||
current_price = current_bar['close']
|
||||
|
||||
# Cek SL/TP jika sedang dalam posisi
|
||||
if in_position:
|
||||
profit = 0
|
||||
if position_type == 'BUY':
|
||||
profit_pips = (current_price - entry_price) / point / 10
|
||||
if current_price <= entry_price - (sl_pips * pip_value):
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': profit_pips, 'reason': 'SL'})
|
||||
capital += profit_pips * value_per_pip
|
||||
profit_pips = (current_price - entry_price) / pip_size
|
||||
if current_price <= entry_price - (sl_pips * pip_size):
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': -sl_pips, 'reason': 'SL'})
|
||||
capital -= sl_pips * value_per_pip
|
||||
in_position = False
|
||||
elif current_price >= entry_price + (tp_pips * pip_value):
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': profit_pips, 'reason': 'TP'})
|
||||
capital += profit_pips * value_per_pip
|
||||
elif current_price >= entry_price + (tp_pips * pip_size):
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': tp_pips, 'reason': 'TP'})
|
||||
capital += tp_pips * value_per_pip
|
||||
in_position = False
|
||||
elif position_type == 'SELL':
|
||||
profit_pips = (entry_price - current_price) / point / 10
|
||||
if current_price >= entry_price + (sl_pips * pip_value):
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': profit_pips, 'reason': 'SL'})
|
||||
capital += profit_pips * value_per_pip
|
||||
profit_pips = (entry_price - current_price) / pip_size
|
||||
if current_price >= entry_price + (sl_pips * pip_size):
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': -sl_pips, 'reason': 'SL'})
|
||||
capital -= sl_pips * value_per_pip
|
||||
in_position = False
|
||||
elif current_price <= entry_price - (tp_pips * pip_value):
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': profit_pips, 'reason': 'TP'})
|
||||
capital += profit_pips * value_per_pip
|
||||
elif current_price <= entry_price - (tp_pips * pip_size):
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': tp_pips, 'reason': 'TP'})
|
||||
capital += tp_pips * value_per_pip
|
||||
in_position = False
|
||||
|
||||
|
||||
if not in_position: # Jika posisi baru saja ditutup
|
||||
equity_curve.append(capital)
|
||||
peak_equity = max(peak_equity, capital)
|
||||
drawdown = (peak_equity - capital) / peak_equity
|
||||
drawdown = (peak_equity - capital) / peak_equity if peak_equity > 0 else 0
|
||||
max_drawdown = max(max_drawdown, drawdown)
|
||||
|
||||
# Cek sinyal baru
|
||||
@@ -96,12 +105,12 @@ def run_backtest(strategy_id, params, historical_data_df):
|
||||
elif (signal == 'SELL' and in_position and position_type == 'BUY') or \
|
||||
(signal == 'BUY' and in_position and position_type == 'SELL'):
|
||||
# Sinyal berlawanan, tutup posisi lama
|
||||
profit_pips = ((current_price - entry_price) if position_type == 'BUY' else (entry_price - current_price)) / point / 10
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': profit_pips, 'reason': 'Signal Flip'})
|
||||
profit_pips = ((current_price - entry_price) if position_type == 'BUY' else (entry_price - current_price)) / pip_size # Calculate pips
|
||||
trades.append({'entry': entry_price, 'exit': current_price, 'profit_pips': profit_pips, 'reason': 'Signal Flip'}) # Log trade
|
||||
capital += profit_pips * value_per_pip
|
||||
equity_curve.append(capital)
|
||||
peak_equity = max(peak_equity, capital)
|
||||
drawdown = (peak_equity - capital) / peak_equity
|
||||
drawdown = (peak_equity - capital) / peak_equity if peak_equity > 0 else 0
|
||||
max_drawdown = max(max_drawdown, drawdown)
|
||||
in_position = False
|
||||
|
||||
@@ -112,6 +121,7 @@ def run_backtest(strategy_id, params, historical_data_df):
|
||||
win_rate = (wins / len(trades) * 100) if trades else 0
|
||||
|
||||
return {
|
||||
"strategy_name": strategy_name,
|
||||
"total_trades": len(trades),
|
||||
"total_profit_pips": total_profit_pips,
|
||||
"win_rate_percent": win_rate,
|
||||
@@ -119,5 +129,5 @@ def run_backtest(strategy_id, params, historical_data_df):
|
||||
"losses": losses,
|
||||
"max_drawdown_percent": max_drawdown * 100,
|
||||
"equity_curve": equity_curve,
|
||||
"trades": trades[-20:] # Tampilkan 20 trade terakhir
|
||||
"trades": trades[-20:]
|
||||
}
|
||||
Reference in New Issue
Block a user