Refactor app and templates for backtesting and active page tracking

- Update `app.py` to include backtesting routes and improve blueprint registration.
- Modify templates to include an active page parameter for navigation highlighting.
- Remove unused `crypto_data.py` and `api_crypto.py`.
- Add new backtesting-related files and templates.
This commit is contained in:
Reynov Christian
2025-08-02 21:26:22 +08:00
parent 998f85267d
commit 53ae5e8861
31 changed files with 968 additions and 1306 deletions
+64 -42
View File
@@ -34,22 +34,22 @@ app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'your-secret-key-here')
# --- Registrasi Blueprints ---
# Impor blueprints setelah 'app' dibuat untuk menghindari circular import
from core.routes.api_dashboard import api_dashboard # noqa: E402
from core.routes.api_chart import api_chart # noqa: E402
from core.routes.api_bots import api_bots # noqa: E402
from core.routes.api_profile import api_profile # noqa: E402
from core.routes.api_portfolio import api_portfolio # noqa: E402
from core.routes.api_history import api_history # noqa: E402
from core.routes.api_notifications import api_notifications # noqa: E402
from core.routes.api_stocks import api_stocks # noqa: E402
from core.routes.api_forex import api_forex # noqa: E402
from core.routes.api_crypto import api_crypto # noqa: E402
from core.routes.api_fundamentals import api_fundamentals # noqa: E402
from core.routes.api_dashboard import api_dashboard
from core.routes.api_chart import api_chart
from core.routes.api_bots import api_bots
from core.routes.api_profile import api_profile
from core.routes.api_portfolio import api_portfolio
from core.routes.api_history import api_history
from core.routes.api_notifications import api_notifications
from core.routes.api_stocks import api_stocks
from core.routes.api_forex import api_forex
from core.routes.api_fundamentals import api_fundamentals
from core.routes.api_backtest import api_backtest
# Buat daftar semua blueprints untuk registrasi yang lebih rapi
blueprints = [
api_dashboard, api_chart, api_bots, api_profile, api_portfolio, api_history,
api_notifications, api_stocks, api_forex, api_crypto, api_fundamentals
api_notifications, api_stocks, api_forex, api_fundamentals, api_backtest
]
# Daftarkan setiap blueprint ke aplikasi
@@ -59,48 +59,47 @@ for blueprint in blueprints:
# --- Rute Halaman (Views) ---
@app.route('/')
def dashboard():
return render_template('index.html')
return render_template('index.html', active_page='dashboard')
@app.route('/trading_bots')
def bots_page():
return render_template('trading_bots.html')
return render_template('trading_bots.html', active_page='trading_bots')
@app.route('/bots/<int:bot_id>')
def bot_detail_page(bot_id):
return render_template('bot_detail.html')
return render_template('bot_detail.html', active_page='trading_bots') # Tetap di menu trading_bots
@app.route('/backtesting')
def backtesting_page():
return render_template('backtesting.html', active_page='backtesting')
# ... (rute-rute halaman lainnya tetap sama) ...
@app.route('/portfolio')
def portfolio_page():
return render_template('portfolio.html')
return render_template('portfolio.html', active_page='portfolio')
@app.route('/history')
def history_page():
return render_template('history.html')
return render_template('history.html', active_page='history')
@app.route('/settings')
def settings_page():
return render_template('settings.html')
return render_template('settings.html', active_page='settings')
@app.route('/profile')
def profile_page():
return render_template('profile.html')
return render_template('profile.html', active_page='profile') # Asumsi profile punya menu sendiri
@app.route('/notifications')
def notifications_page():
return render_template('notifications.html')
@app.route('/cryptocurrency')
def crypto_page():
return render_template('cryptocurrency.html')
return render_template('notifications.html', active_page='notifications')
@app.route('/stocks')
def stocks_page():
return render_template('stocks.html')
return render_template('stocks.html', active_page='stocks')
@app.route('/forex')
def forex_page():
return render_template('forex.html')
return render_template('forex.html', active_page='forex')
# --- Error Handlers & Rute Lain-lain ---
@app.errorhandler(404)
@@ -145,6 +144,21 @@ def shutdown_handler():
mt5.shutdown() # Pastikan koneksi MT5 selalu ditutup
logger.info("Koneksi MetaTrader 5 ditutup. Proses shutdown selesai.")
# --- Filter Log Kustom ---
class RequestLogFilter(logging.Filter):
"""
Filter untuk menyembunyikan log permintaan (polling) yang tidak penting dari terminal.
"""
def filter(self, record):
# Pesan log dari Werkzeug terlihat seperti: "GET /api/path HTTP/1.1" 200 -
msg = record.getMessage()
# Daftar path yang ingin kita sembunyikan dari log konsol
paths_to_ignore = [
"GET /api/notifications/unread-count",
"GET /api/bots/analysis" # <-- Ini juga sering di-poll
]
return not any(path in msg for path in paths_to_ignore)
# --- Titik Eksekusi Utama ---
if __name__ == '__main__':
# Memuat kredensial MT5 dari .env dengan aman
@@ -162,26 +176,34 @@ if __name__ == '__main__':
exit(1)
# Inisialisasi koneksi ke MetaTrader 5
if not initialize_mt5(ACCOUNT, PASSWORD, SERVER):
logger.critical("GAGAL terhubung ke MetaTrader 5. Pastikan kredensial benar dan terminal berjalan.")
exit(1)
else:
# PERBAIKAN: Jangan langsung exit. Coba hubungkan, tapi biarkan aplikasi tetap berjalan jika gagal.
# Koneksi akan dicoba lagi saat bot pertama kali dimulai.
if initialize_mt5(ACCOUNT, PASSWORD, SERVER):
logger.info("Berhasil terhubung ke MetaTrader 5.")
# Muat semua bot yang ada di database
try:
ambil_semua_bot()
logger.info("Semua bot dari database berhasil dimuat.")
except Exception as e:
logger.error(f"Terjadi kesalahan saat memuat bot: {e}", exc_info=True)
else:
logger.warning("GAGAL terhubung ke MetaTrader 5 saat startup. Aplikasi akan berjalan tanpa koneksi live.")
logger.warning("Fitur bot live tidak akan berfungsi sampai koneksi MT5 pulih.")
# --- 3. Daftarkan fungsi shutdown ---
atexit.register(shutdown_handler)
# Jalankan aplikasi Flask
app.run(
debug=os.getenv('FLASK_DEBUG', 'False').lower() == 'true',
host=os.getenv('FLASK_HOST', '127.0.0.1'),
port=int(os.getenv('FLASK_PORT', 5000)),
use_reloader=False # Penting: False untuk mencegah eksekusi ganda pada background thread
)
# --- Terapkan Filter Log ---
# Dapatkan logger bawaan Werkzeug dan tambahkan filter kita
werkzeug_logger = logging.getLogger('werkzeug')
werkzeug_logger.addFilter(RequestLogFilter())
# --- 3. Daftarkan fungsi shutdown ---
# PERBAIKAN: Pindahkan ke luar blok if/else agar selalu dijalankan.
atexit.register(shutdown_handler)
# Jalankan aplikasi Flask
# PERBAIKAN: Pindahkan ke luar blok if/else agar server selalu berjalan.
app.run(
debug=os.getenv('FLASK_DEBUG', 'False').lower() == 'true',
host=os.getenv('FLASK_HOST', '127.0.0.1'),
port=int(os.getenv('FLASK_PORT', 5000)),
use_reloader=False # Penting: False untuk mencegah eksekusi ganda pada background thread
)
+123
View File
@@ -0,0 +1,123 @@
# core/backtesting/engine.py
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.
"""
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
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']
# 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
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
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
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
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
max_drawdown = max(max_drawdown, drawdown)
# Cek sinyal baru
if signal == 'BUY' and not in_position:
in_position = True
position_type = 'BUY'
entry_price = current_price
elif signal == 'SELL' and not in_position:
in_position = True
position_type = 'SELL'
entry_price = current_price
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'})
capital += profit_pips * value_per_pip
equity_curve.append(capital)
peak_equity = max(peak_equity, capital)
drawdown = (peak_equity - capital) / peak_equity
max_drawdown = max(max_drawdown, drawdown)
in_position = False
# Hitung hasil akhir
total_profit_pips = sum(trade['profit_pips'] for trade in trades)
wins = len([trade for trade in trades if trade['profit_pips'] > 0])
losses = len(trades) - wins
win_rate = (wins / len(trades) * 100) if trades else 0
return {
"total_trades": len(trades),
"total_profit_pips": total_profit_pips,
"win_rate_percent": win_rate,
"wins": wins,
"losses": losses,
"max_drawdown_percent": max_drawdown * 100,
"equity_curve": equity_curve,
"trades": trades[-20:] # Tampilkan 20 trade terakhir
}
+7 -3
View File
@@ -68,9 +68,13 @@ class TradingBot(threading.Thread):
time.sleep(self.check_interval)
continue
# --- PERBAIKAN: Panggil metode analyze dari objek strategi ---
# Metode analyze tidak lagi memerlukan argumen
self.last_analysis = self.strategy_instance.analyze()
# --- PERBAIKAN: Bot sekarang yang mengambil data ---
from core.data.fetch import get_rates
tf_const = self.tf_map.get(self.timeframe, mt5.TIMEFRAME_H1)
# Ambil data yang cukup untuk strategi terkompleks (misal, 250 bar untuk EMA 200)
df = get_rates(self.market_for_mt5, tf_const, 250)
self.last_analysis = self.strategy_instance.analyze(df)
logger.info(f"Bot {self.id} [{self.strategy_name}] - Last Analysis: {self.last_analysis}")
signal = self.last_analysis.get("signal", "HOLD")
+30
View File
@@ -0,0 +1,30 @@
# core/routes/api_backtest.py
import pandas as pd
import json
from flask import Blueprint, request, jsonify
from core.backtesting.engine import run_backtest
api_backtest = Blueprint('api_backtest', __name__)
@api_backtest.route('/api/backtest/run', methods=['POST'])
def run_backtest_route():
if 'file' not in request.files:
return jsonify({"error": "Tidak ada file data yang diunggah"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "Nama file kosong"}), 400
try:
# Baca data CSV yang diunggah
df = pd.read_csv(file, parse_dates=['time'])
# Ambil parameter dari form data
strategy_id = request.form.get('strategy')
params = json.loads(request.form.get('params', '{}'))
results = run_backtest(strategy_id, params, df)
return jsonify(results)
except Exception as e:
return jsonify({"error": f"Terjadi kesalahan saat backtesting: {str(e)}"}), 500
-39
View File
@@ -1,39 +0,0 @@
from flask import Blueprint, jsonify
# Initialize blueprint
api_crypto = Blueprint('api_crypto', __name__)
# Sample data for initial load
SAMPLE_CRYPTO_DATA = [
{
'name': 'Bitcoin',
'symbol': 'BTC',
'price': 'Rp 1.234,567,890.00',
'change': '+2.34%',
'market_cap': 'Rp 2.345,678,901,234.00'
},
{
'name': 'Ethereum',
'symbol': 'ETH',
'price': 'Rp 123,456,789.00',
'change': '-1.23%',
'market_cap': 'Rp 1.234,567,890,123.00'
}
]
@api_crypto.route('/api/crypto')
def get_crypto_data():
"""Get cryptocurrency market data"""
try:
# Try to get real data from CMC
from core.utils.external import get_crypto_data_from_cmc
real_data = get_crypto_data_from_cmc()
if real_data and len(real_data) > 0:
return jsonify(real_data)
# Fallback to sample data if API fails
return jsonify(SAMPLE_CRYPTO_DATA)
except Exception as e:
# Return error response
return jsonify({'error': str(e)}), 500
+7 -10
View File
@@ -1,27 +1,24 @@
# core/strategies/base_strategy.py
class BaseStrategy:
from abc import ABC, abstractmethod
class BaseStrategy(ABC):
"""
Kelas dasar abstrak untuk semua strategi trading.
Setiap strategi harus mewarisi kelas ini dan mengimplementasikan metode `analyze`.
"""
def __init__(self, bot_instance, params: dict = {}):
"""
Inisialisasi strategi dengan instance dari bot yang menjalankannya.
Args:
bot_instance (TradingBot): Instance dari bot yang aktif.
params (dict): Dictionary berisi parameter kustom untuk strategi.
"""
self.bot = bot_instance
self.params = params
def analyze(self):
@abstractmethod
def analyze(self, df):
"""
Metode inti yang harus di-override oleh setiap strategi turunan.
Metode ini harus mengembalikan sebuah dictionary yang berisi hasil analisis.
Menerima DataFrame sebagai input.
"""
raise NotImplementedError("Setiap strategi harus mengimplementasikan metode `analyze()`.")
raise NotImplementedError("Setiap strategi harus mengimplementasikan metode `analyze(df)`.")
@classmethod
def get_definable_params(cls):
+1 -8
View File
@@ -1,7 +1,5 @@
# /core/strategies/bollinger_bands.py
import MetaTrader5 as mt5
from .base_strategy import BaseStrategy
from core.data.fetch import get_rates
class BollingerBandsStrategy(BaseStrategy):
name = 'Bollinger Bands Reversion'
@@ -15,16 +13,11 @@ class BollingerBandsStrategy(BaseStrategy):
{"name": "bb_std", "label": "Standar Deviasi BB", "type": "number", "default": 2.0, "step": 0.1}
]
def analyze(self):
def analyze(self, df):
"""
Menganalisis pasar menggunakan strategi Bollinger Bands® Mean Reversion.
Ideal untuk pasar ranging yang volatil seperti EURUSD.
"""
tf_const = self.bot.tf_map.get(self.bot.timeframe, mt5.TIMEFRAME_H1)
# Butuh data yang cukup untuk BBands(20)
df = get_rates(self.bot.market_for_mt5, tf_const, 21)
if df is None or df.empty or len(df) < 21:
return {"signal": "HOLD", "price": None, "explanation": "Data tidak cukup untuk Bollinger Bands."}
+1 -8
View File
@@ -1,9 +1,7 @@
# /core/strategies/bollinger_squeeze.py
import pandas_ta as ta
import numpy as np
import MetaTrader5 as mt5
from .base_strategy import BaseStrategy
from core.data.fetch import get_rates # <-- PERBAIKAN: Impor dari lokasi yang benar
class BollingerSqueezeStrategy(BaseStrategy):
name = 'Bollinger Squeeze Breakout'
@@ -21,17 +19,12 @@ class BollingerSqueezeStrategy(BaseStrategy):
{"name": "volume_factor", "label": "Faktor Volume", "type": "number", "default": 1.5, "step": 0.1}
]
def analyze(self):
def analyze(self, df):
"""
Menganalisis pasar menggunakan strategi Bollinger Band Squeeze.
Strategi ini menunggu periode volatilitas rendah ("squeeze") lalu
masuk saat harga breakout.
"""
tf_const = self.bot.tf_map.get(self.bot.timeframe, mt5.TIMEFRAME_H1)
# Butuh data yang cukup untuk rolling window squeeze (120) + BBands (20)
df = get_rates(self.bot.market_for_mt5, tf_const, 150) # <-- PERBAIKAN: Gunakan fungsi yang benar
if df is None or df.empty or len(df) < 121:
return {"signal": "HOLD", "price": None, "explanation": "Data tidak cukup untuk Bollinger Squeeze."}
+1 -9
View File
@@ -1,8 +1,6 @@
# /core/strategies/ma_crossover.py
import pandas_ta as ta
import MetaTrader5 as mt5
from .base_strategy import BaseStrategy
from core.data.fetch import get_rates
class MACrossoverStrategy(BaseStrategy):
name = 'Moving Average Crossover'
@@ -16,17 +14,11 @@ class MACrossoverStrategy(BaseStrategy):
{"name": "slow_period", "label": "Periode MA Lambat", "type": "number", "default": 50}
]
def analyze(self):
def analyze(self, df):
"""
Menganalisis pasar menggunakan strategi Moving Average Crossover (20/50).
Ideal untuk pasar dengan tren kuat seperti XAUUSD.
"""
# Mengakses properti dari instance bot yang tersimpan
tf_const = self.bot.tf_map.get(self.bot.timeframe, mt5.TIMEFRAME_H1)
# Butuh data yang cukup untuk MA 50
df = get_rates(self.bot.market_for_mt5, tf_const, 52)
if df is None or df.empty or len(df) < 51:
return {"signal": "HOLD", "price": None, "explanation": "Data tidak cukup untuk MA Crossover."}
+2 -3
View File
@@ -19,7 +19,7 @@ class MercyEdgeStrategy(BaseStrategy):
{"name": "stoch_smooth", "label": "Stoch Smooth", "type": "number", "default": 3},
]
def analyze(self):
def analyze(self, df_h1):
# Ambil parameter dinamis
macd_fast = self.params.get('macd_fast', 12)
macd_slow = self.params.get('macd_slow', 26)
@@ -28,9 +28,8 @@ class MercyEdgeStrategy(BaseStrategy):
stoch_d = self.params.get('stoch_d', 3)
stoch_smooth = self.params.get('stoch_smooth', 3)
# Gunakan fungsi get_rates yang terstandarisasi
# Ambil data sekunder (D1), data primer (H1) sudah diberikan
df_d1 = get_rates(self.bot.market_for_mt5, mt5.TIMEFRAME_D1, 200)
df_h1 = get_rates(self.bot.market_for_mt5, mt5.TIMEFRAME_H1, 100)
if df_d1 is None or df_h1 is None or len(df_d1) < 50 or len(df_h1) < 30:
return {"signal": "HOLD", "price": None, "explanation": "Data tidak cukup"}
+1 -6
View File
@@ -1,17 +1,12 @@
# core/strategies/pulse_sync.py
import pandas_ta as ta
import MetaTrader5 as mt5
from .base_strategy import BaseStrategy
from core.data.fetch import get_rates
class PulseSyncStrategy(BaseStrategy):
name = 'Pulse Sync (AI)'
description = 'Menggunakan AI untuk menganalisis momentum harga berdasarkan Simple Moving Average (SMA).'
def analyze(self):
tf_const = self.bot.tf_map.get(self.bot.timeframe, mt5.TIMEFRAME_H1)
df = get_rates(self.bot.market_for_mt5, tf_const, 100)
def analyze(self, df):
if df is None or df.empty or len(df) < 30:
return {"signal": "HOLD", "price": None, "explanation": "Data tidak cukup"}
+1 -8
View File
@@ -2,9 +2,7 @@
import pandas_ta as ta
import numpy as np
import MetaTrader5 as mt5
from .base_strategy import BaseStrategy
from core.data.fetch import get_rates
class QuantumVelocityStrategy(BaseStrategy):
name = 'Quantum Velocity'
@@ -21,15 +19,10 @@ class QuantumVelocityStrategy(BaseStrategy):
{"name": "squeeze_factor", "label": "Faktor Squeeze", "type": "number", "default": 0.7, "step": 0.1},
]
def analyze(self):
def analyze(self, df):
"""
Menganalisis pasar dengan filter tren jangka panjang dan pemicu breakout.
"""
tf_const = self.bot.tf_map.get(self.bot.timeframe, mt5.TIMEFRAME_H1)
# Butuh data yang cukup untuk EMA 200
df = get_rates(self.bot.market_for_mt5, tf_const, 250)
if df is None or df.empty or len(df) < 201:
return {"signal": "HOLD", "price": None, "explanation": "Data tidak cukup untuk Quantum Velocity."}
+1 -9
View File
@@ -1,8 +1,6 @@
# /core/strategies/quantumbotx_hybrid.py
import pandas_ta as ta
import MetaTrader5 as mt5
from .base_strategy import BaseStrategy
from core.data.fetch import get_rates
class QuantumBotXHybridStrategy(BaseStrategy):
name = 'QuantumBotX Hybrid'
@@ -20,19 +18,13 @@ class QuantumBotXHybridStrategy(BaseStrategy):
{"name": "bb_std", "label": "Std Dev BB", "type": "number", "default": 2.0, "step": 0.1}
]
def analyze(self):
def analyze(self, df):
"""
Menganalisis pasar menggunakan strategi Hybrid yang adaptif.
Menggunakan MA Crossover saat trending (ADX > 25) dan
Bollinger Bands saat ranging (ADX < 25).
"""
tf_const = self.bot.tf_map.get(self.bot.timeframe, mt5.TIMEFRAME_H1)
# PERBAIKAN: Minta lebih banyak data. Indikator kompleks seperti ADX
# butuh "pemanasan" lebih lama. 100 bar adalah angka yang lebih aman.
data_points_to_fetch = 100
required_data_points = 51 # Tetap butuh minimal 51 untuk SMA(50)
df = get_rates(self.bot.market_for_mt5, tf_const, data_points_to_fetch)
if df is None or df.empty or len(df) < required_data_points:
return {"signal": "HOLD", "price": None, "explanation": f"Data tidak cukup ({len(df) if df is not None else 0}/{required_data_points} bar)."}
+1 -7
View File
@@ -1,8 +1,6 @@
# /core/strategies/rsi_breakout.py
import pandas_ta as ta
import MetaTrader5 as mt5
from .base_strategy import BaseStrategy
from core.data.fetch import get_rates
class RSIBreakoutStrategy(BaseStrategy):
name = 'RSI Breakout'
@@ -17,15 +15,11 @@ class RSIBreakoutStrategy(BaseStrategy):
{"name": "oversold_level", "label": "Level Oversold", "type": "number", "default": 30}
]
def analyze(self):
tf_const = self.bot.tf_map.get(self.bot.timeframe, mt5.TIMEFRAME_H1)
def analyze(self, df):
rsi_period = self.params.get('rsi_period', 14)
overbought_level = self.params.get('overbought_level', 70)
oversold_level = self.params.get('oversold_level', 30)
df = get_rates(self.bot.market_for_mt5, tf_const, rsi_period + 5)
if df is None or df.empty or len(df) < rsi_period + 2:
return {"signal": "HOLD", "price": None, "explanation": "Data tidak cukup untuk RSI."}
+170
View File
@@ -0,0 +1,170 @@
// static/js/backtesting.js
document.addEventListener('DOMContentLoaded', () => {
const strategySelect = document.getElementById('strategy-select');
const paramsContainer = document.getElementById('params-container');
const form = document.getElementById('backtest-form');
const runBtn = document.getElementById('run-backtest-btn');
const resultsContainer = document.getElementById('results-container');
const resultsSummary = document.getElementById('results-summary');
const loadingSpinner = document.getElementById('loading-spinner');
let equityChart = null; // Variabel untuk menyimpan instance grafik
const resultsLog = document.getElementById('results-log');
// Muat strategi ke dropdown
async function loadStrategies() {
try {
const response = await fetch('/api/strategies');
if (!response.ok) throw new Error('Gagal memuat strategi.');
const strategies = await response.json();
strategySelect.innerHTML = '<option value="" disabled selected>Pilih sebuah strategi</option>';
strategies.forEach(strategy => {
const option = document.createElement('option');
option.value = strategy.id;
option.textContent = strategy.name;
strategySelect.appendChild(option);
});
} catch (error) {
console.error('Error loading strategies:', error);
strategySelect.innerHTML = '<option value="">Gagal memuat strategi</option>';
}
}
// Muat parameter saat strategi dipilih
strategySelect.addEventListener('change', async () => {
const strategyId = strategySelect.value;
paramsContainer.innerHTML = '<p class="text-sm text-gray-500">Memuat parameter...</p>';
if (!strategyId) {
paramsContainer.innerHTML = '';
return;
}
try {
const res = await fetch(`/api/strategies/${strategyId}/params`);
const params = await res.json();
paramsContainer.innerHTML = ''; // Kosongkan lagi
if (params.length > 0) {
params.forEach(param => {
paramsContainer.innerHTML += `
<div>
<label for="${param.name}" class="block text-sm font-medium text-gray-700">${param.label}</label>
<input type="${param.type || 'number'}" name="${param.name}" id="${param.name}" value="${param.default}" step="${param.step || 'any'}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
</div>`;
});
} else {
paramsContainer.innerHTML = '<p class="text-sm text-gray-500">Strategi ini tidak memiliki parameter kustom.</p>';
}
} catch (err) {
console.error('Gagal memuat parameter strategi:', err);
paramsContainer.innerHTML = '<p class="text-sm text-red-500">Gagal memuat parameter.</p>';
}
});
// Jalankan backtest saat form disubmit
form.addEventListener('submit', async (e) => {
e.preventDefault();
loadingSpinner.classList.remove('hidden');
resultsContainer.classList.add('hidden');
runBtn.disabled = true;
runBtn.textContent = 'Menjalankan...';
const formData = new FormData(form);
// Kumpulkan parameter kustom
const params = {};
// Tambahkan parameter SL/TP yang mungkin tidak ada di form kustom
params['sl_pips'] = 100; // Nilai default, bisa dibuat dinamis nanti
params['tp_pips'] = 200; // Nilai default
paramsContainer.querySelectorAll('input').forEach(input => {
const value = parseFloat(input.value);
// Pastikan hanya angka valid yang di-parse, selain itu ambil string
params[input.name] = isNaN(value) ? input.value : value;
});
formData.append('params', JSON.stringify(params));
try {
const response = await fetch('/api/backtest/run', {
method: 'POST',
body: formData, // Kirim sebagai multipart/form-data
});
const results = await response.json();
if (response.ok) {
displayResults(results);
} else {
alert(`Error: ${results.error}`);
}
} catch (err) {
console.error("Backtest failed:", err);
alert('Gagal terhubung ke server.');
} finally {
loadingSpinner.classList.add('hidden');
runBtn.disabled = false;
runBtn.textContent = 'Jalankan Backtest';
}
});
function displayResults(data) {
resultsContainer.classList.remove('hidden');
// PERBAIKAN: Tampilkan 6 metrik utama
resultsSummary.innerHTML = `
<div class="p-4 bg-gray-50 rounded-lg"><p class="text-sm text-gray-500">Total Profit</p><p class="text-2xl font-bold text-green-600">${data.total_profit_pips.toFixed(2)} pips</p></div>
<div class="p-4 bg-gray-50 rounded-lg"><p class="text-sm text-gray-500">Max Drawdown</p><p class="text-2xl font-bold text-red-600">${data.max_drawdown_percent.toFixed(2)}%</p></div>
<div class="p-4 bg-gray-50 rounded-lg"><p class="text-sm text-gray-500">Win Rate</p><p class="text-2xl font-bold text-blue-600">${data.win_rate_percent.toFixed(2)}%</p></div>
<div class="p-4 bg-gray-50 rounded-lg"><p class="text-sm text-gray-500">Total Trades</p><p class="text-2xl font-bold">${data.total_trades}</p></div>
<div class="p-4 bg-gray-50 rounded-lg"><p class="text-sm text-gray-500">Wins</p><p class="text-2xl font-bold">${data.wins}</p></div>
<div class="p-4 bg-gray-50 rounded-lg"><p class="text-sm text-gray-500">Losses</p><p class="text-2xl font-bold">${data.losses}</p></div>
`;
// Tampilkan grafik kurva ekuitas
displayEquityChart(data.equity_curve);
// Tampilkan log trade (opsional)
if (data.trades && data.trades.length > 0) {
let logHtml = '<h4 class="text-lg font-semibold mt-6 mb-2">20 Trade Terakhir</h4><div class="text-xs font-mono border rounded p-2 bg-gray-50 max-h-64 overflow-y-auto">';
data.trades.forEach(trade => {
const profitClass = trade.profit_pips > 0 ? 'text-green-600' : 'text-red-600';
logHtml += `<p>Entry: ${trade.entry.toFixed(4)} | Exit: ${trade.exit.toFixed(4)} | Profit: <span class="${profitClass}">${trade.profit_pips.toFixed(2)} pips</span> | Reason: ${trade.reason}</p>`;
});
logHtml += '</div>';
resultsLog.innerHTML = logHtml;
} else {
resultsLog.innerHTML = '';
}
}
function displayEquityChart(equityData) {
const ctx = document.getElementById('equity-chart').getContext('2d');
if (equityChart) {
equityChart.destroy(); // Hancurkan grafik lama sebelum membuat yang baru
}
equityChart = new Chart(ctx, {
type: 'line',
data: {
labels: Array.from({ length: equityData.length }, (_, i) => i + 1), // Label 1, 2, 3, ...
datasets: [{
label: 'Equity Curve',
data: equityData,
borderColor: 'rgb(59, 130, 246)',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.1,
pointRadius: 0,
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: 'Pertumbuhan Modal (Equity Curve)' }
},
scales: { y: { beginAtZero: false } }
}
});
}
loadStrategies();
});
-42
View File
@@ -1,42 +0,0 @@
document.getElementById('sidebar-toggle').addEventListener('click', function() {
document.getElementById('sidebar').classList.toggle('collapsed');
});
async function fetchCryptoData() {
const tableBody = document.querySelector('tbody');
try {
const response = await fetch('/api/crypto');
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
}
const cryptos = await response.json();
if (!cryptos || cryptos.length === 0) {
tableBody.innerHTML = '<tr><td colspan="5" class="p-4 text-center text-gray-500">Tidak ada data cryptocurrency yang tersedia.</td></tr>';
return;
}
const rowsHtml = cryptos.map(crypto => {
const changeClass = crypto.change.startsWith('+') ? 'text-green-600' : 'text-red-600';
return `
<tr>
<td class="px-6 py-4 whitespace-nowrap"><div class="flex items-center"><div class="font-medium text-gray-900">${crypto.name}</div><div class="text-sm text-gray-500 ml-2">${crypto.symbol}</div></div></td>
<td class="px-6 py-4 whitespace-nowrap font-medium">${crypto.price}</td>
<td class="px-6 py-4 whitespace-nowrap ${changeClass} font-medium">${crypto.change}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${crypto.market_cap}</td>
<td class="px-6 py-4 whitespace-nowrap text-right"><button class="bg-blue-600 text-white py-1 px-3 rounded-md text-sm font-medium hover:bg-blue-700">Trade</button></td>
</tr>
`;
}).join('');
tableBody.innerHTML = rowsHtml;
} catch (error) {
console.error('Gagal mengambil data crypto:', error);
tableBody.innerHTML = '<tr><td colspan="5" class="p-4 text-center text-red-500">Gagal memuat data.</td></tr>';
}
}
document.addEventListener('DOMContentLoaded', fetchCryptoData);
setInterval(fetchCryptoData, 15000);
+27
View File
@@ -0,0 +1,27 @@
<!-- templates/500.html -->
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Terjadi Kesalahan Internal - QuantumBotX</title>
<!-- Menggunakan versi Tailwind yang lebih modern jika memungkinkan, tapi kita samakan dengan 404.html -->
<link href="https://cdn.tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans flex items-center justify-center h-screen">
<div class="text-center">
<h1 class="text-9xl font-bold text-red-500">500</h1>
<h2 class="text-3xl font-semibold text-gray-800 mt-4">Terjadi Kesalahan Internal</h2>
<p class="text-gray-600 mt-2">
Maaf, terjadi masalah pada server kami. Tim kami telah diberitahu dan sedang menanganinya.
</p>
<div class="mt-8">
<a href="{{ url_for('dashboard') }}" class="bg-blue-500 text-white px-6 py-3 rounded-lg shadow hover:bg-blue-600 transition">
<i class="fas fa-home mr-2"></i>Kembali ke Dashboard
</a>
</div>
</div>
</body>
</html>
+50
View File
@@ -0,0 +1,50 @@
<!-- templates/backtesting.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title %}Backtester{% endblock %}
{% block content %}
<h2 class="text-2xl font-bold text-gray-800 mb-4">Strategy Backtester</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Kolom Konfigurasi -->
<div class="lg:col-span-1 bg-white p-6 rounded-lg shadow">
<form id="backtest-form">
<div class="space-y-4">
<div>
<label for="strategy-select" class="block text-sm font-medium text-gray-700">Pilih Strategi</label>
<select id="strategy-select" name="strategy" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm"></select>
</div>
<div>
<label for="data-file" class="block text-sm font-medium text-gray-700">Unggah Data Historis (CSV)</label>
<input type="file" id="data-file" name="file" accept=".csv" class="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100" required>
</div>
<div id="params-container" class="space-y-4 pt-4 border-t">
<!-- Parameter akan dimuat di sini -->
</div>
<button type="submit" id="run-backtest-btn" class="w-full bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700">
Jalankan Backtest
</button>
</div>
</form>
</div>
<!-- Kolom Hasil -->
<div id="results-container" class="lg:col-span-2 bg-white p-6 rounded-lg shadow hidden">
<h3 class="text-xl font-bold mb-4">Hasil Backtest</h3>
<div id="results-summary" class="grid grid-cols-2 md:grid-cols-3 gap-4 mb-6"></div>
<div class="mb-6">
<canvas id="equity-chart"></canvas>
</div>
<div id="results-log"></div>
</div>
<div id="loading-spinner" class="lg:col-span-2 flex items-center justify-center hidden">
<i class="fas fa-spinner fa-spin text-blue-500 text-4xl"></i>
<p class="ml-4 text-gray-600">Menjalankan simulasi...</p>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/backtesting.js') }}"></script>
{% endblock %}
+76
View File
@@ -0,0 +1,76 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Dashboard{% endblock %} - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
{% block head_extra %}{% endblock %}
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- SIDEBAR -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text">
<h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1>
<p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="/" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50 {% if active_page == 'dashboard' %}active{% endif %}"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="/trading_bots" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50 {% if active_page == 'trading_bots' %}active{% endif %}"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="/portfolio" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50 {% if active_page == 'portfolio' %}active{% endif %}"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="/backtesting" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50 {% if active_page == 'backtesting' %}active{% endif %}"><i class="fas fa-vial"></i><span class="ml-3 sidebar-text">Backtester</span></a>
<a href="/history" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50 {% if active_page == 'history' %}active{% endif %}"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="/settings" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50 {% if active_page == 'settings' %}active{% endif %}"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="/stocks" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50 {% if active_page == 'stocks' %}active{% endif %}"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="/forex" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50 {% if active_page == 'forex' %}active{% endif %}"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center"><i class="fas fa-user text-gray-600"></i></div>
<div class="ml-3 sidebar-text "><p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p><p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p></div>
</a>
</div>
</div>
<!-- MAIN CONTENT -->
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
<div class="ml-4 relative">
<input type="text" class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Search markets, bots..." />
<i class="fas fa-search absolute right-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600"><i class="fas fa-bell"></i><span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span></a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300"><i class="fas fa-user text-gray-600"></i></a>
</div>
</div>
</header>
<!-- Ini adalah area di mana konten unik setiap halaman akan disuntikkan -->
<main class="p-6">
{% block content %}{% endblock %}
</main>
</div>
</div>
<!-- Skrip yang dibutuhkan oleh semua halaman -->
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<!-- Blok untuk skrip spesifik halaman -->
{% block scripts %}{% endblock %}
</body>
</html>
+42 -102
View File
@@ -1,108 +1,48 @@
<!-- templates/bot_detail.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Detail Bot - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"/>
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text"><h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1><p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p></div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="/" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="/trading_bots" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="/portfolio" class="nav-item flex items-center px-4 py-3 text-blue-600 bg-blue-50 border-r-4 border-blue-600"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="/history" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="/settings" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<!-- templates/bot_detail.html (Setelah Refactor) -->
{% extends "base.html" %}
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="/cryptocurrency" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="/stocks" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="/forex" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center">
<i class="fas fa-user text-gray-600"></i>
</div>
<div class="ml-3 sidebar-text ">
<p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p>
<p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p>
</div>
</a>
</div>
</div>
<!-- Konten Utama -->
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600"><i class="fas fa-bell"></i><span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span></a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300"><i class="fas fa-user text-gray-600"></i></a>
</div>
</div>
</header>
{% block title %}Detail Bot{% endblock %}
<!-- Konten Utama Halaman Detail Bot -->
<main class="p-6">
<!-- Header Halaman -->
<div class="flex justify-between items-start mb-6 bg-white p-4 rounded-lg shadow">
<div>
<h2 id="bot-name-header" class="text-2xl font-bold text-gray-800">Memuat...</h2>
<p id="bot-market-header" class="text-gray-600">Memuat detail bot...</p>
</div>
<div id="bot-status-badge" class="px-4 py-2 rounded-full text-sm font-semibold bg-gray-200 text-gray-800">Memuat Status...</div>
</div>
{% block content %}
<!-- Header Halaman -->
<div class="flex justify-between items-start mb-6 bg-white p-4 rounded-lg shadow">
<div>
<h2 id="bot-name-header" class="text-2xl font-bold text-gray-800">Memuat...</h2>
<p id="bot-market-header" class="text-gray-600">Memuat detail bot...</p>
</div>
<div id="bot-status-badge" class="px-4 py-2 rounded-full text-sm font-semibold bg-gray-200 text-gray-800">Memuat Status...</div>
</div>
<!-- Layout Grid Utama -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Kolom Kiri (Log Aktivitas) -->
<div class="lg:col-span-2 bg-white rounded-lg shadow">
<div class="p-4 border-b"><h3 class="text-lg font-semibold text-gray-800">Log Aktivitas</h3></div>
<div id="history-log-container" class="overflow-y-auto" style="max-height: 60vh;">
<p class="text-center text-gray-500 p-4">Memuat riwayat...</p>
</div>
</div>
<!-- Kolom Kanan (Info & Analisis) -->
<div class="space-y-6">
<div class="bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-2 mb-3">Parameter</h3>
<div id="bot-parameters-container" class="text-sm space-y-2">
<p class="text-center text-gray-500">Memuat...</p>
</div>
</div>
<div class="bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-2 mb-3">Analisis Real-Time</h3>
<div id="bot-analysis-container" class="text-sm space-y-2">
<p class="text-center text-gray-500">Memuat...</p>
</div>
<div id="analysis-signal" class="mt-4 text-center font-bold text-lg p-2 rounded-md bg-gray-100 text-gray-600">-</div>
</div>
</div>
</div>
</main>
<!-- Layout Grid Utama -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Kolom Kiri (Log Aktivitas) -->
<div class="lg:col-span-2 bg-white rounded-lg shadow">
<div class="p-4 border-b"><h3 class="text-lg font-semibold text-gray-800">Log Aktivitas</h3></div>
<div id="history-log-container" class="overflow-y-auto" style="max-height: 60vh;">
<p class="text-center text-gray-500 p-4">Memuat riwayat...</p>
</div>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/bot_detail.js') }}"></script>
</body>
</html>
<!-- Kolom Kanan (Info & Analisis) -->
<div class="space-y-6">
<div class="bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-2 mb-3">Parameter</h3>
<div id="bot-parameters-container" class="text-sm space-y-2">
<p class="text-center text-gray-500">Memuat...</p>
</div>
</div>
<div class="bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-2 mb-3">Analisis Real-Time</h3>
<div id="bot-analysis-container" class="text-sm space-y-2">
<p class="text-center text-gray-500">Memuat...</p>
</div>
<div id="analysis-signal" class="mt-4 text-center font-bold text-lg p-2 rounded-md bg-gray-100 text-gray-600">-</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/bot_detail.js') }}"></script>
{% endblock %}
-94
View File
@@ -1,94 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cryptocurrency Market - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text">
<h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1>
<p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="{{ url_for('dashboard') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="{{ url_for('bots_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="{{ url_for('portfolio_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="{{ url_for('history_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="{{ url_for('settings_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="{{ url_for('crypto_page') }}" class="nav-item flex items-center px-4 py-3 text-blue-600 bg-blue-50 border-r-4 border-blue-600"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="{{ url_for('stocks_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="{{ url_for('forex_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center"><i class="fas fa-user text-gray-600"></i></div>
<div class="ml-3 sidebar-text "><p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p><p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p></div>
</a>
</div>
</div>
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
<div class="ml-4 relative"><input type="text" class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Search markets, bots..."><i class="fas fa-search absolute right-3 top-3 text-gray-400"></i></div>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600">
<i class="fas fa-bell"></i>
<span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span>
</a>
<a href="{{ url_for('profile_page') }}" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300"><i class="fas fa-user text-gray-600"></i></a>
</div>
</div>
</header>
<main class="p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Pasar Cryptocurrency</h2>
<p class="text-gray-600">Data harga real-time dari pasar kripto global.</p>
</div>
<div class="relative">
<input type="text" class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Cari aset...">
<i class="fas fa-search absolute right-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="bg-white rounded-lg shadow overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Aset</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Perubahan 24j</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Kap. Pasar</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr><td colspan="5" class="p-4 text-center text-gray-500">Memuat data...</td></tr>
</tbody>
</table>
</div>
</main>
</div>
</div>
<script src="{{ url_for('static', filename='js/cryptocurrency.js') }}"></script>
</body>
</html>
+46 -103
View File
@@ -1,108 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Forex Market - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text">
<h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1>
<p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="{{ url_for('dashboard') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="{{ url_for('bots_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="{{ url_for('portfolio_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="{{ url_for('history_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="{{ url_for('settings_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="{{ url_for('crypto_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="{{ url_for('stocks_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="{{ url_for('forex_page') }}" class="nav-item flex items-center px-4 py-3 text-blue-600 bg-blue-50 border-r-4 border-blue-600"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="{{ url_for('profile_page') }}" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center"><i class="fas fa-user text-gray-600"></i></div>
<div class="ml-3 sidebar-text "><p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p><p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p></div>
</a>
</div>
</div>
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
<div class="ml-4 relative"><input type="text" class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Search markets, bots..."><i class="fas fa-search absolute right-3 top-3 text-gray-400"></i></div>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600">
<i class="fas fa-bell"></i>
<span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span>
</a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300"><i class="fas fa-user text-gray-600"></i></a>
</div>
</div>
</header>
<main class="p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Pasar Forex</h2>
<p class="text-gray-600">Data harga real-time dari pasar valuta asing.</p>
</div>
</div>
<!-- templates/forex.html (Setelah Refactor) -->
{% extends "base.html" %}
<div class="bg-white rounded-lg shadow overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pasangan</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga Bid</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga Ask</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Spread</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th>
</tr>
</thead>
<tbody id="forex-table-body" class="bg-white divide-y divide-gray-200">
<tr><td colspan="5" class="p-4 text-center text-gray-500">Memuat data forex...</td></tr>
</tbody>
</table>
</div>
</main>
</div>
{% block title %}Forex Market{% endblock %}
{% block content %}
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Pasar Forex</h2>
<p class="text-gray-600">Data harga real-time dari pasar valuta asing.</p>
</div>
<!-- Modal -->
<div id="forex-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full hidden">
<div class="relative top-20 mx-auto p-5 border w-1/2 shadow-lg rounded-md bg-white">
<div class="mt-3 text-center">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title"></h3>
<div class="mt-2 px-7 py-3">
<p class="text-sm text-gray-500" id="modal-content"></p>
</div>
<div class="items-center px-4 py-3">
<button id="close-modal" class="px-4 py-2 bg-gray-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-300">
Close
</button>
</div>
</div>
<div class="bg-white rounded-lg shadow overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Pasangan</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga Bid</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga Ask</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Spread</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th>
</tr>
</thead>
<tbody id="forex-table-body" class="bg-white divide-y divide-gray-200">
<tr><td colspan="5" class="p-4 text-center text-gray-500">Memuat data forex...</td></tr>
</tbody>
</table>
</div>
<!-- Modal -->
<div id="forex-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full hidden">
<div class="relative top-20 mx-auto p-5 border w-1/2 shadow-lg rounded-md bg-white">
<div class="mt-3 text-center">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title"></h3>
<div class="mt-2 px-7 py-3">
<p class="text-sm text-gray-500" id="modal-content"></p>
</div>
<div class="items-center px-4 py-3">
<button id="close-modal" class="px-4 py-2 bg-gray-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-300">
Close
</button>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/forex.js') }}"></script>
</body>
</html>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/forex.js') }}"></script>
{% endblock %}
+33 -99
View File
@@ -1,102 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Riwayat Transaksi - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">
QT
</div>
<div class="ml-3 logo-text">
<h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1>
<p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="/" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="/trading_bots" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="/portfolio" class="nav-item flex items-center px-4 py-3 text-blue-600 bg-blue-50 border-r-4 border-blue-600"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="/history" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="/settings" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="/cryptocurrency" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="/stocks" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="/forex" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t"><a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center"><i class="fas fa-user text-gray-600"></i></div>
<div class="ml-3 sidebar-text "><p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p><p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p></div></a>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 overflow-auto">
<!-- Top Navigation -->
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none">
<i class="fas fa-bars"></i>
</button>
<div class="ml-4 relative">
<input type="text" class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Search markets, bots...">
<i class="fas fa-search absolute right-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600">
<i class="fas fa-bell"></i>
<span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span>
</a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300">
<i class="fas fa-user text-gray-600"></i>
</a>
</div>
</div>
</header>
<!-- templates/history.html (Setelah Refactor) -->
{% extends "base.html" %}
<main class="p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Riwayat Transaksi Global</h2>
<p class="text-gray-600">Semua transaksi yang telah ditutup dari akun MetaTrader 5.</p>
</div>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Simbol</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tipe</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Volume</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Profit</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Waktu Penutupan</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Magic Number</th>
</tr>
</thead>
<tbody id="history-table-body" class="bg-white divide-y divide-gray-200">
<!-- Data akan dimuat di sini oleh JavaScript -->
<tr><td colspan="6" class="p-4 text-center text-gray-500">Memuat riwayat...</td></tr>
</tbody>
</table>
</div>
</main>
</div>
{% block title %}Riwayat Transaksi{% endblock %}
{% block content %}
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Riwayat Transaksi Global</h2>
<p class="text-gray-600">Semua transaksi yang telah ditutup dari akun MetaTrader 5.</p>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Simbol</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tipe</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Volume</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Profit</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Waktu Penutupan</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Magic Number</th>
</tr>
</thead>
<tbody id="history-table-body" class="bg-white divide-y divide-gray-200">
<!-- Data akan dimuat di sini oleh JavaScript -->
<tr><td colspan="6" class="p-4 text-center text-gray-500">Memuat riwayat...</td></tr>
</tbody>
</table>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/history.js') }}"></script>
</body>
</html>
{% endblock %}
+56 -121
View File
@@ -1,127 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Dashboard - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/dashboard.css') }}" />
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- SIDEBAR -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text">
<h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1>
<p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4">
<p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p>
</div>
<a href="/" class="nav-item flex items-center px-4 py-3 text-blue-600 bg-blue-50 border-r-4 border-blue-600"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="/trading_bots" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="/portfolio" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="/history" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="/settings" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<div class="px-4 mt-8 mb-4">
<p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p>
</div>
<a href="/cryptocurrency" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="/stocks" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="/forex" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center"><i class="fas fa-user text-gray-600"></i></div>
<div class="ml-3 sidebar-text">
<p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p>
<p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p>
</div>
</a>
</div>
</div>
{% extends "base.html" %}
<!-- MAIN CONTENT -->
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
<div class="ml-4 relative">
<input type="text" class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Search markets, bots..." />
<i class="fas fa-search absolute right-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600"><i class="fas fa-bell"></i><span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span></a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300"><i class="fas fa-user text-gray-600"></i></a>
</div>
</div>
</header>
{% block title %}Dashboard{% endblock %}
<main class="p-6">
<div class="mb-6">
<h2 class="text-2xl font-bold text-gray-800">Dashboard</h2>
<p class="text-gray-600">Welcome back! Here's your trading overview.</p>
</div>
{% block head_extra %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/dashboard.css') }}" />
{% endblock %}
<!-- STAT CARDS -->
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4 mb-6">
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-sm font-medium text-gray-500">Total Saldo (Equity)</h3>
<p id="total-equity" class="mt-1 text-3xl font-semibold text-gray-800"><i class="fas fa-spinner fa-spin text-gray-400"></i></p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-sm font-medium text-gray-500">Profit Hari Ini</h3>
<p id="todays-profit" class="mt-1 text-3xl font-semibold text-gray-800"><i class="fas fa-spinner fa-spin text-gray-400"></i></p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-sm font-medium text-gray-500">Bot Aktif</h3>
<p id="active-bots-count" class="mt-1 text-3xl font-semibold text-gray-800"><i class="fas fa-spinner fa-spin text-gray-400"></i></p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-sm font-medium text-gray-500">Total Bot</h3>
<p id="total-bots-count" class="mt-1 text-3xl font-semibold text-gray-800"><i class="fas fa-spinner fa-spin text-gray-400"></i></p>
</div>
</div>
{% block content %}
<div class="mb-6">
<h2 class="text-2xl font-bold text-gray-800">Dashboard</h2>
<p class="text-gray-600">Welcome back! Here's your trading overview.</p>
</div>
<!-- GRAFIK -->
<div class="bg-white rounded-lg shadow p-6 mb-6">
<h3 class="text-md font-semibold text-gray-700 mb-2">Grafik Harga EUR/USD</h3>
<div class="chart-container"><canvas id="priceChart" height="100"></canvas></div>
</div>
<div class="bg-white rounded-lg shadow p-6 mb-6">
<h3 class="text-md font-semibold text-gray-700 mb-2">RSI EUR/USD (H1)</h3>
<div class="chart-container"><canvas id="rsiChart" height="100"></canvas></div>
</div>
<!-- BOT AKTIF -->
<div class="bg-white rounded-lg shadow">
<div class="p-6 border-b">
<h3 class="text-lg font-semibold text-gray-800">Active Trading Bots</h3>
</div>
<div id="active-bots-list" class="divide-y">
<p class="p-4 text-gray-500">Memuat daftar bot...</p>
</div>
<div class="p-4 border-t">
<a href="/trading_bots" class="block w-full text-center text-blue-600 font-medium hover:text-blue-800 transition">View All Bots</a>
</div>
</div>
</main>
</div>
<!-- STAT CARDS -->
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4 mb-6">
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-sm font-medium text-gray-500">Total Saldo (Equity)</h3>
<p id="total-equity" class="mt-1 text-3xl font-semibold text-gray-800"><i class="fas fa-spinner fa-spin text-gray-400"></i></p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-sm font-medium text-gray-500">Profit Hari Ini</h3>
<p id="todays-profit" class="mt-1 text-3xl font-semibold text-gray-800"><i class="fas fa-spinner fa-spin text-gray-400"></i></p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-sm font-medium text-gray-500">Bot Aktif</h3>
<p id="active-bots-count" class="mt-1 text-3xl font-semibold text-gray-800"><i class="fas fa-spinner fa-spin text-gray-400"></i></p>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-sm font-medium text-gray-500">Total Bot</h3>
<p id="total-bots-count" class="mt-1 text-3xl font-semibold text-gray-800"><i class="fas fa-spinner fa-spin text-gray-400"></i></p>
</div>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/dashboard.js') }}"></script>
</body>
</html>
<!-- GRAFIK -->
<div class="bg-white rounded-lg shadow p-6 mb-6">
<h3 class="text-md font-semibold text-gray-700 mb-2">Grafik Harga EUR/USD</h3>
<div class="chart-container"><canvas id="priceChart" height="100"></canvas></div>
</div>
<div class="bg-white rounded-lg shadow p-6 mb-6">
<h3 class="text-md font-semibold text-gray-700 mb-2">RSI EUR/USD (H1)</h3>
<div class="chart-container"><canvas id="rsiChart" height="100"></canvas></div>
</div>
<!-- BOT AKTIF -->
<div class="bg-white rounded-lg shadow">
<div class="p-6 border-b">
<h3 class="text-lg font-semibold text-gray-800">Active Trading Bots</h3>
</div>
<div id="active-bots-list" class="divide-y">
<p class="p-4 text-gray-500">Memuat daftar bot...</p>
</div>
<div class="p-4 border-t">
<a href="/trading_bots" class="block w-full text-center text-blue-600 font-medium hover:text-blue-800 transition">View All Bots</a>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/dashboard.js') }}"></script>
{% endblock %}
+17 -74
View File
@@ -1,75 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notifikasi - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text">
<h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1>
<p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="/" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="/trading_bots" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="/portfolio" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="/history" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="/settings" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="/cryptocurrency" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="/stocks" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="/forex" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center"><i class="fas fa-user text-gray-600"></i></div>
<div class="ml-3 sidebar-text "><p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p><p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p></div>
</a>
</div>
</div>
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
<div class="ml-4 relative"><input type="text" class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Search markets, bots..."><i class="fas fa-search absolute right-3 top-3 text-gray-400"></i></div>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-blue-600">
<i class="fas fa-bell"></i>
<span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span>
</a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300"><i class="fas fa-user text-gray-600"></i></a>
</div>
</div>
</header>
<main class="p-6">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Notifikasi</h2>
<div class="bg-white rounded-lg shadow">
<!-- Kontainer ini akan diisi oleh JavaScript -->
<div id="notifications-container" class="divide-y divide-gray-200">
<p class="p-6 text-center text-gray-500">Memuat notifikasi...</p>
</div>
</div>
</main>
</div>
<!-- templates/notifications.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title %}Notifikasi{% endblock %}
{% block content %}
<h2 class="text-2xl font-bold text-gray-800 mb-6">Notifikasi</h2>
<div class="bg-white rounded-lg shadow">
<!-- Kontainer ini akan diisi oleh JavaScript -->
<div id="notifications-container" class="divide-y divide-gray-200">
<p class="p-6 text-center text-gray-500">Memuat notifikasi...</p>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
</body>
</html>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
{% endblock %}
+53 -99
View File
@@ -1,102 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Portfolio Real-Time - QuantumBotX</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text"><h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1><p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p></div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="/" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="/trading_bots" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="/portfolio" class="nav-item flex items-center px-4 py-3 text-blue-600 bg-blue-50 border-r-4 border-blue-600"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="/history" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="/settings" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<!-- templates/portfolio.html (Setelah Refactor) -->
{% extends "base.html" %}
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="/cryptocurrency" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="/stocks" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="/forex" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center">
<i class="fas fa-user text-gray-600"></i>
</div>
<div class="ml-3 sidebar-text ">
<p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p>
<p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p>
</div>
</a>
</div>
</div>
<!-- Konten Utama -->
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600"><i class="fas fa-bell"></i><span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span></a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300"><i class="fas fa-user text-gray-600"></i></a>
</div>
</div>
</header>
<main class="p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Portfolio Real-Time</h2>
<p class="text-gray-600">Posisi trading yang sedang terbuka di akun MetaTrader 5.</p>
</div>
<div id="portfolio-summary" class="text-right">
<!-- Summary akan dimuat di sini -->
</div>
</div>
<div class="mb-6 bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold text-gray-800 mb-2">Grafik Profit/Loss Terbuka (Real-Time)</h3>
<div style="height: 250px;"><canvas id="pnlChart"></canvas></div>
</div>
{% block title %}Portfolio Real-Time{% endblock %}
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Simbol</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tipe</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Volume</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga Buka</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Profit/Loss</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Magic Number</th>
</tr>
</thead>
<tbody id="portfolio-table-body" class="bg-white divide-y divide-gray-200">
<tr><td colspan="6" class="p-4 text-center text-gray-500">Memuat posisi terbuka...</td></tr>
</tbody>
</table>
</div>
</main>
</div>
{% block head_extra %}
<!--
Chart.js sudah ada di base.html. Kita hanya perlu menambahkan adapter tanggal.
Namun, lebih baik memuat semua JS di akhir, jadi kita akan letakkan ini di blok 'scripts'.
Blok ini dibiarkan sebagai contoh jika butuh CSS khusus.
-->
{% endblock %}
{% block content %}
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Portfolio Real-Time</h2>
<p class="text-gray-600">Posisi trading yang sedang terbuka di akun MetaTrader 5.</p>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/portfolio.js') }}"></script>
</body>
</html>
<div id="portfolio-summary" class="text-right">
<!-- Summary akan dimuat di sini -->
</div>
</div>
<div class="mb-6 bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold text-gray-800 mb-2">Grafik Profit/Loss Terbuka (Real-Time)</h3>
<div style="height: 250px;"><canvas id="pnlChart"></canvas></div>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Simbol</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tipe</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Volume</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga Buka</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Profit/Loss</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Magic Number</th>
</tr>
</thead>
<tbody id="portfolio-table-body" class="bg-white divide-y divide-gray-200">
<tr><td colspan="6" class="p-4 text-center text-gray-500">Memuat posisi terbuka...</td></tr>
</tbody>
</table>
</div>
{% endblock %}
{% block scripts %}
<!--
Memuat adapter tanggal untuk Chart.js yang diperlukan di halaman ini,
diikuti dengan skrip logika halaman portfolio.
-->
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<script src="{{ url_for('static', filename='js/portfolio.js') }}"></script>
{% endblock %}
+45 -102
View File
@@ -1,106 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Profil - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text">
<h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1>
<p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="/" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="/trading_bots" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="/portfolio" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="/history" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="/settings" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="/cryptocurrency" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="/stocks" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="/forex" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center"><i class="fas fa-user text-gray-600"></i></div>
<div class="ml-3 sidebar-text "><p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p><p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p></div>
</a>
</div>
</div>
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
<div class="ml-4 relative"><input type="text" class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Search markets, bots..."><i class="fas fa-search absolute right-3 top-3 text-gray-400"></i></div>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600">
<i class="fas fa-bell"></i>
<span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span>
</a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300"><i class="fas fa-user text-gray-600"></i></a>
</div>
</div>
</header>
<main class="p-6">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Profil Saya</h2>
<!-- templates/profile.html (Setelah Refactor) -->
{% extends "base.html" %}
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="md:col-span-1">
<div class="bg-white p-6 rounded-lg shadow text-center">
<div class="w-24 h-24 rounded-full bg-gray-200 flex items-center justify-center mx-auto mb-4">
<i class="fas fa-user fa-3x text-gray-500"></i>
</div>
<h3 id="profile-display-name" class="text-xl font-bold text-gray-800">Memuat...</h3>
<p class="text-sm text-gray-500">Developer</p>
<p id="profile-join-date" class="text-xs text-gray-400 mt-2">Bergabung sejak: Juli 2025</p>
</div>
</div>
{% block title %}Profil{% endblock %}
<div class="md:col-span-2">
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4">Detail Akun</h3>
<form id="profile-form" class="space-y-4">
<div>
<label for="profile-name" class="text-sm font-medium text-gray-700">Nama Lengkap</label>
<input type="text" id="profile-name" class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm" required>
</div>
<div>
<label for="profile-email" class="text-sm font-medium text-gray-700">Alamat Email</label>
<input type="email" id="profile-email" class="mt-1 block w-full px-3 py-2 bg-gray-100 border border-gray-300 rounded-md" required>
</div>
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4 pt-6">Keamanan</h3>
<div>
<label for="profile-password" class="text-sm font-medium text-gray-700">Ganti Password (kosongkan jika tidak ingin diubah)</label>
<input type="password" id="profile-password" placeholder="••••••••" class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm">
</div>
<div class="flex justify-end pt-4">
<button type="submit" class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700">Simpan Perubahan</button>
</div>
</form>
</div>
</div>
</div>
</main>
{% block content %}
<h2 class="text-2xl font-bold text-gray-800 mb-6">Profil Saya</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="md:col-span-1">
<div class="bg-white p-6 rounded-lg shadow text-center">
<div class="w-24 h-24 rounded-full bg-gray-200 flex items-center justify-center mx-auto mb-4">
<i class="fas fa-user fa-3x text-gray-500"></i>
</div>
<h3 id="profile-display-name" class="text-xl font-bold text-gray-800">Memuat...</h3>
<p class="text-sm text-gray-500">Developer</p>
<p id="profile-join-date" class="text-xs text-gray-400 mt-2">Bergabung sejak: Juli 2025</p>
</div>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/profile.js') }}"></script>
</body>
</html>
<div class="md:col-span-2">
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4">Detail Akun</h3>
<form id="profile-form" class="space-y-4">
<div>
<label for="profile-name" class="text-sm font-medium text-gray-700">Nama Lengkap</label>
<input type="text" id="profile-name" class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm" required>
</div>
<div>
<label for="profile-email" class="text-sm font-medium text-gray-700">Alamat Email</label>
<input type="email" id="profile-email" class="mt-1 block w-full px-3 py-2 bg-gray-100 border border-gray-300 rounded-md" required>
</div>
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4 pt-6">Keamanan</h3>
<div>
<label for="profile-password" class="text-sm font-medium text-gray-700">Ganti Password (kosongkan jika tidak ingin diubah)</label>
<input type="password" id="profile-password" placeholder="••••••••" class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm">
</div>
<div class="flex justify-end pt-4">
<button type="submit" class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700">Simpan Perubahan</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/profile.js') }}"></script>
{% endblock %}
+28 -131
View File
@@ -1,137 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Settings - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">
QT
<!-- templates/settings.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title %}Settings{% endblock %}
{% block content %}
<h2 class="text-2xl font-bold text-gray-800 mb-6">Pengaturan</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2 space-y-6">
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4">Profil</h3>
<div class="space-y-4">
<div>
<label class="text-sm font-medium text-gray-700">Nama Lengkap</label>
<input type="text" value="Reynov Christian" class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
<div class="ml-3 logo-text">
<h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1>
<p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p>
<div>
<label class="text-sm font-medium text-gray-700">Alamat Email</label>
<input type="email" value="contact@chrisnov.com" class="mt-1 block w-full px-3 py-2 bg-gray-100 border border-gray-300 rounded-md shadow-sm" readonly>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4">
<p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p>
</div>
<a href="/" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50">
<i class="fas fa-chart-line"></i>
<span class="ml-3 sidebar-text">Dashboard</span>
</a>
<a href="/trading_bots" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50">
<i class="fas fa-robot"></i>
<span class="ml-3 sidebar-text">Trading Bots</span>
</a>
<a href="/portfolio" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50">
<i class="fas fa-wallet"></i>
<span class="ml-3 sidebar-text">Portfolio</span>
</a>
<a href="/history" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50">
<i class="fas fa-history"></i>
<span class="ml-3 sidebar-text">History</span>
</a>
<a href="/settings" class="nav-item flex items-center px-4 py-3 text-blue-600 bg-blue-50 border-r-4 border-blue-600">
<i class="fas fa-cog"></i>
<span class="ml-3 sidebar-text">Settings</span>
</a>
<div class="px-4 mt-8 mb-4">
<p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p>
</div>
<a href="/cryptocurrency" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50">
<i class="fab fa-bitcoin"></i>
<span class="ml-3 sidebar-text">Cryptocurrency</span>
</a>
<a href="/stocks" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50">
<i class="fas fa-chart-bar"></i>
<span class="ml-3 sidebar-text">Stocks</span>
</a>
<a href="/forex" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50">
<i class="fas fa-exchange-alt"></i>
<span class="ml-3 sidebar-text">Forex</span>
</a>
</nav>
</div>
<div class="p-4 border-t">
<a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center">
<i class="fas fa-user text-gray-600"></i>
</div>
<div class="ml-3 sidebar-text ">
<p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p>
<p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p>
</div>
</a>
<button class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700">Simpan Perubahan</button>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 overflow-auto">
<!-- Top Navigation -->
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none">
<i class="fas fa-bars"></i>
</button>
<div class="ml-4 relative">
<input type="text" class="w-64 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Search markets, bots...">
<i class="fas fa-search absolute right-3 top-3 text-gray-400"></i>
</div>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600">
<i class="fas fa-bell"></i>
<span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span>
</a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300">
<i class="fas fa-user text-gray-600"></i>
</a>
</div>
</div>
</header>
<main class="p-6">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Pengaturan</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2 space-y-6">
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4">Profil</h3>
<div class="space-y-4">
<div>
<label class="text-sm font-medium text-gray-700">Nama Lengkap</label>
<input type="text" value="Reynov Christian" class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500">
</div>
<div>
<label class="text-sm font-medium text-gray-700">Alamat Email</label>
<input type="email" value="contact@chrisnov.com" class="mt-1 block w-full px-3 py-2 bg-gray-100 border border-gray-300 rounded-md shadow-sm" readonly>
</div>
<button class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700">Simpan Perubahan</button>
</div>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4">API Keys Bursa</h3>
</div>
</div>
</div>
</main>
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4">API Keys Bursa</h3>
<!-- Konten untuk API Keys bisa ditambahkan di sini -->
</div>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/settings.js') }}"></script>
</body>
</html>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/settings.js') }}"></script>
{% endblock %}
+45 -94
View File
@@ -1,100 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stocks Market - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text">
<h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1>
<p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="{{ url_for('dashboard') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="{{ url_for('bots_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="{{ url_for('portfolio_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="{{ url_for('history_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="{{ url_for('settings_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="{{ url_for('crypto_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="{{ url_for('stocks_page') }}" class="nav-item flex items-center px-4 py-3 text-blue-600 bg-blue-50 border-r-4 border-blue-600"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="{{ url_for('forex_page') }}" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="{{ url_for('profile_page') }}" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center"><i class="fas fa-user text-gray-600"></i></div>
<div class="ml-3 sidebar-text "><p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p><p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p></div>
</a>
</div>
</div>
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
</div>
</div>
</header>
<main class="p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Pasar Saham</h2>
<p class="text-gray-600">Data harga real-time dari pasar saham global.</p>
</div>
</div>
<!-- templates/stocks.html (Setelah Refactor) -->
{% extends "base.html" %}
<div class="bg-white rounded-lg shadow overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Saham</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Perubahan 24j</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Waktu</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th>
</tr>
</thead>
<tbody id="stocks-table-body" class="bg-white divide-y divide-gray-200">
<tr><td colspan="5" class="p-4 text-center text-gray-500">Memuat data saham...</td></tr>
</tbody>
</table>
</div>
</main>
</div>
{% block title %}Stocks Market{% endblock %}
{% block content %}
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Pasar Saham</h2>
<p class="text-gray-600">Data harga real-time dari pasar saham global.</p>
</div>
</div>
<!-- Modal -->
<div id="stock-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full hidden">
<div class="relative top-20 mx-auto p-5 border w-1/2 shadow-lg rounded-md bg-white">
<div class="mt-3 text-center">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title"></h3>
<div class="mt-2 px-7 py-3">
<p class="text-sm text-gray-500" id="modal-content"></p>
</div>
<div class="items-center px-4 py-3">
<button id="close-modal" class="px-4 py-2 bg-gray-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-300">
Close
</button>
</div>
<div class="bg-white rounded-lg shadow overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Saham</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Perubahan 24j</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Waktu</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th>
</tr>
</thead>
<tbody id="stocks-table-body" class="bg-white divide-y divide-gray-200">
<tr><td colspan="5" class="p-4 text-center text-gray-500">Memuat data saham...</td></tr>
</tbody>
</table>
</div>
<!-- Modal -->
<div id="stock-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full hidden">
<div class="relative top-20 mx-auto p-5 border w-1/2 shadow-lg rounded-md bg-white">
<div class="mt-3 text-center">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title"></h3>
<div class="mt-2 px-7 py-3">
<p class="text-sm text-gray-500" id="modal-content"></p>
</div>
<div class="items-center px-4 py-3">
<button id="close-modal" class="px-4 py-2 bg-gray-500 text-white text-base font-medium rounded-md w-full shadow-sm hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-300">
Close
</button>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/stocks.js') }}"></script>
</body>
</html>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/stocks.js') }}"></script>
{% endblock %}
+40 -93
View File
@@ -1,94 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Trading Bots - QuantumBotX</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-white w-64 shadow-lg flex flex-col">
<div class="p-4 flex items-center border-b">
<div class="w-10 h-10 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold">QT</div>
<div class="ml-3 logo-text"><h1 class="text-xl font-bold text-gray-800">QuantumBotX</h1><p class="text-xs text-gray-500">The AI-Powered Strategy Platform</p></div>
</div>
<div class="flex-1 overflow-y-auto">
<nav class="mt-6">
<div class="px-4 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Navigation</p></div>
<a href="/" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-line"></i><span class="ml-3 sidebar-text">Dashboard</span></a>
<a href="/trading_bots" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-robot"></i><span class="ml-3 sidebar-text">Trading Bots</span></a>
<a href="/portfolio" class="nav-item flex items-center px-4 py-3 text-blue-600 bg-blue-50 border-r-4 border-blue-600"><i class="fas fa-wallet"></i><span class="ml-3 sidebar-text">Portfolio</span></a>
<a href="/history" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-history"></i><span class="ml-3 sidebar-text">History</span></a>
<a href="/settings" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-cog"></i><span class="ml-3 sidebar-text">Settings</span></a>
{% extends "base.html" %}
<div class="px-4 mt-8 mb-4"><p class="text-xs font-semibold text-gray-500 uppercase tracking-wider sidebar-text">Market Data</p></div>
<a href="/cryptocurrency" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fab fa-bitcoin"></i><span class="ml-3 sidebar-text">Cryptocurrency</span></a>
<a href="/stocks" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-chart-bar"></i><span class="ml-3 sidebar-text">Stocks</span></a>
<a href="/forex" class="nav-item flex items-center px-4 py-3 text-gray-600 hover:text-blue-600 hover:bg-blue-50"><i class="fas fa-exchange-alt"></i><span class="ml-3 sidebar-text">Forex</span></a>
</nav>
</div>
<div class="p-4 border-t">
<a href="/profile" class="flex items-center group">
<div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center">
<i class="fas fa-user text-gray-600"></i>
</div>
<div class="ml-3 sidebar-text ">
<p class="text-sm font-medium text-gray-800 group-hover:text-blue-600">Reynov Christian</p>
<p class="text-xs text-gray-500 group-hover:text-blue-600">Developer</p>
</div>
</a>
</div>
</div>
<!-- Konten Utama -->
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm">
<div class="flex items-center justify-between px-6 py-4">
<div class="flex items-center">
<button id="sidebar-toggle" class="text-gray-500 focus:outline-none"><i class="fas fa-bars"></i></button>
</div>
<div class="flex items-center space-x-4">
<a href="/notifications" class="relative text-gray-500 hover:text-blue-600"><i class="fas fa-bell"></i><span id="notification-dot" class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500 hidden"></span></a>
<a href="/profile" class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center hover:bg-gray-300"><i class="fas fa-user text-gray-600"></i></a>
</div>
</div>
</header>
<main class="p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Trading Bots</h2>
<p class="text-gray-600">Kelola semua bot trading aktif dan non-aktif Anda.</p>
</div>
<div class="flex items-center space-x-2">
<button id="start-all-btn" class="bg-green-500 text-white py-2 px-4 rounded-lg font-medium hover:bg-green-600 transition flex items-center" title="Jalankan semua bot yang dijeda">
<i class="fas fa-play mr-2"></i> Start All
</button>
<button id="stop-all-btn" class="bg-red-500 text-white py-2 px-4 rounded-lg font-medium hover:bg-red-600 transition flex items-center" title="Hentikan semua bot yang aktif">
<i class="fas fa-stop mr-2"></i> Stop All
</button>
<button id="create-bot-btn" class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700 transition flex items-center"><i class="fas fa-plus mr-2"></i> Buat Bot Baru</button>
</div>
</div>
{% block title %}Trading Bots{% endblock %}
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nama / Pasar</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Parameter</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Konfigurasi</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th>
</tr>
</thead>
<tbody id="bots-table-body" class="bg-white divide-y divide-gray-200"></tbody>
</table>
</div>
</main>
{% block head_extra %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/dashboard.css') }}" />
{% endblock %}
{% block content %}
<!-- Header Halaman: Judul di kiri, tombol di kanan -->
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-2xl font-bold text-gray-800">Trading Bots</h2>
<p class="text-gray-600">Kelola semua bot dan strategi Anda.</p>
</div>
<div class="flex items-center space-x-2">
<button id="start-all-btn" class="bg-green-500 text-white py-2 px-4 rounded-lg font-medium hover:bg-green-600 transition flex items-center" title="Jalankan semua bot yang dijeda">
<i class="fas fa-play mr-2"></i> Start All</button>
<button id="stop-all-btn" class="bg-red-500 text-white py-2 px-4 rounded-lg font-medium hover:bg-red-600 transition flex items-center" title="Hentikan semua bot yang aktif">
<i class="fas fa-stop mr-2"></i> Stop All</button>
<button id="create-bot-btn" class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700 transition flex items-center"><i class="fas fa-plus mr-2"></i> Buat Bot Baru</button>
</div>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full divide-y divide-gray-200" aria-label="Trading Bots Table">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Nama / Pasar</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Parameter</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Konfigurasi</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th scope="col" class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th>
</tr>
</thead>
<tbody id="bots-table-body" class="bg-white divide-y divide-gray-200"></tbody>
</table>
</div>
<!-- Modal untuk Membuat/Mengedit Bot -->
<div id="create-bot-modal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full flex items-center justify-center z-50 p-4">
<!-- PERBAIKAN: Panel modal kini menggunakan flexbox vertikal dan tinggi maksimal -->
@@ -173,7 +119,8 @@
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
<script src="{{ url_for('static', filename='js/trading_bots.js') }}"></script>
</body>
</html>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/trading_bots.js') }}"></script>
{% endblock %}