feat: add i18n support and prepare for Vercel deployment

- Streamlined .env.example to include only essential Flask and MT5 configs for production environments
- Added .vercel directory to .gitignore for clean Vercel deployments
- Enabled internationalization by adding data-i18n attributes to strategy switcher UI elements
- Updated MT5 setup guide and roadmap documentation for clarity and current focus

This change simplifies configuration for server deployment while enhancing UI accessibility across languages.
This commit is contained in:
Reynov Christian
2025-10-16 01:47:55 +08:00
parent 79d860e9a8
commit 5b41fdea95
49 changed files with 4246 additions and 999 deletions
+9 -31
View File
@@ -1,34 +1,12 @@
# Flask Configuration
FLASK_APP=run.py
FLASK_ENV=development
FLASK_DEBUG=True
FLASK_DEBUG=False
FLASK_HOST=127.0.0.1
FLASK_PORT=5000
# Server Configuration
HOST=0.0.0.0
PORT=5000
# MT5 Configuration (required for local development)
MT5_LOGIN=your_mt5_account_number
MT5_PASSWORD=your_mt5_password
MT5_SERVER=your_mt5_server
# Database Configuration (SQLite default)
DATABASE_URL=sqlite:///database.db
# MT5 Configuration (REQUIRED for data downloading)
MT5_LOGIN=your_mt5_account_number_here
MT5_PASSWORD=your_mt5_password_here
MT5_SERVER=your_mt5_broker_server_here
# Example for FBS Demo: FBS-Demo
# Example for XM Global: XMGlobal-ServerName
# MT5 Installation Paths (OPTIONAL - auto-detected if not specified)
MT5_TERMINAL_PATH=C:\Program Files\MetaTrader 5
MT5_DATA_PATH=%APPDATA%\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075
# Advanced MT5 Settings
MT5_TIMEOUT_SECONDS=60
# Backtesting Configuration
BACKTEST_LOG_LEVEL=INFO
# Application Settings
SECRET_KEY=your-secret-key-here-make-it-very-long-and-random
# Additional MT5 Symbols (comma-separated)
CUSTOM_MT5_SYMBOLS=GBPAUD,NZDCAD,USDMXN
# Skip MT5 initialization in deployment environments (Vercel, etc.)
SKIP_MT5_INIT=0
+2
View File
@@ -90,3 +90,5 @@ DESKTOP_DEPLOYMENT.md
INVESTMENT_RETURN.md
PAYMENT_SETUP.md
STRATEGY_IDEAS.md
.vercel
+1
View File
@@ -1,3 +1,4 @@
/** eslint-disable markdown/fenced-code-language */
# 🚀 QuantumBotX MT5 Integration Guide
> **Get Historical Market Data for Advanced Backtesting**
+194
View File
@@ -0,0 +1,194 @@
# QuantumBotX Windows Packaging Guide
This guide explains how to package your QuantumBotX trading application into a Windows installer for distribution to non-technical users.
## 🎯 What This Packaging Solution Provides
**Single EXE Installer** - Professional Windows installer
**One-Click Installation** - Simple for end users
**Desktop Shortcuts** - Easy application access
**Uninstaller** - Clean removal capability
**Setup Wizard** - Automated first-time setup
**User-Friendly Scripts** - Simple startup process
**Comprehensive Documentation** - Clear instructions for users
## 📋 Prerequisites
### For Building the Package
1. **Python 3.8+** (already installed)
2. **PyInstaller** (already installed via this guide)
3. **NSIS (Optional)** - For creating the installer EXE
- Download from: https://nsis.sourceforge.io/Download
- Install and ensure `makensis` is in your PATH
### For End Users
1. **Windows 7+** (64-bit recommended)
2. **MetaTrader 5** - Must be installed separately
3. **Internet Connection** - For initial setup
## 🚀 Quick Start
### Option 1: Build Everything (Recommended)
```bash
python build_installer.py
```
### Option 2: Manual Build Process
```bash
# 1. Build with PyInstaller
pyinstaller --clean quantumbotx.spec
# 2. Create portable version (optional)
python -c "import zipfile; zipf=zipfile.ZipFile('QuantumBotX-Portable.zip','w',8); [zipf.write(f,f) for d in ['dist/QuantumBotX','.'] for f in [os.path.join(r,d) for r,_,fs in os.walk(d) for f in fs]]"
# 3. Create installer (requires NSIS)
makensis installer.nsi
```
## 📁 Generated Files
After successful build, you'll have:
```txt
QuantumBotX-Installer.exe # Main Windows installer
QuantumBotX-Portable.zip # Portable version (alternative)
dist/QuantumBotX/ # PyInstaller output (for troubleshooting)
```
## 🎮 For End Users
### Installation Process
1. **Download** `QuantumBotX-Installer.exe`
2. **Run** the installer (requires admin privileges)
3. **Follow** the setup wizard
4. **Launch** from desktop shortcut or start menu
### Daily Usage
1. **Start MetaTrader 5** first
2. **Launch QuantumBotX** via desktop shortcut or start menu
3. **Open browser** to http://127.0.0.1:5000
4. **Configure settings** if needed
## 🔧 Configuration Files
The installer includes these configuration files:
- **`.env.example`** - Template for environment variables
- **`start.bat`** - Windows startup script
- **`setup_quantumbotx.py`** - Setup wizard for first run
- **`QUICK_START_GUIDE.md`** - User instructions
## 🛠️ Troubleshooting
### Build Issues
**PyInstaller fails:**
```bash
# Clean and rebuild
pyinstaller --clean quantumbotx.spec
```
**NSIS not found:**
- Install NSIS from https://nsis.sourceforge.io/
- Or use the portable version instead
**Missing dependencies:**
```bash
pip install -r requirements.txt
```
### Runtime Issues
**Application won't start:**
- Check if MetaTrader 5 is running
- Verify `.env` file has correct credentials
- Check Windows Event Viewer for errors
**Port already in use:**
- Close other applications using port 5000
- Or modify `FLASK_PORT` in `.env` file
**MetaTrader 5 connection fails:**
- Verify MT5 credentials in `.env`
- Ensure MT5 is running and logged in
- Check MT5 terminal for connection status
## 📦 Distribution
### For Technical Users
- Share `QuantumBotX-Installer.exe` for full installation
- Or share `QuantumBotX-Portable.zip` for manual installation
### For Non-Technical Users
1. **Share the installer** `QuantumBotX-Installer.exe`
2. **Provide simple instructions:**
- Double-click to install
- Follow the setup wizard
- Use desktop shortcut to launch
- Ensure MetaTrader 5 is running
### System Requirements for End Users
- **OS:** Windows 7 SP1+ (64-bit recommended)
- **RAM:** 4GB minimum, 8GB recommended
- **Storage:** 500MB free space
- **Network:** Internet connection for initial setup
- **Software:** MetaTrader 5 (installed separately)
## 🔄 Updates and Maintenance
### Creating Updates
1. **Increment version** in `installer.nsi`
2. **Rebuild** using `python build_installer.py`
3. **Test** on clean system
4. **Distribute** new installer
### Uninstallation
- Use Windows Add/Remove Programs
- Or run `Uninstall.exe` from installation directory
- Or use the uninstall shortcut in Start Menu
## 🆘 Support
### Common User Questions
**"How do I configure my MT5 credentials?"**
- Copy `.env.example` to `.env`
- Edit `.env` with your MT5 account details
- Restart the application
**"The application says MT5 is not connected"**
- Ensure MetaTrader 5 is running
- Check MT5 login credentials
- Verify MT5 server settings
**"I can't access the web interface"**
- Check if the application is running
- Verify the URL: http://127.0.0.1:5000
- Check firewall settings
## 🎯 Advanced Configuration
### Customizing the Installer
Edit `installer.nsi` to:
- Change installation directory
- Modify shortcuts
- Add custom messages
- Include additional files
### Customizing PyInstaller
Edit `quantumbotx.spec` to:
- Add/remove files
- Change executable properties
- Modify hidden imports
- Customize build options
## 📞 Getting Help
1. **Check the logs** in the `logs/` directory
2. **Review** `QUICK_START_GUIDE.md`
3. **Consult** `README.md` for technical details
4. **Check** MetaTrader 5 documentation for connection issues
---
**🎉 Your application is now ready for distribution to non-technical users!**
+47
View File
@@ -0,0 +1,47 @@
# QuantumBotX - Quick Start Guide
## First Time Setup
1. **Install MetaTrader 5** (Required)
- Download from: https://www.metatrader5.com/
- Install and create a demo account
- Keep MT5 running in the background
- ⚠️ **IMPORTANT:** MetaTrader 5 must be running for QuantumBotX to work
2. **Configure Your Settings**
- Copy `.env.example` to `.env`
- Edit `.env` with your MT5 credentials:
```ini
MT5_LOGIN=your_account_number
MT5_PASSWORD=your_password
MT5_SERVER=your_server_name
```
3. **Start the Application**
- Double-click `start.bat` (Windows)
- Open http://127.0.0.1:5000 in your browser
## ✅ System Requirements
- **Windows 7 SP1 or later** (64-bit recommended)
- **MetaTrader 5** (must be installed separately)
- **4GB RAM minimum** (8GB recommended)
- **500MB free disk space**
- **Internet connection** for initial setup
- **❌ Python NOT required** (already bundled in the installer)
## Daily Use
1. Start MetaTrader 5 first
2. Run `start.bat`
3. Open your web browser to http://127.0.0.1:5000
## Troubleshooting
- Make sure MetaTrader 5 is running
- Check your .env file has correct credentials
- If the app won't start, try running `python run.py` directly
## Support
If you need help, check the README.md file or contact support.
+40 -206
View File
@@ -1,218 +1,52 @@
# 🚀 QuantumBotX Product Roadmap 2025-2026
# 📋 QuantumBotX Development Roadmap
> **From Local MT5 Platform → Global Cloud Trading System**
**👀 Stay Tuned for Exciting Updates!**
## 🎯 **What's Coming Next**
### **Q4 2025: Intelligence Enhancement**
- **Advanced AI Features**: Enhanced strategy analysis with machine learning
- **Real-time Notifications**: Telegram integration for trade alerts
- **Portfolio Analytics**: Advanced performance dashboards
- **Enterprise Features**: Multi-account management and compliance logging
### **Exciting New Project** 🚀
We're developing **QuantumBotX API** - a revolutionary cloud-based trading platform that will give users unprecedented freedom:
> **"Trade anywhere, anytime, with any broker - no local installations required!"**
**QuantumBotX API will feature:**
- 🌐 **Cloud-native architecture** - Run on any device, anywhere
- 🔄 **Direct broker integration** - No intermediaries, pure API trading
- 🌏 **Cross-broker support** - IC Markets, Pepperstone, and beyond
-**Real-time execution** - Ultra-low latency trade processing
- 🎓 **Advanced education** - Built-in learning with community support
### **Timeline**
- **Q4 2025**: Closed beta testing with select users
- **Q1 2026**: Public beta launch with premium support
- **Q2 2026**: Full global launch with subscription tiers
---
## 📍 **Current Status: v2.0.3 - Global Expansion Complete**
**Multilingual Support**: Indonesian & English fully implemented
**Data Integration**: Smart MT5 detection with web controls
**Professional Documentation**: Comprehensive setup guides
**User Experience**: Streamlined onboarding and settings
## 🤝 **Community & Support**
**We're building more than software - we're building a trading community!**
- **Discord Community**: Join our growing trader community
- **Educational Content**: Free trading courses and tutorials
- **Open Source**: Contribute to the project and shape its future
- **Mentorship Program**: One-on-one guidance for serious traders
---
## 🎯 **Q4 2025: Stability & Market Penetration**
## 🗺️ **Our Mission**
### **v2.1 - "Intelligence Enhancement"** (3 Months)
**Empowering traders worldwide with safe, educational, and profitable algorithmic trading solutions.**
#### 🧠 **AI & Analytics Features**
-**MACD_STOCH_FILTER Strategy**: Advanced precision entry system
-**Telegram Notifications**: Real-time alerts for trades/errors
-**Smart Parameter Optimization**: AI-assisted strategy tuning
-**Portfolio Analytics Dashboard**: Advanced risk/correlation analysis
-**Enhanced Market Regime Detection**: Multi-timeframe market analysis
#### 📊 **Advanced Reporting**
-**Performance Heatmaps**: Strategy performance visualization
-**Risk Attribution Analysis**: Which factors affect returns most
-**Monthly Reporting**: Automated performance summaries
-**Benchmarking System**: Compare against market indices
#### 🔧 **Platform Improvements**
-**Multi-Account Management**: Handle multiple trading accounts
-**Strategy Cloning**: Duplicate and modify existing strategies
-**Bulk Operations**: Start/stop multiple bots simultaneously
-**API Rate Limiting**: Prevent excessive broker API calls
### **Market Goals Q4**
- **GitHub Release**: Open-source launch with 200+ stars
- **Community Building**: Discord/Telegram groups with 500+ members
- **Education Focus**: Launch beginner YouTube channel
- **Broker Partnerships**: Initial deals with 2-3 brokers
*From local learning platform → Global trading ecosystem!* 🚀💫
---
## 🌟 **Q1 2026: Enterprise Features & API Foundation**
### **v2.2 - "Enterprise Ready"** (3-4 Months)
#### 🏢 **Enterprise Features**
-**User Roles & Permissions**: Admin, Trader, Viewer roles
-**Audit Logging**: Complete action history for compliance
-**White-label Support**: Custom branding for firms
-**API Documentation**: REST endpoints for integrations
#### 🔗 **External Integrations**
-**Discord Webhooks**: Trading alerts to Discord channels
-**Slack Notifications**: Team trading notifications
-**Email Automation**: Professional performance reports
-**Webhooks**: Custom integration support
### **QuantumBotX API Research & Planning** ⚡
**Timeline: Q1 2026**
- **Market Research**: Identify best broker API partners (IC Markets, Pepperstone focus)
- **Technical Architecture**: Design microservices for cloud deployment
- **Security Planning**: API key management and authentication systems
- **Prototype Development**: Proof-of-concept API trading endpoints
### **Market Goals Q1**
- **10,000 Downloads**: Aggressive growth with community marketing
- **Enterprise Pilot**: First institutional client deployment
- **Language Expansion**: Chinese interface preparation
- **Subscription Model**: Freemium to pro upgrade conversion testing
---
## 🚀 **Q2-Q3 2026: API Platform Launch**
### **QuantumBotX API v1.0 - "Cloud Liberation"** 🌐
#### 🎯 **Core API Features**
-**Direct Broker API Integration**: No MT5 terminal required!
-**Cross-Platform Support**: Windows, Mac, Linux, VPS, Cloud
-**Multi-Broker Support**: IC Markets, Pepperstone, Key brokers
-**24/7 Trading**: Server-based operation
#### 🏗️ **Technical Implementation**
-**Microservices Architecture**: Website (Flask) + Trading Engine (FastAPI)
-**Docker Orchestration**: Containerized deployment
-**WebSocket Streaming**: Real-time market data
-**REST API Trading**: Place/exit trades programmatically
#### 💰 **Supported Brokers (Phase 1)**
-**IC Markets**: Primary focus, excellent API documentation
-**Pepperstone**: Enterprise-grade API support
-**Deriv**: Binary/forex options integration
-**Additional**: Research and partnership development
#### 🌐 **Business Model**
-**Free Tier**: Basic features for individual traders
-**Pro Subscription**: $29/month - Unlimited brokers/signals
-**Enterprise Tier**: $299/month - White-label + custom features
-**Broker Partnerships**: Revenue sharing per active trader
### **Current MT5 Version (Maintenance Mode)**
-**Continued Support**: Windows users still supported
-**Learning Path**: Beginner education funnel to API version
-**Feature Parity**: API features backported where possible
### **API Launch Strategy**
1. **Closed Beta**: 100 users, fee waivers for feedback
2. **Public Beta**: 1,000 users, premium support
3. **Full Launch**: Global availability with paid subscriptions
4. **Partnership Program**: Broker incentives for integrations
---
## 📊 **Q4 2026: Scale & Expansion**
### **QuantumBotX API v2.0 - "Global Domination"** 🌍
#### 🌏 **International Expansion**
-**Multi-Language Support**: Chinese, Arabic, Spanish, Russian
-**Regional Broker Focus**: Local broker partnerships
-**Cultural Customization**: Holiday awareness per region
-**Localized Education**: Region-specific trading content
#### 📈 **Advanced Features**
-**AI Strategy Optimization**: ML-based parameter tuning
-**Portfolio Management**: Multi-strategy allocation
-**Arbitrage Engine**: Cross-broker opportunity scanning
-**Community Marketplace**: User-created strategy sharing
#### 💰 **Revenue Targets**
- **Base**: 5,000 paid subscribers @ $29/month ($1.74M ARR)
- **Enterprise**: 20 clients @ $299/month ($718K ARR)
- **Total Target**: $2.45M ARR by end of 2026
---
## 🎯 **Key Success Metrics**
### **Technical KPIs**
-**99.9% Uptime**: Cloud-based reliability
-**<5ms Latency**: Low-latency trade execution
-**Zero Broker Disconnects**: Robust connection handling
-**Real-time Performance**: Sub-second signal generation
### **Business KPIs**
-**Community Size**: 10,000+ active traders
-**Market Coverage**: 50+ countries reached
-**Broker Partnerships**: 10+ integrated brokers
-**Conversion Rate**: 15% free → paid upgrade rate
### **Product KPIs**
-**Strategy Performance**: Consistently beating benchmarks
-**User Satisfaction**: 4.8+ star ratings
-**Educational Value**: Proven learning outcomes
-**Risk Management**: Zero significant account losses
---
## 💡 **Key Competitive Advantages**
### **Vs Traditional Platforms**
-**Education First**: Learning-focused vs profit-focused
-**Conservative Approach**: Risk management prioritized
-**Cultural Intelligence**: Indonesian market psychology
### **Vs Other Trading Bots**
-**Multi-Broker Freedom**: Not locked to one platform
-**Strategy Transparency**: Open-source strategy evaluation
-**Community Building**: Collaborative improvement
### **Vs API-Only Solutions**
-**Complete Platform**: Not just APIs, full trading system
-**Educational Framework**: Learning built-in
-**Risk Management**: Conservative by default
---
## 🎖️ **Development Principles**
### **User-Centric Development**
- **Safety First**: Every feature vetted for risk management
- **Education Priority**: Learning > Profits mindset
- **Inclusive Design**: Support traders of all experience levels
### **Technical Excellence**
- **Microservices**: Scalable, maintainable architecture
- **Security First**: Financial data protection paramount
- **Performance Obsessed**: Low latency, high reliability
### **Sustainable Growth**
- **Open Source Roots**: Community contributions welcomed
- **Transparent Pricing**: No surprise fees or commissions
- **Ethical Practices**: No predatory monetization
---
## 🌟 **Mission Achievement Timeline**
### **2025 Goals**
- ✅ Launch Windows MT5 platform
- ✅ Build Indonesian trading community
- ✅ Establish educational reputation
- ✅ Research API broker partnerships
### **2026 Goals**
- 🚧 Launch QuantumBotX API platform
- 🚧 Achieve 10,000+ active users
- 🚧 Generate $2M+ annual revenue
- 🚧 Become leading algorithmic trading education platform
---
**Roadmap Last Updated: September 2025**
*Version: Comprehensive Expansion Plan* 🚀🔮
**Roadmap Updated: September 2025**
</content>
+10
View File
@@ -0,0 +1,10 @@
import os
from core import create_app
# Set environment to skip MT5 initialization on Vercel
os.environ['SKIP_MT5_INIT'] = '1'
app = create_app()
if __name__ == '__main__':
app.run()
+220
View File
@@ -0,0 +1,220 @@
#!/usr/bin/env python3
"""
QuantumBotX Windows Installer Builder
Builds the complete installer package for distribution
"""
import os
import sys
import subprocess
import shutil
from pathlib import Path
def run_command(command, shell=True, cwd=None):
"""Run a command and return the result."""
try:
print(f"Running: {command}")
result = subprocess.run(command, shell=shell, cwd=cwd, capture_output=True, text=True)
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
return result.returncode == 0, result.stdout, result.stderr
except Exception as e:
return False, "", str(e)
def check_requirements():
"""Check if all required tools are installed."""
print("Checking requirements...")
# Check PyInstaller
success, _, _ = run_command("pyinstaller --version")
if not success:
print("❌ PyInstaller not found. Installing...")
run_command("pip install pyinstaller")
# Check NSIS (we'll assume it's installed or provide instructions)
print("✓ PyInstaller found")
# Check if favicon exists for installer icon
if not os.path.exists("static/favicon.ico"):
print("⚠️ Warning: static/favicon.ico not found. Installer will use default icon.")
return True
def clean_previous_builds():
"""Clean previous build artifacts."""
print("Cleaning previous builds...")
directories_to_clean = [
"build",
"dist",
"__pycache__",
"*.spec~"
]
for path in directories_to_clean:
if os.path.exists(path):
if os.path.isfile(path):
os.remove(path)
else:
shutil.rmtree(path)
print(f" Cleaned: {path}")
# Clean Python cache
for root, dirs, files in os.walk("."):
for dir_name in dirs:
if dir_name == "__pycache__":
pycache_path = os.path.join(root, dir_name)
shutil.rmtree(pycache_path)
print(f" Cleaned: {pycache_path}")
def build_with_pyinstaller():
"""Build the application using PyInstaller."""
print("Building with PyInstaller...")
# Build the executable
success, stdout, stderr = run_command("pyinstaller --clean quantumbotx.spec")
if not success:
print(f"❌ PyInstaller build failed: {stderr}")
return False
print("✓ PyInstaller build completed successfully")
return True
def create_installer():
"""Create the Windows installer using NSIS."""
print("Creating Windows installer...")
# Check if NSIS is installed
success, _, _ = run_command("makensis /VERSION")
if not success:
print("❌ NSIS not found!")
print("Please install NSIS from: https://nsis.sourceforge.io/Download")
print("Or download from: https://nsis.sourceforge.io/Download")
print()
print("Alternative: You can manually install the application by:")
print("1. Running the PyInstaller build")
print("2. Copying the dist/QuantumBotX folder to the target computer")
print("3. Running setup_quantumbotx.py on the target computer")
return False
# Create the installer
success, stdout, stderr = run_command("makensis installer.nsi")
if not success:
print(f"❌ NSIS build failed: {stderr}")
return False
print("✓ Windows installer created successfully")
return True
def create_portable_package():
"""Create a portable ZIP package as alternative."""
print("Creating portable ZIP package...")
try:
import zipfile
# Create portable package
portable_name = "QuantumBotX-Portable.zip"
with zipfile.ZipFile(portable_name, 'w', zipfile.ZIP_DEFLATED) as zipf:
# Add the main executable and supporting files
dist_path = Path("dist/QuantumBotX")
if dist_path.exists():
for file_path in dist_path.rglob("*"):
if file_path.is_file():
arcname = file_path.relative_to("dist")
zipf.write(file_path, arcname)
# Add additional files
additional_files = [
"start.bat",
"start.sh",
"setup_quantumbotx.py",
"requirements.txt",
".env.example",
"README.md",
"QUICK_START_GUIDE.md",
"MT5_SETUP_GUIDE.md"
]
for file_name in additional_files:
if os.path.exists(file_name):
zipf.write(file_name, file_name)
print(f"✓ Portable package created: {portable_name}")
return True
except ImportError:
print("⚠️ zipfile not available, skipping portable package")
return False
except Exception as e:
print(f"❌ Error creating portable package: {e}")
return False
def main():
"""Main build function."""
print("QuantumBotX Windows Installer Builder")
print("====================================")
print()
# Check requirements
if not check_requirements():
print("❌ Requirements check failed")
input("Press Enter to exit...")
return
# Clean previous builds
clean_previous_builds()
# Build with PyInstaller
if not build_with_pyinstaller():
print("❌ PyInstaller build failed")
input("Press Enter to exit...")
return
# Create installer
installer_created = create_installer()
# Create portable package as backup
create_portable_package()
print()
if installer_created:
print("🎉 Build Complete!")
print("==================")
print("✓ Windows installer: QuantumBotX-Installer.exe")
print("✓ Portable package: QuantumBotX-Portable.zip")
print()
print("Distribution files are ready in the current directory.")
print()
print("To test the installer:")
print("1. Copy QuantumBotX-Installer.exe to a test computer")
print("2. Run the installer")
print("3. Follow the setup wizard")
print()
else:
print("⚠️ Build Complete (Installer not available)")
print("=============================================")
print("✓ Application executable: dist/QuantumBotX/")
print("✓ Portable package: QuantumBotX-Portable.zip")
print()
print("Since NSIS is not installed, you have these options:")
print("1. Install NSIS and run this script again")
print("2. Use the portable version (dist/QuantumBotX/)")
print("3. Manually copy files to target computer")
print()
print("Next steps for end users:")
print("1. Install MetaTrader 5")
print("2. Configure .env file with MT5 credentials")
print("3. Run start.bat to launch the application")
print()
input("Press Enter to exit...")
if __name__ == "__main__":
main()
+164 -4
View File
@@ -1,10 +1,13 @@
# core/__init__.py
import os
import sys
import logging
import sqlite3
from logging.handlers import RotatingFileHandler
from flask import Flask, render_template, send_from_directory
from dotenv import load_dotenv
from werkzeug.security import generate_password_hash
class RequestLogFilter(logging.Filter):
"""Filter untuk menghilangkan noise dari terminal log."""
@@ -58,6 +61,155 @@ class RequestLogFilter(logging.Filter):
# Tampilkan semua request lainnya (termasuk GET yang error)
return True
def init_database():
"""Initialize database and create tables if they don't exist."""
try:
# Get the directory where the executable is located
if getattr(sys, 'frozen', False):
# Running as PyInstaller bundle
base_dir = os.path.dirname(sys.executable)
db_path = os.path.join(base_dir, 'bots.db')
else:
# Running as script
base_dir = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(base_dir, '..', '..', 'bots.db')
# Create connection
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Create users table
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
join_date DATETIME DEFAULT CURRENT_TIMESTAMP
)
''')
# Create bots table
cursor.execute('''
CREATE TABLE IF NOT EXISTS bots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
market TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'Dijeda',
lot_size REAL NOT NULL DEFAULT 0.01,
sl_pips INTEGER NOT NULL DEFAULT 100,
tp_pips INTEGER NOT NULL DEFAULT 200,
timeframe TEXT NOT NULL DEFAULT 'H1',
check_interval_seconds INTEGER NOT NULL DEFAULT 60,
strategy TEXT NOT NULL,
strategy_params TEXT,
enable_strategy_switching INTEGER NOT NULL DEFAULT 0
)
''')
# Create trade_history table
cursor.execute('''
CREATE TABLE IF NOT EXISTS trade_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
bot_id INTEGER NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
action TEXT NOT NULL,
details TEXT,
is_notification INTEGER NOT NULL DEFAULT 0,
is_read INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (bot_id) REFERENCES bots (id) ON DELETE CASCADE
)
''')
# Create backtest_results table
cursor.execute('''
CREATE TABLE IF NOT EXISTS backtest_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
strategy_name TEXT NOT NULL,
data_filename TEXT NOT NULL,
total_profit_usd REAL NOT NULL,
total_trades INTEGER NOT NULL,
win_rate_percent REAL NOT NULL,
max_drawdown_percent REAL NOT NULL,
wins INTEGER NOT NULL,
losses INTEGER NOT NULL,
equity_curve TEXT,
trade_log TEXT,
parameters TEXT
)
''')
# Create trading_sessions table (AI Mentor)
cursor.execute('''
CREATE TABLE IF NOT EXISTS trading_sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_date DATE NOT NULL,
user_id INTEGER DEFAULT 1,
total_trades INTEGER NOT NULL DEFAULT 0,
total_profit_loss REAL NOT NULL DEFAULT 0.0,
emotions TEXT NOT NULL DEFAULT 'netral',
market_conditions TEXT NOT NULL DEFAULT 'normal',
personal_notes TEXT,
risk_score INTEGER DEFAULT 5,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
)
''')
# Create ai_mentor_reports table
cursor.execute('''
CREATE TABLE IF NOT EXISTS ai_mentor_reports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id INTEGER NOT NULL,
trading_patterns_analysis TEXT,
emotional_analysis TEXT,
risk_management_score INTEGER,
recommendations TEXT,
motivation_message TEXT,
language TEXT DEFAULT 'bahasa_indonesia',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES trading_sessions (id) ON DELETE CASCADE
)
''')
# Create daily_trading_data table
cursor.execute('''
CREATE TABLE IF NOT EXISTS daily_trading_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id INTEGER NOT NULL,
bot_id INTEGER NOT NULL,
symbol TEXT NOT NULL,
entry_time DATETIME,
exit_time DATETIME,
profit_loss REAL NOT NULL,
lot_size REAL NOT NULL,
stop_loss_used BOOLEAN DEFAULT 0,
take_profit_used BOOLEAN DEFAULT 0,
risk_percent REAL,
strategy_used TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES trading_sessions (id) ON DELETE CASCADE,
FOREIGN KEY (bot_id) REFERENCES bots (id) ON DELETE CASCADE
)
''')
# Check if default user exists
cursor.execute('SELECT COUNT(*) FROM users')
if cursor.fetchone()[0] == 0:
# Insert default user
default_password_hash = generate_password_hash('admin')
cursor.execute(
'INSERT INTO users (name, email, password_hash) VALUES (?, ?, ?)',
('Admin User', 'admin@quantumbotx.com', default_password_hash)
)
conn.commit()
conn.close()
except Exception as e:
raise Exception(f"Database initialization failed: {e}")
# ============================
# APPLICATION FACTORY FUNCTION
# ============================
@@ -66,13 +218,21 @@ def create_app():
Membuat dan mengkonfigurasi instance aplikasi Flask.
"""
load_dotenv()
app = Flask(
__name__,
__name__,
instance_relative_config=True,
template_folder='../templates',
static_folder='../static'
)
)
# Initialize database on startup
try:
init_database()
app.logger.info("Database initialized successfully")
except Exception as e:
app.logger.error(f"Failed to initialize database: {e}")
# Don't crash the app, but log the error
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'your-secret-key-here')
# Konfigurasi logging yang lebih bersih
@@ -203,4 +363,4 @@ def create_app():
return send_from_directory(os.path.join(app.root_path, 'static'),
'favicon.ico', mimetype='image/vnd.microsoft.icon')
return app
return app
+13 -4
View File
@@ -1,15 +1,24 @@
# core/db/connection.py
import sqlite3
import os
import sys
# Tentukan nama file database di satu tempat.
DATABASE_FILENAME = 'bots.db'
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATABASE_PATH = os.path.join(BASE_DIR, '..', '..', DATABASE_FILENAME)
def get_db_connection():
"""Membuat dan mengembalikan koneksi ke database SQLite."""
conn = sqlite3.connect(DATABASE_PATH)
# Get the directory where the executable is located
if getattr(sys, 'frozen', False):
# Running as PyInstaller bundle
base_dir = os.path.dirname(sys.executable)
db_path = os.path.join(base_dir, DATABASE_FILENAME)
else:
# Running as script
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(BASE_DIR, '..', '..', DATABASE_FILENAME)
conn = sqlite3.connect(db_path)
# Mengatur agar hasil query bisa diakses seperti dictionary
conn.row_factory = sqlite3.Row
return conn
return conn
-1
View File
@@ -10,7 +10,6 @@ import os
import sys
import subprocess
import platform
import json
import urllib.request
from pathlib import Path
+186
View File
@@ -0,0 +1,186 @@
; QuantumBotX Windows Installer
; This script creates a Windows installer for the QuantumBotX trading application
!include "MUI2.nsh"
!include "FileFunc.nsh"
!include "LogicLib.nsh"
; General
Name "QuantumBotX Trading Bot"
OutFile "QuantumBotX-Installer.exe"
Unicode True
InstallDir "C:\QuantumBotX"
InstallDirRegKey HKCU "Software\QuantumBotX" ""
RequestExecutionLevel admin
; Modern UI Configuration
!define MUI_ABORTWARNING
!define MUI_ICON "static\favicon.ico"
!define MUI_UNICON "static\favicon.ico"
; Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "LICENSE.md"
; Custom prerequisites page
Page custom PrerequisitesPage PrerequisitesPageLeave
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
; Languages
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "Indonesian"
; Version Information
VIProductVersion "2.0.0.0"
VIAddVersionKey "ProductName" "QuantumBotX Trading Bot"
VIAddVersionKey "CompanyName" "Chrisnov IT Solutions"
VIAddVersionKey "FileVersion" "2.0.0.0"
VIAddVersionKey "ProductVersion" "2.0.0.0"
VIAddVersionKey "FileDescription" "Professional Trading Bot Application"
VIAddVersionKey "LegalCopyright" "Copyright (c) 2025 Reynov Christian - Chrisnov IT Solutions"
VIAddVersionKey "Contact" "contact@chrisnov.com"
Section "Install"
SetOutPath "$INSTDIR"
; Stop any running instances
DetailPrint "Stopping any running instances..."
nsExec::Exec 'taskkill /f /im "QuantumBotX.exe"'
; Create installation directory
CreateDirectory "$INSTDIR"
; Copy main files
File "dist\QuantumBotX\QuantumBotX.exe"
File /r "dist\QuantumBotX\_internal"
File "start.bat"
File "start.sh"
File "setup_quantumbotx.py"
File "requirements.txt"
File ".env.example"
File "README.md"
File "QUICK_START_GUIDE.md"
File "MT5_SETUP_GUIDE.md"
File /r "templates"
File /r "static"
File /r "core"
; Create data directories
CreateDirectory "$INSTDIR\logs"
CreateDirectory "$INSTDIR\lab"
CreateDirectory "$INSTDIR\testing"
CreateDirectory "$INSTDIR\docs"
; Copy optional directories if they exist
IfFileExists "docs\*.*" 0 +2
File /r "docs"
IfFileExists "lab\*.*" 0 +2
File /r "lab"
IfFileExists "testing\*.*" 0 +2
File /r "testing"
; Create desktop shortcut
CreateShortCut "$DESKTOP\QuantumBotX.lnk" "$INSTDIR\start.bat" "" "$INSTDIR\start.bat" 0
; Create start menu entries
CreateDirectory "$SMPROGRAMS\QuantumBotX"
CreateShortCut "$SMPROGRAMS\QuantumBotX\QuantumBotX.lnk" "$INSTDIR\start.bat" "" "$INSTDIR\start.bat" 0
CreateShortCut "$SMPROGRAMS\QuantumBotX\Uninstall.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0
; Store installation folder
WriteRegStr HKCU "Software\QuantumBotX" "" $INSTDIR
; Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"
; Write registry for Add/Remove Programs
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QuantumBotX" "DisplayName" "QuantumBotX Trading Bot"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QuantumBotX" "UninstallString" "$INSTDIR\Uninstall.exe"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QuantumBotX" "DisplayVersion" "2.0.0"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QuantumBotX" "Publisher" "Chrisnov IT Solutions"
WriteRegDWord HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QuantumBotX" "NoModify" 1
WriteRegDWord HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QuantumBotX" "NoRepair" 1
SectionEnd
Section "Uninstall"
; Remove files and directories
Delete "$INSTDIR\Uninstall.exe"
Delete "$INSTDIR\QuantumBotX.exe"
RMDir /r "$INSTDIR\_internal"
RMDir /r "$INSTDIR\templates"
RMDir /r "$INSTDIR\static"
RMDir /r "$INSTDIR\core"
RMDir /r "$INSTDIR\docs"
RMDir /r "$INSTDIR\lab"
RMDir /r "$INSTDIR\testing"
RMDir /r "$INSTDIR\logs"
Delete "$INSTDIR\*.*"
RMDir "$INSTDIR"
; Remove shortcuts
Delete "$DESKTOP\QuantumBotX.lnk"
RMDir /r "$SMPROGRAMS\QuantumBotX"
; Remove registry entries
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\QuantumBotX"
DeleteRegKey /ifempty HKCU "Software\QuantumBotX"
; Stop running instances before uninstall
nsExec::Exec 'taskkill /f /im "QuantumBotX.exe"'
nsExec::Exec 'taskkill /f /im "python.exe"'
SectionEnd
Function .onInit
; Check if already installed
ReadRegStr $R0 HKCU "Software\QuantumBotX" ""
${If} $R0 != ""
MessageBox MB_YESNO "QuantumBotX is already installed. Do you want to reinstall?" IDYES continue
Abort
continue:
${EndIf}
FunctionEnd
Function PrerequisitesPage
!insertmacro MUI_HEADER_TEXT "Prerequisites" "Important setup information"
nsDialogs::Create 1018
Pop $0
${NSD_CreateLabel} 0 0 100% 24u "Before using QuantumBotX, you need to:"
Pop $0
${NSD_CreateLabel} 0 30u 100% 60u "1. Install MetaTrader 5 from https://www.metatrader5.com/$\n2. Create a demo or live trading account$\n3. Keep MT5 running in background when using QuantumBotX$\n$\n⚠️ Python is NOT required - it's already bundled in this installer!"
Pop $0
${NSD_CreateLabel} 0 100u 100% 24u "System Requirements:"
Pop $0
${NSD_CreateLabel} 0 130u 100% 40u "• Windows 7 SP1 or later (64-bit recommended)$\n• 4GB RAM minimum (8GB recommended)$\n• 500MB free disk space$\n• Internet connection for initial setup"
Pop $0
nsDialogs::Show
FunctionEnd
Function PrerequisitesPageLeave
# This function is called when leaving the prerequisites page
# We could add validation here if needed
FunctionEnd
Function .onInstSuccess
MessageBox MB_YESNO "Installation completed successfully! $\n$\nDo you want to run the setup wizard now?" IDNO end
Exec '"$INSTDIR\setup_quantumbotx.py"'
end:
FunctionEnd
+1 -1
View File
@@ -1,5 +1,5 @@
{
"broker": "FBS-Demo",
"company": "FBS Markets Inc.",
"last_check": "2025-09-24T10:46:19.830283"
"last_check": "2025-10-09T12:17:45.370404"
}
+3
View File
@@ -0,0 +1,3 @@
@echo off
cd /d "%~dp0"
start.bat
+99
View File
@@ -0,0 +1,99 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['run.py'],
pathex=['.'],
binaries=[],
datas=[
('templates', 'templates'),
('static', 'static'),
('core', 'core'),
('.env.example', '.env.example'),
('README.md', 'README.md'),
('MT5_SETUP_GUIDE.md', 'MT5_SETUP_GUIDE.md'),
],
hiddenimports=[
'MetaTrader5',
'flask',
'werkzeug',
'jinja2',
'numpy',
'pandas',
'pandas_ta',
'core',
'core.bots',
'core.brokers',
'core.data',
'core.db',
'core.routes',
'core.strategies',
'core.utils',
'core.ai',
'core.backtesting',
'core.education',
'core.interfaces',
'core.services',
'core.seasonal',
'core.helpers',
'core.bots.controller',
'core.bots.trading_bot',
'core.brokers.base_broker',
'core.brokers.binance_broker',
'core.brokers.broker_factory',
'core.brokers.ctrader_broker',
'core.brokers.indonesian_brokers',
'core.brokers.interactive_brokers',
'core.brokers.tradingview_broker',
'core.data.chart_data',
'core.db.connection',
'core.db.models',
'core.db.queries',
'core.ai.ollama_client',
'core.ai.trading_mentor_ai',
'core.backtesting.engine',
'core.backtesting.enhanced_engine',
'core.education.atr_education',
'core.utils.mt5',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='QuantumBotX',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
icon=None,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='QuantumBotX',
)
+1
View File
@@ -4,6 +4,7 @@ charset-normalizer==3.4.2
click==8.2.1
colorama==0.4.6
Flask==3.1.1
gunicorn
idna==3.10
itsdangerous==2.2.0
Jinja2==3.1.6
+14 -2
View File
@@ -29,9 +29,21 @@ app = create_app()
@app.route('/api/health')
def health_check():
"""Endpoint untuk memastikan server berjalan."""
return jsonify({"status": "ok", "message": "Server is running"})
mt5_status = "MT5 connected" if mt5.isinitialize() else "MT5 not connected" # pyright: ignore[reportAttributeAccessIssue]
return jsonify({"status": "ok", "message": "Server is running", "mt5": mt5_status})
if __name__ == '__main__':
# Skip MT5 initialization if SKIP_MT5_INIT is set (for Vercel deployment)
if os.getenv('SKIP_MT5_INIT') == '1':
logging.info("Skipping MT5 initialization (deployment mode).")
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
)
sys.exit(0)
# --- Inisialisasi MT5 Terpusat ---
# Dilakukan di sini untuk memastikan hanya berjalan sekali.
try:
@@ -72,4 +84,4 @@ if __name__ == '__main__':
host=os.getenv('FLASK_HOST', '127.0.0.1'),
port=int(os.getenv('FLASK_PORT', 5000)),
use_reloader=False
)
)
+205
View File
@@ -0,0 +1,205 @@
#!/usr/bin/env python3
"""
QuantumBotX Setup Script
Automated setup for non-technical users
"""
import os
import subprocess
from pathlib import Path
def run_command(command, shell=True):
"""Run a command and return the result."""
try:
result = subprocess.run(command, shell=shell, capture_output=True, text=True)
return result.returncode == 0, result.stdout, result.stderr
except Exception as e:
return False, "", str(e)
def check_python():
"""Check if Python is installed."""
success, _, _ = run_command("python --version")
return success
def install_requirements():
"""Install Python requirements."""
print("Installing Python dependencies...")
success, stdout, stderr = run_command("pip install -r requirements.txt")
if not success:
print(f"Error installing requirements: {stderr}")
return False
print("✓ Python dependencies installed successfully")
return True
def create_startup_scripts():
"""Create user-friendly startup scripts."""
print("Creating startup scripts...")
# Create start.bat
start_bat = '''@echo off
echo Starting QuantumBotX Trading Application...
echo =========================================
echo.
echo Make sure MetaTrader 5 is running before continuing!
echo.
pause
echo.
python run.py
pause
'''
with open('start.bat', 'w') as f:
f.write(start_bat)
# Create start.sh for Unix-like systems (if needed)
start_sh = '''#!/bin/bash
echo "Starting QuantumBotX Trading Application..."
echo "========================================"
echo ""
echo "Make sure MetaTrader 5 is running before continuing!"
echo ""
read -p "Press Enter to continue..."
echo ""
python3 run.py
'''
with open('start.sh', 'w') as f:
f.write(start_sh)
# Make start.sh executable on Unix systems
try:
os.chmod('start.sh', 0o755)
except:
pass # Windows doesn't need this
print("✓ Startup scripts created")
return True
def create_desktop_shortcut():
"""Create desktop shortcut (Windows only)."""
print("Creating desktop shortcut...")
try:
# Get user's desktop path
desktop = Path.home() / "Desktop"
shortcut_path = desktop / "QuantumBotX.lnk"
# Create a simple batch file that will be the shortcut target
shortcut_bat = '''@echo off
cd /d "%~dp0"
start.bat
'''
with open('quantum_botx_shortcut.bat', 'w') as f:
f.write(shortcut_bat)
print("✓ Desktop shortcut script created")
print(f" You can manually create a shortcut to: {os.path.abspath('quantum_botx_shortcut.bat')}")
print(" And place it on your desktop")
except Exception as e:
print(f"Note: Could not create desktop shortcut automatically: {e}")
print(" You can manually create a shortcut to start.bat on your desktop")
return True
def create_user_instructions():
"""Create user-friendly instructions."""
print("Creating user instructions...")
instructions = '''# QuantumBotX - Quick Start Guide
## First Time Setup
1. **Install MetaTrader 5**
- Download from: https://www.metatrader5.com/
- Install and create a demo account
- Keep MT5 running in the background
2. **Configure Your Settings**
- Copy `.env.example` to `.env`
- Edit `.env` with your MT5 credentials:
```
MT5_LOGIN=your_account_number
MT5_PASSWORD=your_password
MT5_SERVER=your_server_name
```
3. **Start the Application**
- Double-click `start.bat` (Windows)
- Open http://127.0.0.1:5000 in your browser
## Daily Use
1. Start MetaTrader 5 first
2. Run `start.bat`
3. Open your web browser to http://127.0.0.1:5000
## Troubleshooting
- Make sure MetaTrader 5 is running
- Check your .env file has correct credentials
- If the app won't start, try running `python run.py` directly
## Support
If you need help, check the README.md file or contact support.
'''
with open('QUICK_START_GUIDE.md', 'w') as f:
f.write(instructions)
print("✓ User instructions created")
return True
def main():
"""Main setup function."""
print("QuantumBotX Setup Wizard")
print("========================")
print()
# Check Python
if not check_python():
print("❌ Python is not installed or not in PATH")
print("Please install Python 3.8 or higher from https://python.org")
input("Press Enter to exit...")
return
print("✓ Python found")
# Install requirements
if not install_requirements():
print("❌ Failed to install requirements")
input("Press Enter to exit...")
return
# Create startup scripts
if not create_startup_scripts():
print("❌ Failed to create startup scripts")
input("Press Enter to exit...")
return
# Create desktop shortcut
create_desktop_shortcut()
# Create user instructions
if not create_user_instructions():
print("❌ Failed to create user instructions")
input("Press Enter to exit...")
return
print()
print("🎉 Setup Complete!")
print("==================")
print()
print("Next steps:")
print("1. Install and configure MetaTrader 5")
print("2. Copy .env.example to .env and configure your credentials")
print("3. Run start.bat to launch the application")
print("4. Open http://127.0.0.1:5000 in your browser")
print()
print("See QUICK_START_GUIDE.md for detailed instructions")
print()
input("Press Enter to exit...")
if __name__ == "__main__":
main()
+10
View File
@@ -0,0 +1,10 @@
@echo off
echo Starting QuantumBotX Trading Application...
echo =========================================
echo.
echo Make sure MetaTrader 5 is running before continuing!
echo.
pause
echo.
QuantumBotX.exe
pause
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
echo "Starting QuantumBotX Trading Application..."
echo "========================================"
echo ""
echo "Make sure MetaTrader 5 is running before continuing!"
echo ""
read -p "Press Enter to continue..."
echo ""
python3 run.py
-39
View File
@@ -9,7 +9,6 @@ document.addEventListener('DOMContentLoaded', () => {
const loadingSpinner = document.getElementById('loading-spinner');
let equityChart = null; // Variabel untuk menyimpan instance grafik
const resultsLog = document.getElementById('results-log');
const downloadBtn = document.getElementById('download-data-btn');
// Muat strategi ke dropdown
async function loadStrategies() {
@@ -242,43 +241,5 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
// Handle data download
downloadBtn.addEventListener('click', async () => {
if (!confirm("This will download historical market data from MT5.\n\nRequirements:\n• MT5 Terminal must be running\n• MT5 Python library must be available\n• Account credentials in .env file\n\nThis may take several minutes. Continue?")) {
return;
}
const originalText = downloadBtn.innerHTML;
downloadBtn.disabled = true;
downloadBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Downloading...';
try {
const response = await fetch('/api/download-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
const result = await response.json();
if (response.ok) {
if (result.success) {
alert(`✅ Download completed successfully!\n\nDownloaded: ${result.downloaded_files.length} files\nFailed: ${result.failed_count}\n\nFiles saved to 'lab/backtest_data/' directory.`);
} else {
alert(`❌ Download failed: ${result.error}`);
}
} else {
alert(`❌ Download error: ${result.error}`);
}
} catch (err) {
console.error("Download failed:", err);
alert('❌ Failed to connect to server for data download.');
} finally {
downloadBtn.disabled = false;
downloadBtn.innerHTML = originalText;
}
});
loadStrategies();
});
+15 -7
View File
@@ -47,18 +47,26 @@ document.addEventListener('DOMContentLoaded', function() {
currentSort.direction = 'asc';
}
// Update sort icons
document.getElementById('profit-sort-icon').innerHTML = column === 'profit' ? (currentSort.direction === 'asc' ? '▲' : '▼') : '';
document.getElementById('time-sort-icon').innerHTML = column === 'time' ? (currentSort.direction === 'asc' ? '▲' : '▼') : '';
// Reset all icons
['symbol', 'type', 'volume', 'profit', 'time', 'magic'].forEach(col => {
const iconEl = document.getElementById(`${col}-sort-icon`);
if (iconEl) {
iconEl.innerHTML = '';
}
});
// Set active icon
const activeIconEl = document.getElementById(`${column}-sort-icon`);
if (activeIconEl) {
activeIconEl.innerHTML = currentSort.direction === 'asc' ? '▲' : '▼';
}
const sortedData = [...historyData].sort((a, b) => {
let aValue = a[column];
let bValue = b[column];
if (column === 'profit' || column === 'time') {
aValue = parseFloat(aValue);
bValue = parseFloat(bValue);
} else if (column === 'volume' || column === 'magic') {
// For numeric columns, parse as float
if (['profit', 'time', 'volume', 'magic', 'type'].includes(column)) {
aValue = parseFloat(aValue);
bValue = parseFloat(bValue);
}
+1100 -48
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -84,7 +84,7 @@ document.addEventListener('DOMContentLoaded', function() {
portfolioTableBody.innerHTML = ''; // Kosongkan tabel sebelum diisi
if (positions.length === 0) {
portfolioTableBody.innerHTML = '<tr><td colspan="6" class="p-4 text-center text-gray-500">Tidak ada posisi terbuka.</td></tr>';
portfolioTableBody.innerHTML = `<tr><td colspan="6" class="p-4 text-center text-gray-500">${window.QuantumBotXI18n.t('portfolio.no_open_positions')}</td></tr>`;
} else {
positions.forEach(pos => {
totalProfit += pos.profit;
@@ -112,7 +112,7 @@ document.addEventListener('DOMContentLoaded', function() {
else if (totalProfit < previousTotalProfit) trendIcon = '<i class="fas fa-arrow-down text-red-500"></i>';
portfolioSummary.innerHTML = `
<p class="text-sm text-gray-500">Total P/L Terbuka</p>
<p class="text-sm text-gray-500">${window.QuantumBotXI18n.t('portfolio.open_pnl_total')}</p>
<p class="text-2xl font-bold ${totalProfitClass}">${formatCurrency(totalProfit)} <span class="ml-2 text-lg">${trendIcon}</span></p>`;
previousTotalProfit = totalProfit;
+13 -5
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Halaman Tidak Ditemukan - QuantumBotX</title>
<title><span data-i18n="error.404_title">Halaman Tidak Ditemukan - QuantumBotX</span></title>
<link href="https://cdn.jsdelivr.net/npm/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') }}">
@@ -11,13 +11,21 @@
<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-blue-500">404</h1>
<h2 class="text-3xl font-semibold text-gray-800 mt-4">Halaman Tidak Ditemukan</h2>
<p class="text-gray-600 mt-2">Maaf, halaman yang Anda cari tidak ada atau telah dipindahkan.</p>
<h2 class="text-3xl font-semibold text-gray-800 mt-4" data-i18n="error.404_heading">Halaman Tidak Ditemukan</h2>
<p class="text-gray-600 mt-2" data-i18n="error.404_message">Maaf, halaman yang Anda cari tidak ada atau telah dipindahkan.</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
<i class="fas fa-home mr-2"></i><span data-i18n="error.back_to_dashboard">Kembali ke Dashboard</span>
</a>
</div>
</div>
<script src="{{ url_for('static', filename='js/i18n.js') }}"></script>
<script>
// Initialize i18n for this page
document.addEventListener('DOMContentLoaded', function() {
window.QuantumBotXI18n.init();
});
</script>
</body>
</html>
</html>
+12 -4
View File
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Terjadi Kesalahan Internal - QuantumBotX</title>
<title><span data-i18n="error.500_title">Terjadi Kesalahan Internal - QuantumBotX</span></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">
@@ -13,15 +13,23 @@
<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">
<h2 class="text-3xl font-semibold text-gray-800 mt-4" data-i18n="error.500_heading">Terjadi Kesalahan Internal</h2>
<p class="text-gray-600 mt-2" data-i18n="error.500_message">
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
<i class="fas fa-home mr-2"></i><span data-i18n="error.back_to_dashboard">Kembali ke Dashboard</span>
</a>
</div>
</div>
<script src="{{ url_for('static', filename='js/i18n.js') }}"></script>
<script>
// Initialize i18n for this page
document.addEventListener('DOMContentLoaded', function() {
window.QuantumBotXI18n.init();
});
</script>
</body>
</html>
+116 -115
View File
@@ -1,7 +1,8 @@
<!-- templates/ai_mentor/daily_report.html -->
{% extends \"base.html\" %}
{% extends "base.html" %}
{% block title %}📊 Laporan AI Mentor - {{ session_data.session_date if session_data else 'Hari Ini' }} - QuantumBotX{% endblock %}
{% block title_key %}ai_mentor.report_page_title{% endblock %}
{% block title %}📊 AI Mentor Report{% endblock %}
{% block head %}
<style>
@@ -71,179 +72,179 @@
{% endblock %}
{% block content %}
<div class=\"container mx-auto px-4 py-8\">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<div class=\"ai-report-card\">
<div class=\"flex justify-between items-start\">
<div class="ai-report-card">
<div class="flex justify-between items-start">
<div>
<h1 class=\"text-3xl font-bold mb-2\">📊 Laporan AI Mentor</h1>
<p class=\"text-blue-100 text-lg\">Analisis personal untuk {{ session_data.session_date if session_data else 'Hari Ini' }}</p>
<div class=\"mt-4 flex items-center space-x-4\">
<span class=\"text-blue-200\">🤖 Dibuat oleh AI</span>
<span class=\"text-blue-200\">• 🇮🇩 Bahasa Indonesia</span>
<span class=\"text-blue-200\">• 📈 Data Real</span>
<h1 class="text-3xl font-bold mb-2" data-i18n="ai_mentor.report_title">📊 Laporan AI Mentor</h1>
<p class="text-blue-100 text-lg" data-i18n="ai_mentor.report_subtitle">Analisis personal untuk {{ session_data.session_date if session_data else 'Hari Ini' }}</p>
<div class="mt-4 flex items-center space-x-4">
<span class="text-blue-200" data-i18n="ai_mentor.created_by_ai">🤖 Dibuat oleh AI</span>
<span class="text-blue-200" data-i18n="ai_mentor.language_indonesian">• 🇮🇩 Bahasa Indonesia</span>
<span class="text-blue-200" data-i18n="ai_mentor.real_data">• 📈 Data Real</span>
</div>
</div>
<div class=\"text-right\">
<div class="text-right">
{% if session_data %}
<div class=\"text-3xl font-bold {{ 'text-green-300' if session_data.total_profit_loss > 0 else 'text-red-300' if session_data.total_profit_loss < 0 else 'text-gray-300' }}\">
${{ \"%.2f\"|format(session_data.total_profit_loss) }}
<div class="text-3xl font-bold {{ 'text-green-300' if session_data.total_profit_loss > 0 else 'text-red-300' if session_data.total_profit_loss < 0 else 'text-gray-300' }}">
${{ "%.2f"|format(session_data.total_profit_loss) }}
</div>
<div class=\"text-blue-200\">{{ session_data.total_trades }} trades</div>
<div class="text-blue-200">{{ session_data.total_trades }} <span data-i18n="ai_mentor.trades">trades</span></div>
{% else %}
<div class=\"text-gray-300\">No data</div>
<div class="text-gray-300" data-i18n="ai_mentor.no_data">No data</div>
{% endif %}
</div>
</div>
</div>
<!-- Navigation -->
<div class=\"mb-6\">
<nav class=\"flex space-x-4\">
<a href=\"{{ url_for('ai_mentor.dashboard') }}\" class=\"text-blue-600 hover:text-blue-800\">Kembali ke Dashboard</a>
<a href=\"{{ url_for('ai_mentor.history') }}\" class=\"text-gray-600 hover:text-gray-800\">📚 Riwayat Laporan</a>
<div class="mb-6">
<nav class="flex space-x-4">
<a href="{{ url_for('ai_mentor.dashboard') }}" class="text-blue-600 hover:text-blue-800"><span data-i18n="ai_mentor.back_to_dashboard">Kembali ke Dashboard</span></a>
<a href="{{ url_for('ai_mentor.history') }}" class="text-gray-600 hover:text-gray-800">📚 <span data-i18n="ai_mentor.report_history">Riwayat Laporan</span></a>
</nav>
</div>
{% if session_data and analysis %}
<!-- Trading Summary -->
<div class=\"analysis-section\">
<h2 class=\"text-xl font-bold mb-4 flex items-center text-gray-800\">
<span class=\"mr-2\">📊</span>
Ringkasan Trading
<div class="analysis-section">
<h2 class="text-xl font-bold mb-4 flex items-center text-gray-800">
<span class="mr-2">📊</span>
<span data-i18n="ai_mentor.trading_summary">Ringkasan Trading</span>
</h2>
<div class=\"grid grid-cols-1 md:grid-cols-4 gap-4\">
<div class=\"text-center\">
<div class=\"text-2xl font-bold text-blue-600\">{{ session_data.total_trades }}</div>
<div class=\"text-sm text-gray-600\">Total Trades</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="text-center">
<div class="text-2xl font-bold text-blue-600">{{ session_data.total_trades }}</div>
<div class="text-sm text-gray-600" data-i18n="ai_mentor.total_trades">Total Trades</div>
</div>
<div class=\"text-center\">
<div class=\"text-2xl font-bold {{ 'profit-positive' if session_data.total_profit_loss > 0 else 'profit-negative' if session_data.total_profit_loss < 0 else 'text-gray-600' }}\">
${{ \"%.2f\"|format(session_data.total_profit_loss) }}
<div class="text-center">
<div class="text-2xl font-bold {{ 'profit-positive' if session_data.total_profit_loss > 0 else 'profit-negative' if session_data.total_profit_loss < 0 else 'text-gray-600' }}">
${{ "%.2f"|format(session_data.total_profit_loss) }}
</div>
<div class=\"text-sm text-gray-600\">Profit/Loss</div>
<div class="text-sm text-gray-600" data-i18n="ai_mentor.profit_loss">Profit/Loss</div>
</div>
<div class=\"text-center\">
<span class=\"emotion-badge emotion-{{ session_data.emotions }}\">{{ session_data.emotions.title() }}</span>
<div class=\"text-sm text-gray-600 mt-1\">Kondisi Emosi</div>
<div class="text-center">
<span class="emotion-badge emotion-{{ session_data.emotions }}">{{ session_data.emotions.title() }}</span>
<div class="text-sm text-gray-600 mt-1" data-i18n="ai_mentor.emotional_condition">Kondisi Emosi</div>
</div>
<div class=\"text-center\">
<div class=\"text-lg font-semibold text-gray-700\">{{ session_data.market_conditions.title() }}</div>
<div class=\"text-sm text-gray-600\">Kondisi Market</div>
<div class="text-center">
<div class="text-lg font-semibold text-gray-700">{{ session_data.market_conditions.title() }}</div>
<div class="text-sm text-gray-600" data-i18n="ai_mentor.market_conditions">Kondisi Market</div>
</div>
</div>
</div>
<!-- AI Analysis Sections -->
<div class=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Trading Patterns -->
<div class=\"analysis-section\">
<h3 class=\"text-lg font-bold mb-3 text-gray-800\">🔍 Analisis Pola Trading</h3>
<div class=\"space-y-3\">
<div class="analysis-section">
<h3 class="text-lg font-bold mb-3 text-gray-800">🔍 <span data-i18n="ai_mentor.trading_patterns">Analisis Pola Trading</span></h3>
<div class="space-y-3">
<div>
<span class=\"font-semibold text-gray-700\">Pola Utama:</span>
<span class=\"text-blue-600\">{{ analysis.pola_trading.pola_utama }}</span>
<span class="font-semibold text-gray-700" data-i18n="ai_mentor.main_pattern">Pola Utama:</span>
<span class="text-blue-600">{{ analysis.pola_trading.pola_utama }}</span>
</div>
<div>
<p class=\"text-gray-700\">{{ analysis.pola_trading.analisis }}</p>
<p class="text-gray-700">{{ analysis.pola_trading.analisis }}</p>
</div>
<div class=\"bg-green-50 p-3 rounded-lg\">
<strong class=\"text-green-700\">💪 Kekuatan:</strong>
<p class=\"text-green-600\">{{ analysis.pola_trading.kekuatan }}</p>
<div class="bg-green-50 p-3 rounded-lg">
<strong class="text-green-700">💪 <span data-i18n="ai_mentor.strength">Kekuatan:</span></strong>
<p class="text-green-600">{{ analysis.pola_trading.kekuatan }}</p>
</div>
<div class=\"bg-blue-50 p-3 rounded-lg\">
<strong class=\"text-blue-700\">🎯 Area Perbaikan:</strong>
<p class=\"text-blue-600\">{{ analysis.pola_trading.area_perbaikan }}</p>
<div class="bg-blue-50 p-3 rounded-lg">
<strong class="text-blue-700">🎯 <span data-i18n="ai_mentor.improvement_areas">Area Perbaikan:</span></strong>
<p class="text-blue-600">{{ analysis.pola_trading.area_perbaikan }}</p>
</div>
</div>
</div>
<!-- Emotional Analysis -->
<div class=\"analysis-section\">
<h3 class=\"text-lg font-bold mb-3 text-gray-800\">🧠 Analisis Emosi vs Performa</h3>
<div class=\"space-y-3\">
<div class=\"bg-purple-50 p-3 rounded-lg\">
<strong class=\"text-purple-700\">💭 Feedback Emosi:</strong>
<p class=\"text-purple-600\">{{ analysis.emosi_vs_performa.feedback }}</p>
<div class="analysis-section">
<h3 class="text-lg font-bold mb-3 text-gray-800">🧠 <span data-i18n="ai_mentor.emotional_analysis">Analisis Emosi vs Performa</span></h3>
<div class="space-y-3">
<div class="bg-purple-50 p-3 rounded-lg">
<strong class="text-purple-700">💭 <span data-i18n="ai_mentor.emotional_feedback">Feedback Emosi:</span></strong>
<p class="text-purple-600">{{ analysis.emosi_vs_performa.feedback }}</p>
</div>
<div class=\"bg-yellow-50 p-3 rounded-lg\">
<strong class=\"text-yellow-700\">💡 Tip:</strong>
<p class=\"text-yellow-600\">{{ analysis.emosi_vs_performa.tip }}</p>
<div class="bg-yellow-50 p-3 rounded-lg">
<strong class="text-yellow-700">💡 <span data-i18n="ai_mentor.tip">Tip:</span></strong>
<p class="text-yellow-600">{{ analysis.emosi_vs_performa.tip }}</p>
</div>
</div>
</div>
</div>
<!-- Risk Management -->
<div class=\"analysis-section\">
<h3 class=\"text-lg font-bold mb-3 text-gray-800\">🛡️ Evaluasi Manajemen Risiko</h3>
<div class=\"flex items-center space-x-4 mb-4\">
<span class=\"risk-score risk-{{ 'excellent' if '10' in analysis.manajemen_risiko.nilai else 'good' if any(x in analysis.manajemen_risiko.nilai for x in ['8', '9']) else 'warning' if any(x in analysis.manajemen_risiko.nilai for x in ['6', '7']) else 'danger' }}\">
<div class="analysis-section">
<h3 class="text-lg font-bold mb-3 text-gray-800">🛡️ <span data-i18n="ai_mentor.risk_management">Evaluasi Manajemen Risiko</span></h3>
<div class="flex items-center space-x-4 mb-4">
<span class="risk-score risk-{{ 'excellent' if '10' in analysis.manajemen_risiko.nilai else 'good' if any(x in analysis.manajemen_risiko.nilai for x in ['8', '9']) else 'warning' if any(x in analysis.manajemen_risiko.nilai for x in ['6', '7']) else 'danger' }}">
{{ analysis.manajemen_risiko.nilai }}
</span>
<span class=\"text-gray-700\">{{ analysis.manajemen_risiko.feedback }}</span>
<span class="text-gray-700">{{ analysis.manajemen_risiko.feedback }}</span>
</div>
{% if analysis.manajemen_risiko.detail %}
<div class=\"bg-gray-50 p-3 rounded-lg mb-3\">
<p class=\"text-gray-700\">{{ analysis.manajemen_risiko.detail }}</p>
<div class="bg-gray-50 p-3 rounded-lg mb-3">
<p class="text-gray-700">{{ analysis.manajemen_risiko.detail }}</p>
</div>
{% endif %}
{% if analysis.manajemen_risiko.apresiasi %}
<div class=\"bg-green-50 p-3 rounded-lg\">
<p class=\"text-green-700\">{{ analysis.manajemen_risiko.apresiasi }}</p>
<div class="bg-green-50 p-3 rounded-lg">
<p class="text-green-700">{{ analysis.manajemen_risiko.apresiasi }}</p>
</div>
{% elif analysis.manajemen_risiko.saran %}
<div class=\"bg-orange-50 p-3 rounded-lg\">
<p class=\"text-orange-700\">{{ analysis.manajemen_risiko.saran }}</p>
<div class="bg-orange-50 p-3 rounded-lg">
<p class="text-orange-700">{{ analysis.manajemen_risiko.saran }}</p>
</div>
{% elif analysis.manajemen_risiko.peringatan %}
<div class=\"bg-red-50 p-3 rounded-lg\">
<p class=\"text-red-700\">{{ analysis.manajemen_risiko.peringatan }}</p>
<div class="bg-red-50 p-3 rounded-lg">
<p class="text-red-700">{{ analysis.manajemen_risiko.peringatan }}</p>
</div>
{% endif %}
</div>
<!-- Recommendations -->
<div class=\"analysis-section\">
<h3 class=\"text-lg font-bold mb-3 text-gray-800\">💡 Rekomendasi AI Mentor</h3>
<div class=\"space-y-2\">
<div class="analysis-section">
<h3 class="text-lg font-bold mb-3 text-gray-800">💡 <span data-i18n="ai_mentor.recommendations">Rekomendasi AI Mentor</span></h3>
<div class="space-y-2">
{% for rekomendasi in analysis.rekomendasi %}
<div class=\"recommendation-item\">
<p class=\"text-gray-700\">{{ rekomendasi }}</p>
<div class="recommendation-item">
<p class="text-gray-700">{{ rekomendasi }}</p>
</div>
{% endfor %}
</div>
</div>
<!-- Motivation Message -->
<div class=\"bg-gradient-to-r from-green-400 to-blue-500 text-white rounded-lg p-6\">
<h3 class=\"text-xl font-bold mb-3\">💪 Pesan Motivasi</h3>
<p class=\"text-lg leading-relaxed\">{{ analysis.motivasi }}</p>
<div class="bg-gradient-to-r from-green-400 to-blue-500 text-white rounded-lg p-6">
<h3 class="text-xl font-bold mb-3">💪 <span data-i18n="ai_mentor.motivational_message">Pesan Motivasi</span></h3>
<p class="text-lg leading-relaxed">{{ analysis.motivasi }}</p>
</div>
<!-- Trade Details -->
{% if session_data.trades %}
<div class=\"analysis-section\">
<h3 class=\"text-lg font-bold mb-3 text-gray-800\">📈 Detail Trades Hari Ini</h3>
<div class=\"space-y-2\">
<div class="analysis-section">
<h3 class="text-lg font-bold mb-3 text-gray-800">📈 <span data-i18n="ai_mentor.trade_details">Detail Trades Hari Ini</span></h3>
<div class="space-y-2">
{% for trade in session_data.trades %}
<div class=\"trade-item\">
<div class=\"flex-1\">
<span class=\"font-semibold\">{{ trade.symbol }}</span>
<span class=\"text-sm text-gray-500 ml-2\">Lot: {{ trade.lot_size }}</span>
<div class="trade-item">
<div class="flex-1">
<span class="font-semibold">{{ trade.symbol }}</span>
<span class="text-sm text-gray-500 ml-2" data-i18n="ai_mentor.lot_size">Lot: {{ trade.lot_size }}</span>
{% if trade.strategy %}
<span class=\"text-xs bg-gray-200 px-2 py-1 rounded ml-2\">{{ trade.strategy }}</span>
<span class="text-xs bg-gray-200 px-2 py-1 rounded ml-2">{{ trade.strategy }}</span>
{% endif %}
</div>
<div class=\"text-right\">
<div class=\"{{ 'profit-positive' if trade.profit > 0 else 'profit-negative' if trade.profit < 0 else 'text-gray-600' }}\">
${{ \"%.2f\"|format(trade.profit) }}
<div class="text-right">
<div class="{{ 'profit-positive' if trade.profit > 0 else 'profit-negative' if trade.profit < 0 else 'text-gray-600' }}">
${{ "%.2f"|format(trade.profit) }}
</div>
<div class=\"text-xs text-gray-500\">
SL: {{ '✅' if trade.stop_loss_used else '❌' }} |
TP: {{ '✅' if trade.take_profit_used else '❌' }}
<div class="text-xs text-gray-500">
<span data-i18n="ai_mentor.sl">SL: {{ '✅' if trade.stop_loss_used else '❌' }}</span> |
<span data-i18n="ai_mentor.tp">TP: {{ '✅' if trade.take_profit_used else '❌' }}</span>
</div>
</div>
</div>
@@ -254,22 +255,22 @@
<!-- Personal Notes -->
{% if session_data.personal_notes %}
<div class=\"analysis-section\">
<h3 class=\"text-lg font-bold mb-3 text-gray-800\">📝 Catatan Pribadi Anda</h3>
<div class=\"bg-blue-50 p-4 rounded-lg border-l-4 border-blue-500\">
<p class=\"text-gray-700 italic\">\"{{ session_data.personal_notes }}\"</p>
<div class="analysis-section">
<h3 class="text-lg font-bold mb-3 text-gray-800">📝 <span data-i18n="ai_mentor.personal_notes">Catatan Pribadi Anda</span></h3>
<div class="bg-blue-50 p-4 rounded-lg border-l-4 border-blue-500">
<p class="text-gray-700 italic">"{{ session_data.personal_notes }}"</p>
</div>
</div>
{% endif %}
{% else %}
<!-- No Data State -->
<div class=\"text-center py-12\">
<div class=\"text-6xl mb-4\">📊</div>
<h2 class=\"text-2xl font-bold text-gray-600 mb-4\">Belum Ada Data Trading</h2>
<p class=\"text-gray-500 mb-6\">Mulai trading untuk mendapatkan analisis personal dari AI mentor Anda!</p>
<a href=\"{{ url_for('ai_mentor.dashboard') }}\" class=\"bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors\">
Kembali ke Dashboard
<div class="text-center py-12">
<div class="text-6xl mb-4">📊</div>
<h2 class="text-2xl font-bold text-gray-600 mb-4" data-i18n="ai_mentor.no_trading_data">Belum Ada Data Trading</h2>
<p class="text-gray-500 mb-6" data-i18n="ai_mentor.start_trading_for_analysis">Mulai trading untuk mendapatkan analisis personal dari AI mentor Anda!</p>
<a href="{{ url_for('ai_mentor.dashboard') }}" class="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors">
<span data-i18n="ai_mentor.back_to_dashboard_button">Kembali ke Dashboard</span>
</a>
</div>
{% endif %}
@@ -277,23 +278,23 @@
{% if ai_report %}
<!-- Full AI Report Modal (Optional) -->
<div id=\"fullReportModal\" class=\"fixed inset-0 bg-black bg-opacity-50 hidden z-50\">
<div class=\"flex items-center justify-center min-h-screen p-4\">
<div class=\"bg-white rounded-lg max-w-4xl w-full max-h-96 overflow-y-auto p-6\">
<div class=\"flex justify-between items-center mb-4\">
<h3 class=\"text-lg font-bold\">📝 Laporan AI Lengkap</h3>
<button onclick=\"closeFullReport()\" class=\"text-gray-500 hover:text-gray-700\">
<div id="fullReportModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="bg-white rounded-lg max-w-4xl w-full max-h-96 overflow-y-auto p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-bold">📝 <span data-i18n="ai_mentor.full_report">Laporan AI Lengkap</span></h3>
<button onclick="closeFullReport()" class="text-gray-500 hover:text-gray-700">
</button>
</div>
<pre class=\"whitespace-pre-wrap text-sm bg-gray-50 p-4 rounded-lg\">{{ ai_report }}</pre>
<pre class="whitespace-pre-wrap text-sm bg-gray-50 p-4 rounded-lg">{{ ai_report }}</pre>
</div>
</div>
</div>
<div class=\"fixed bottom-4 right-4\">
<button onclick=\"showFullReport()\" class=\"bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 transition-colors\">
📝 Lihat Laporan Lengkap
<div class="fixed bottom-4 right-4">
<button onclick="showFullReport()" class="bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 transition-colors">
📝 <span data-i18n="ai_mentor.view_full_report">Lihat Laporan Lengkap</span>
</button>
</div>
+54 -53
View File
@@ -1,7 +1,8 @@
<!-- templates/ai_mentor/dashboard.html -->
{% extends "base.html" %}
{% block title %}🧠 AI Mentor Trading - QuantumBotX{% endblock %}
{% block title_key %}ai_mentor.page_title{% endblock %}
{% block title %}🧠 AI Mentor Trading{% endblock %}
{% block head_extra %}
<style>
@@ -20,11 +21,11 @@
font-weight: 500;
}
.emotion-tenang { background-color: #10b981; color: white; }
.emotion-serakah { background-color: #f59e0b; color: white; }
.emotion-takut { background-color: #8b5cf6; color: white; }
.emotion-frustasi { background-color: #ef4444; color: white; }
.emotion-netral { background-color: #6b7280; color: white; }
.emotion-calm { background-color: #10b981; color: white; }
.emotion-greedy { background-color: #f59e0b; color: white; }
.emotion-fear { background-color: #8b5cf6; color: white; }
.emotion-frustrated { background-color: #ef4444; color: white; }
.emotion-neutral { background-color: #6b7280; color: white; }
.profit-positive { color: #10b981; font-weight: bold; }
.profit-negative { color: #ef4444; font-weight: bold; }
@@ -149,8 +150,8 @@
<h2 class="text-xl font-bold mb-2">{{ holiday_config.active_holiday }} 🎆</h2>
<p class="text-lg">{{ holiday_greeting }}</p>
{% if holiday_config.adjustments.risk_reduction %}
<p class="text-sm mt-2 opacity-90">
⚠️ Risk otomatis dikurangi {{ (100 - holiday_config.adjustments.risk_reduction * 100)|int }}% untuk periode liburan
<p class="text-sm mt-2 opacity-90" data-i18n="ai_mentor.holiday_risk_reduction">
⚠️ Risk automatically reduced by {{ (100 - holiday_config.adjustments.risk_reduction * 100)|int }}% during holiday period
</p>
{% endif %}
</div>
@@ -158,14 +159,14 @@
<!-- Header -->
<div class="mentor-card">
<h1 class="text-3xl font-bold mb-2">🧠 AI Mentor Trading Indonesia</h1>
<p class="text-blue-100 text-lg">Mentor digital Anda untuk sukses trading jangka panjang</p>
<h1 class="text-3xl font-bold mb-2" data-i18n="ai_mentor.header">🧠 AI Mentor Trading</h1>
<p class="text-blue-100 text-lg" data-i18n="ai_mentor.subtitle">Your digital mentor for long-term trading success</p>
<div class="mt-4 flex items-center space-x-4">
<span class="text-blue-200">🇮🇩 Bahasa Indonesia</span>
<span class="text-blue-200">• 📊 Analisis Real-time</span>
<span class="text-blue-200">• 🎯 Personal</span>
<span class="text-blue-200" data-i18n="ai_mentor.language_indonesian">🇺🇸 English Language</span>
<span class="text-blue-200" data-i18n="ai_mentor.realtime">• 📊 Real-time Analysis</span>
<span class="text-blue-200" data-i18n="ai_mentor.personal">• 🎯 Personal</span>
{% if holiday_config.active_holiday %}
<span class="text-blue-200">• {{ holiday_config.active_holiday.split()[0] }} 🎄</span>
<span class="text-blue-200" data-i18n="ai_mentor.holiday">• {{ holiday_config.active_holiday.split()[0] }} 🎄</span>
{% endif %}
</div>
</div>
@@ -173,35 +174,35 @@
<!-- Quick Stats -->
<div class="quick-stats">
<div class="stat-card">
<h3 class="text-lg font-semibold text-gray-600 mb-2">Total Sesi</h3>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.total_sessions">Total Sessions</h3>
<div class="text-3xl font-bold text-blue-600">{{ total_sessions or 0 }}</div>
<p class="text-sm text-gray-500 mt-1">sesi trading</p>
<p class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.sessions">trading sessions</p>
</div>
<div class="stat-card">
<h3 class="text-lg font-semibold text-gray-600 mb-2">Win Rate</h3>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.win_rate">Win Rate</h3>
<div class="text-3xl font-bold {{ 'text-green-600' if (win_rate or 0) >= 60 else 'text-red-600' if (win_rate or 0) < 40 else 'text-yellow-600' }}">{{ "%.1f"|format(win_rate or 0) }}%</div>
<p class="text-sm text-gray-500 mt-1">sesi profit</p>
<p class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.profit_sessions">profit sessions</p>
</div>
<div class="stat-card">
<h3 class="text-lg font-semibold text-gray-600 mb-2">Hari Ini</h3>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.today">Today</h3>
{% if today_session %}
{% set profit_loss = today_session.get('profit_loss', today_session.get('total_profit_loss', 0)) %}
<div class="text-2xl font-bold {{ 'profit-positive' if profit_loss > 0 else 'profit-negative' if profit_loss < 0 else 'text-gray-600' }}">
${{ "%.2f"|format(profit_loss) }}
</div>
<span class="emotion-badge emotion-{{ today_session.get('emotions', 'netral') }}">{{ (today_session.get('emotions', 'netral')|title) }}</span>
<span class="emotion-badge emotion-{{ today_session.get('emotions', 'neutral') }}">{{ (today_session.get('emotions', 'neutral')|title) }}</span>
{% else %}
<div class="text-2xl font-bold text-gray-400">-</div>
<p class="text-sm text-gray-500 mt-1">belum trading</p>
<p class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.not_traded">not traded yet</p>
{% endif %}
</div>
<div class="stat-card">
<h3 class="text-lg font-semibold text-gray-600 mb-2">Status AI</h3>
<div class="text-2xl font-bold text-green-600">🤖 Aktif</div>
<p class="text-sm text-gray-500 mt-1">siap menganalisis</p>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.ai_status">AI Status</h3>
<div class="text-2xl font-bold text-green-600" data-i18n="ai_mentor.ai_active">🤖 Active</div>
<p class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.ai_ready">ready to analyze</p>
</div>
</div>
@@ -211,7 +212,7 @@
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-bold mb-4 flex items-center">
<span class="mr-2">📊</span>
Trading Hari Ini
<span data-i18n="ai_mentor.today_trading">Today's Trading</span>
</h2>
{% if today_session %}
@@ -219,43 +220,43 @@
{% set total_trades = today_session.get('total_trades', today_session.get('trades_count', 0)) %}
<div class="space-y-3">
<div class="flex justify-between items-center">
<span class="text-gray-600">Total Trades:</span>
<span class="text-gray-600" data-i18n="ai_mentor.total_trades">Total Trades:</span>
<span class="font-semibold">{{ total_trades }}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-600">P&L:</span>
<span class="text-gray-600" data-i18n="ai_mentor.pnl">P&L:</span>
<span class="font-semibold {{ 'profit-positive' if profit_loss > 0 else 'profit-negative' if profit_loss < 0 else 'text-gray-600' }}">
${{ "%.2f"|format(profit_loss) }}
</span>
</div>
<div class="flex justify-between items-center">
<span class="text-gray-600">Emosi:</span>
<span class="emotion-badge emotion-{{ today_session.get('emotions', 'netral') }}">{{ (today_session.get('emotions', 'netral')|title) }}</span>
<span class="text-gray-600" data-i18n="ai_mentor.emotion">Emotion:</span>
<span class="emotion-badge emotion-{{ today_session.get('emotions', 'neutral') }}">{{ (today_session.get('emotions', 'neutral')|title) }}</span>
</div>
{% if today_session.get('personal_notes') %}
<div class="ai-insight">
<p class="text-sm"><strong>Catatan Anda:</strong></p>
<p class="text-sm"><strong data-i18n="ai_mentor.your_notes">Your Notes:</strong></p>
<p class="text-gray-700 italic">"{{ today_session.get('personal_notes') }}"</p>
</div>
{% endif %}
<div class="mt-4 space-y-2">
<a href="{{ url_for('ai_mentor.today_report') }}" class="block w-full bg-blue-600 text-white text-center py-2 rounded-lg hover:bg-blue-700 transition-colors">
🧠 Lihat Analisis AI Lengkap
<span data-i18n="ai_mentor.view_full_analysis">🧠 View Full AI Analysis</span>
</a>
<button onclick="openQuickFeedback()" class="block w-full bg-green-600 text-white text-center py-2 rounded-lg hover:bg-green-700 transition-colors">
✏️ Update Emosi & Catatan
<span data-i18n="ai_mentor.update_emotion">✏️ Update Emotion & Notes</span>
</button>
</div>
</div>
{% else %}
<div class="text-center py-8">
<div class="text-6xl mb-4">📈</div>
<h3 class="text-lg font-semibold text-gray-600 mb-2">Belum Ada Trading Hari Ini</h3>
<p class="text-gray-500 mb-4">Mulai trading untuk mendapatkan analisis AI yang personal!</p>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.not_traded_today">No Trading Today Yet</h3>
<p class="text-gray-500 mb-4" data-i18n="ai_mentor.start_trading">Start trading to get personalized AI analysis!</p>
<button onclick="openQuickFeedback()" class="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors">
📝 Catat Emosi Trading
<span data-i18n="ai_mentor.record_emotion">📝 Record Trading Emotion</span>
</button>
</div>
{% endif %}
@@ -265,7 +266,7 @@
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-bold mb-4 flex items-center">
<span class="mr-2">🤖</span>
AI Insights Terbaru
<span data-i18n="ai_mentor.ai_insights">Latest AI Insights</span>
</h2>
{% if recent_reports %}
@@ -280,21 +281,21 @@
</span>
</div>
<p class="text-sm text-gray-600">{{ (report.get('motivation', 'No insights available'))[:100] }}{% if (report.get('motivation', '')|length) > 100 %}...{% endif %}</p>
<span class="emotion-badge emotion-{{ report.get('emotions', 'netral') }} text-xs mt-1 inline-block">{{ (report.get('emotions', 'netral')|title) }}</span>
<span class="emotion-badge emotion-{{ report.get('emotions', 'neutral') }} text-xs mt-1 inline-block">{{ (report.get('emotions', 'neutral')|title) }}</span>
</div>
{% endfor %}
</div>
<div class="mt-4">
<a href="{{ url_for('ai_mentor.history') }}" class="block w-full bg-gray-600 text-white text-center py-2 rounded-lg hover:bg-gray-700 transition-colors">
📚 Lihat Semua Riwayat
<span data-i18n="ai_mentor.view_history">📚 View All History</span>
</a>
</div>
{% else %}
<div class="text-center py-8">
<div class="text-6xl mb-4">🤖</div>
<h3 class="text-lg font-semibold text-gray-600 mb-2">AI Siap Membantu!</h3>
<p class="text-gray-500">Mulai trading untuk mendapatkan insight personal dari AI mentor Anda.</p>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.no_insights">AI is Ready to Help!</h3>
<p class="text-gray-500" data-i18n="ai_mentor.start_trading_insights">Start trading to get personalized insights from your AI mentor.</p>
</div>
{% endif %}
</div>
@@ -304,46 +305,46 @@
<div class="bg-gradient-to-r from-green-400 to-blue-500 rounded-lg shadow-md p-6 text-white">
<h2 class="text-xl font-bold mb-4 flex items-center">
<span class="mr-2">💡</span>
Tips Harian dari AI Mentor
<span data-i18n="ai_mentor.daily_tips">Daily Tips from AI Mentor</span>
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="bg-white bg-opacity-20 rounded-lg p-4">
<h4 class="font-semibold mb-2">🎯 Konsistensi</h4>
<p class="text-sm">"Profit kecil tapi konsisten lebih baik daripada profit besar sekali terus loss."</p>
<h4 class="font-semibold mb-2" data-i18n="ai_mentor.consistency">🎯 Consistency</h4>
<p class="text-sm" data-i18n="ai_mentor.consistency_tip">"Small but consistent profits are better than one big profit followed by losses."</p>
</div>
<div class="bg-white bg-opacity-20 rounded-lg p-4">
<h4 class="font-semibold mb-2">🛡️ Risk Management</h4>
<p class="text-sm">"Jangan pernah risiko lebih dari 2% modal per trade. Modal adalah nyawa trader!"</p>
<h4 class="font-semibold mb-2" data-i18n="ai_mentor.risk_management">🛡️ Risk Management</h4>
<p class="text-sm" data-i18n="ai_mentor.risk_tip">"Never risk more than 2% of your capital per trade. Capital is a trader's life!"</p>
</div>
<div class="bg-white bg-opacity-20 rounded-lg p-4">
<h4 class="font-semibold mb-2">🧠 Emosi</h4>
<p class="text-sm">"Trading dengan emosi tenang adalah kunci trader profesional. Istirahat jika frustasi."</p>
<h4 class="font-semibold mb-2" data-i18n="ai_mentor.emotions">🧠 Emotions</h4>
<p class="text-sm" data-i18n="ai_mentor.emotion_tip">"Trading with a calm emotion is the key to professional trading. Take a break if frustrated."</p>
</div>
</div>
</div>
</div>
<!-- Floating Chat Button -->
<button class="floating-chat-btn" onclick="openQuickFeedback()" title="Chat dengan AI Mentor">
<button class="floating-chat-btn" onclick="openQuickFeedback()" title="Chat with AI Mentor">
💬
</button>
<!-- Quick Feedback Modal (akan dimuat dengan AJAX) -->
<!-- Quick Feedback Modal (will be loaded with AJAX) -->
<div id="quickFeedbackModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="bg-white rounded-lg max-w-md w-full p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-bold">💬 Chat dengan AI Mentor</h3>
<h3 class="text-lg font-bold" data-i18n="ai_mentor.chat_with_mentor">💬 Chat with AI Mentor</h3>
<button onclick="closeQuickFeedback()" class="text-gray-500 hover:text-gray-700">
</button>
</div>
<div id="quickFeedbackContent">
<!-- Content akan dimuat dengan AJAX -->
<!-- Content will be loaded with AJAX -->
<div class="text-center py-4">
<div class="text-4xl mb-2">🤖</div>
<p class="text-gray-600">Fitur sedang dikembangkan...</p>
<p class="text-sm text-gray-500 mt-2">Sementara ini, gunakan dashboard untuk melihat analisis AI.</p>
<p class="text-gray-600" data-i18n="ai_mentor.developing">Feature under development...</p>
<p class="text-sm text-gray-500 mt-2" data-i18n="ai_mentor.use_dashboard">For now, use the dashboard to view AI analysis.</p>
</div>
</div>
</div>
+39 -38
View File
@@ -1,6 +1,7 @@
{% extends "base.html" %}
{% block title %}📊 Riwayat AI Mentor - QuantumBotX{% endblock %}
{% block title_key %}ai_mentor.history_page_title{% endblock %}
{% block title %}📊 AI Mentor History{% endblock %}
{% block head_extra %}
<style>
@@ -35,11 +36,11 @@
font-weight: 500;
}
.emotion-tenang { background-color: #10b981; color: white; }
.emotion-serakah { background-color: #f59e0b; color: white; }
.emotion-takut { background-color: #8b5cf6; color: white; }
.emotion-frustasi { background-color: #ef4444; color: white; }
.emotion-netral { background-color: #6b7280; color: white; }
.emotion-calm { background-color: #10b981; color: white; }
.emotion-greedy { background-color: #f59e0b; color: white; }
.emotion-fear { background-color: #8b5cf6; color: white; }
.emotion-frustrated { background-color: #ef4444; color: white; }
.emotion-neutral { background-color: #6b7280; color: white; }
.profit-positive { color: #10b981; font-weight: bold; }
.profit-negative { color: #ef4444; font-weight: bold; }
@@ -143,58 +144,58 @@
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<div class="mentor-card">
<h1 class="text-3xl font-bold mb-2">📊 Riwayat AI Mentor Trading</h1>
<p class="text-blue-100 text-lg">Analisis lengkap perjalanan trading Anda dengan bantuan AI Indonesia</p>
<h1 class="text-3xl font-bold mb-2" data-i18n="ai_mentor.history_title">📊 AI Mentor Trading History</h1>
<p class="text-blue-100 text-lg" data-i18n="ai_mentor.history_subtitle">Complete trading journey analysis with AI assistance</p>
<div class="mt-4 flex items-center space-x-4">
<span class="text-blue-200">🇮🇩 Bahasa Indonesia</span>
<span class="text-blue-200">• 📈 Analisis Performa</span>
<span class="text-blue-200">• 🧠 AI Insights</span>
<span class="text-blue-200" data-i18n="ai_mentor.language">🇺🇸 English Language</span>
<span class="text-blue-200" data-i18n="ai_mentor.performance_analysis">• 📈 Performance Analysis</span>
<span class="text-blue-200" data-i18n="ai_mentor.ai_insights">• 🧠 AI Insights</span>
</div>
</div>
<!-- Quick Statistics -->
<div class="stats-grid">
<div class="stat-card">
<h3 class="text-lg font-semibold text-gray-600 mb-2">Total Laporan</h3>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.total_reports">Total Reports</h3>
<div class="text-3xl font-bold text-blue-600">{{ reports|length if reports else 0 }}</div>
<p class="text-sm text-gray-500 mt-1">sesi dianalisis</p>
<p class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.sessions_analyzed">sessions analyzed</p>
</div>
<div class="stat-card">
<h3 class="text-lg font-semibold text-gray-600 mb-2">Periode</h3>
<div class="text-2xl font-bold text-purple-600">{{ days }} hari</div>
<p class="text-sm text-gray-500 mt-1">riwayat terakhir</p>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.period">Period</h3>
<div class="text-2xl font-bold text-purple-600">{{ days }} <span data-i18n="ai_mentor.days">days</span></div>
<p class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.last_history">last history</p>
</div>
<div class="stat-card">
<h3 class="text-lg font-semibold text-gray-600 mb-2">Performa</h3>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.performance">Performance</h3>
{% set total_profit = reports|sum(attribute='profit_loss') if reports else 0 %}
<div class="text-2xl font-bold {{ 'profit-positive' if total_profit > 0 else 'profit-negative' if total_profit < 0 else 'text-gray-600' }}">
${{ "%.2f"|format(total_profit) }}
</div>
<p class="text-sm text-gray-500 mt-1">total P&L</p>
<p class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.total_pnl">total P&L</p>
</div>
<div class="stat-card">
<h3 class="text-lg font-semibold text-gray-600 mb-2">Win Rate</h3>
<h3 class="text-lg font-semibold text-gray-600 mb-2" data-i18n="ai_mentor.win_rate">Win Rate</h3>
{% set profitable_sessions = reports|selectattr('profit_loss', '>', 0)|list|length if reports else 0 %}
{% set win_rate = (profitable_sessions / reports|length * 100) if reports|length > 0 else 0 %}
<div class="text-2xl font-bold {{ 'text-green-600' if win_rate >= 60 else 'text-red-600' if win_rate < 40 else 'text-yellow-600' }}">
{{ "%.1f"|format(win_rate) }}%
</div>
<p class="text-sm text-gray-500 mt-1">sesi profit</p>
<p class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.profit_sessions">profit sessions</p>
</div>
</div>
<!-- Date Filter -->
<div class="date-filter">
<div class="flex items-center justify-between">
<h3 class="text-lg font-semibold text-gray-700">Filter Periode</h3>
<h3 class="text-lg font-semibold text-gray-700" data-i18n="ai_mentor.period_filter">Period Filter</h3>
<div class="flex space-x-2">
<button class="filter-btn {{ 'active' if days == 7 }}" onclick="changeFilter(7)">7 Hari</button>
<button class="filter-btn {{ 'active' if days == 30 }}" onclick="changeFilter(30)">30 Hari</button>
<button class="filter-btn {{ 'active' if days == 90 }}" onclick="changeFilter(90)">90 Hari</button>
<button class="filter-btn {{ 'active' if days == 365 }}" onclick="changeFilter(365)">1 Tahun</button>
<button class="filter-btn {{ 'active' if days == 7 }}" onclick="changeFilter(7)" data-i18n="ai_mentor.filter_7_days">7 Days</button>
<button class="filter-btn {{ 'active' if days == 30 }}" onclick="changeFilter(30)" data-i18n="ai_mentor.filter_30_days">30 Days</button>
<button class="filter-btn {{ 'active' if days == 90 }}" onclick="changeFilter(90)" data-i18n="ai_mentor.filter_90_days">90 Days</button>
<button class="filter-btn {{ 'active' if days == 365 }}" onclick="changeFilter(365)" data-i18n="ai_mentor.filter_1_year">1 Year</button>
</div>
</div>
</div>
@@ -210,7 +211,7 @@
📅 {{ (report.date if report.date else report.session_date).strftime('%d %B %Y') }}
</div>
<div class="text-sm text-gray-500 mt-1">
{{ (report.date if report.date else report.session_date).strftime('%A') }} - {{ 'Sesi Trading' }}
{{ (report.date if report.date else report.session_date).strftime('%A') }} - <span data-i18n="ai_mentor.trading_session">Trading Session</span>
</div>
</div>
<div class="session-performance">
@@ -220,7 +221,7 @@
</span>
{% endif %}
<span class="trades-count">
{{ report.total_trades or report.trades|length or 0 }} trades
{{ report.total_trades or report.trades|length or 0 }} <span data-i18n="ai_mentor.trades">trades</span>
</span>
<div class="text-xl font-bold {{ 'profit-positive' if (report.profit_loss or 0) > 0 else 'profit-negative' if (report.profit_loss or 0) < 0 else 'text-gray-600' }}">
${{ "%.2f"|format(report.profit_loss or 0) }}
@@ -230,18 +231,18 @@
{% if report.motivation %}
<div class="ai-summary">
<strong>🤖 AI Summary:</strong> {{ report.motivation[:200] }}{% if report.motivation|length > 200 %}...{% endif %}
<strong data-i18n="ai_mentor.ai_summary">🤖 AI Summary:</strong> {{ report.motivation[:200] }}{% if report.motivation|length > 200 %}...{% endif %}
</div>
{% endif %}
<div class="flex items-center justify-between mt-3">
<div class="text-sm text-gray-500">
{% if report.market_conditions %}
🌐 {{ report.market_conditions|title }} Market
🌐 {{ report.market_conditions|title }} <span data-i18n="ai_mentor.market">Market</span>
{% endif %}
</div>
<div class="text-blue-600 text-sm font-medium">
Lihat Detail →
<div class="text-blue-600 text-sm font-medium" data-i18n="ai_mentor.view_details">
View Details
</div>
</div>
</div>
@@ -251,8 +252,8 @@
<!-- Pagination (jika diperlukan) -->
{% if reports|length >= 10 %}
<div class="mt-8 text-center">
<button class="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors">
Muat Lebih Banyak
<button class="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors" data-i18n="ai_mentor.load_more">
Load More
</button>
</div>
{% endif %}
@@ -261,16 +262,16 @@
<!-- Empty State -->
<div class="empty-state">
<div class="text-6xl mb-4">📊</div>
<h3 class="text-xl font-semibold text-gray-700 mb-2">Belum Ada Riwayat Trading</h3>
<p class="text-gray-500 mb-6">
Mulai trading untuk mendapatkan analisis AI dan saran personal dari mentor digital Anda.
<h3 class="text-xl font-semibold text-gray-700 mb-2" data-i18n="ai_mentor.no_history">No Trading History Yet</h3>
<p class="text-gray-500 mb-6" data-i18n="ai_mentor.start_trading_for_history">
Start trading to get AI analysis and personalized advice from your digital mentor.
</p>
<div class="space-x-4">
<a href="/ai-mentor" class="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors inline-block">
Mulai Trading Session
<span data-i18n="ai_mentor.start_trading_session">Start Trading Session</span>
</a>
<a href="/trading_bots" class="bg-gray-600 text-white px-6 py-3 rounded-lg hover:bg-gray-700 transition-colors inline-block">
Lihat Trading Bots
<span data-i18n="ai_mentor.view_trading_bots">View Trading Bots</span>
</a>
</div>
</div>
+47 -47
View File
@@ -1,65 +1,65 @@
<!-- Quick feedback form untuk modal AI mentor -->
<form id=\"quickFeedbackForm\" onsubmit=\"submitQuickFeedback(event)\">
<div class=\"space-y-4\">
<form id="quickFeedbackForm" onsubmit="submitQuickFeedback(event)">
<div class="space-y-4">
<!-- Emosi Saat Ini -->
<div>
<label class=\"block text-sm font-semibold text-gray-700 mb-2\">🧠 Bagaimana perasaan Anda saat trading hari ini?</label>
<div class=\"grid grid-cols-2 gap-2\">
<button type=\"button\" onclick=\"selectEmotion('tenang')\" class=\"emotion-btn p-3 border rounded-lg text-left hover:bg-blue-50 focus:ring-2 focus:ring-blue-500\" data-emotion=\"tenang\">
<div class=\"font-semibold text-green-600\">😌 Tenang</div>
<div class=\"text-xs text-gray-500\">Pikiran jernih, tidak terburu-buru</div>
<label class="block text-sm font-semibold text-gray-700 mb-2" data-i18n="ai_mentor.how_are_you_feeling">🧠 Bagaimana perasaan Anda saat trading hari ini?</label>
<div class="grid grid-cols-2 gap-2">
<button type="button" onclick="selectEmotion('tenang')" class="emotion-btn p-3 border rounded-lg text-left hover:bg-blue-50 focus:ring-2 focus:ring-blue-500" data-emotion="tenang">
<div class="font-semibold text-green-600" data-i18n="ai_mentor.emotion_calm">😌 Tenang</div>
<div class="text-xs text-gray-500" data-i18n="ai_mentor.calm_description">Pikiran jernih, tidak terburu-buru</div>
</button>
<button type=\"button\" onclick=\"selectEmotion('serakah')\" class=\"emotion-btn p-3 border rounded-lg text-left hover:bg-yellow-50 focus:ring-2 focus:ring-yellow-500\" data-emotion=\"serakah\">
<div class=\"font-semibold text-yellow-600\">🤑 Serakah</div>
<div class=\"text-xs text-gray-500\">Ingin profit besar, agresif</div>
<button type="button" onclick="selectEmotion('serakah')" class="emotion-btn p-3 border rounded-lg text-left hover:bg-yellow-50 focus:ring-2 focus:ring-yellow-500" data-emotion="serakah">
<div class="font-semibold text-yellow-600" data-i18n="ai_mentor.emotion_greedy">🤑 Serakah</div>
<div class="text-xs text-gray-500" data-i18n="ai_mentor.greedy_description">Ingin profit besar, agresif</div>
</button>
<button type=\"button\" onclick=\"selectEmotion('takut')\" class=\"emotion-btn p-3 border rounded-lg text-left hover:bg-purple-50 focus:ring-2 focus:ring-purple-500\" data-emotion=\"takut\">
<div class=\"font-semibold text-purple-600\">😰 Takut</div>
<div class=\"text-xs text-gray-500\">Khawatir loss, ragu-ragu</div>
<button type="button" onclick="selectEmotion('takut')" class="emotion-btn p-3 border rounded-lg text-left hover:bg-purple-50 focus:ring-2 focus:ring-purple-500" data-emotion="takut">
<div class="font-semibold text-purple-600" data-i18n="ai_mentor.emotion_fearful">😰 Takut</div>
<div class="text-xs text-gray-500" data-i18n="ai_mentor.fearful_description">Khawatir loss, ragu-ragu</div>
</button>
<button type=\"button\" onclick=\"selectEmotion('frustasi')\" class=\"emotion-btn p-3 border rounded-lg text-left hover:bg-red-50 focus:ring-2 focus:ring-red-500\" data-emotion=\"frustasi\">
<div class=\"font-semibold text-red-600\">😤 Frustasi</div>
<div class=\"text-xs text-gray-500\">Kesal karena loss beruntun</div>
<button type="button" onclick="selectEmotion('frustasi')" class="emotion-btn p-3 border rounded-lg text-left hover:bg-red-50 focus:ring-2 focus:ring-red-500" data-emotion="frustasi">
<div class="font-semibold text-red-600" data-i18n="ai_mentor.emotion_frustrated">😤 Frustasi</div>
<div class="text-xs text-gray-500" data-i18n="ai_mentor.frustrated_description">Kesal karena loss beruntun</div>
</button>
</div>
<input type=\"hidden\" id=\"selectedEmotion\" name=\"emotions\" value=\"netral\" required>
<input type="hidden" id="selectedEmotion" name="emotions" value="netral" required>
</div>
<!-- P&L Saat Ini -->
<div>
<label for=\"currentPnL\" class=\"block text-sm font-semibold text-gray-700 mb-2\">💰 P&L Hari Ini (USD)</label>
<input type=\"number\" id=\"currentPnL\" name=\"current_pnl\" step=\"0.01\"
class=\"w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"
placeholder=\"Contoh: 25.50 atau -15.30\">
<label for="currentPnL" class="block text-sm font-semibold text-gray-700 mb-2" data-i18n="ai_mentor.today_pnl">💰 P&L Hari Ini (USD)</label>
<input type="number" id="currentPnL" name="current_pnl" step="0.01"
class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Contoh: 25.50 atau -15.30" data-i18n="ai_mentor.pnl_placeholder">
</div>
<!-- Catatan Personal -->
<div>
<label for=\"personalNotes\" class=\"block text-sm font-semibold text-gray-700 mb-2\">📝 Catatan Trading Hari Ini</label>
<textarea id=\"personalNotes\" name=\"notes\" rows=\"3\"
class=\"w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"
placeholder=\"Contoh: Hari ini fokus EURUSD, pakai SL ketat. Market agak volatile karena berita NFP...\"></textarea>
<label for="personalNotes" class="block text-sm font-semibold text-gray-700 mb-2" data-i18n="ai_mentor.today_notes">📝 Catatan Trading Hari Ini</label>
<textarea id="personalNotes" name="notes" rows="3"
class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Contoh: Hari ini fokus EURUSD, pakai SL ketat. Market agak volatile karena berita NFP..." data-i18n="ai_mentor.notes_placeholder"></textarea>
</div>
<!-- Action Buttons -->
<div class=\"flex space-x-3\">
<button type=\"button\" onclick=\"getInstantFeedback()\"
class=\"flex-1 bg-green-600 text-white py-3 px-4 rounded-lg hover:bg-green-700 transition-colors\">
⚡ Feedback Instan
<div class="flex space-x-3">
<button type="button" onclick="getInstantFeedback()"
class="flex-1 bg-green-600 text-white py-3 px-4 rounded-lg hover:bg-green-700 transition-colors">
<span data-i18n="ai_mentor.instant_feedback">⚡ Feedback Instan</span>
</button>
<button type=\"submit\"
class=\"flex-1 bg-blue-600 text-white py-3 px-4 rounded-lg hover:bg-blue-700 transition-colors\">
💾 Simpan Data
<button type="submit"
class="flex-1 bg-blue-600 text-white py-3 px-4 rounded-lg hover:bg-blue-700 transition-colors">
<span data-i18n="ai_mentor.save_data">💾 Simpan Data</span>
</button>
</div>
</div>
</form>
<!-- Instant Feedback Area -->
<div id=\"instantFeedback\" class=\"mt-4 hidden\">
<div class=\"bg-gradient-to-r from-blue-50 to-green-50 border border-blue-200 rounded-lg p-4\">
<h4 class=\"font-bold text-blue-800 mb-2\">🤖 Feedback AI Mentor:</h4>
<div id=\"feedbackContent\" class=\"text-gray-700\"></div>
<div id="instantFeedback" class="mt-4 hidden">
<div class="bg-gradient-to-r from-blue-50 to-green-50 border border-blue-200 rounded-lg p-4">
<h4 class="font-bold text-blue-800 mb-2" data-i18n="ai_mentor.ai_feedback">🤖 Feedback AI Mentor:</h4>
<div id="feedbackContent" class="text-gray-700"></div>
</div>
</div>
@@ -73,7 +73,7 @@ function selectEmotion(emotion) {
});
// Add selection to clicked button
const clickedBtn = document.querySelector(`[data-emotion=\"${emotion}\"]`);
const clickedBtn = document.querySelector(`[data-emotion="${emotion}"]`);
clickedBtn.classList.add('ring-2', 'bg-blue-100', 'border-blue-500');
// Update hidden input
@@ -92,7 +92,7 @@ function getInstantFeedback() {
const feedbackDiv = document.getElementById('instantFeedback');
const contentDiv = document.getElementById('feedbackContent');
feedbackDiv.classList.remove('hidden');
contentDiv.innerHTML = '<div class=\"animate-pulse\">🤖 AI sedang menganalisis trading Anda...</div>';
contentDiv.innerHTML = '<div class="animate-pulse" data-i18n="ai_mentor.analyzing">🤖 AI sedang menganalisis trading Anda...</div>';
fetch('/ai-mentor/api/generate-instant-feedback', {
method: 'POST',
@@ -106,30 +106,30 @@ function getInstantFeedback() {
if (data.success) {
const feedback = data.feedback;
contentDiv.innerHTML = `
<div class=\"space-y-3\">
<div class="space-y-3">
<div>
<strong class=\"text-blue-700\">🧠 Analisis Emosi:</strong>
<p class=\"text-gray-700\">${feedback.emotional_analysis}</p>
<strong class="text-blue-700" data-i18n="ai_mentor.emotional_analysis">🧠 Analisis Emosi:</strong>
<p class="text-gray-700">${feedback.emotional_analysis}</p>
</div>
<div>
<strong class=\"text-green-700\">💪 Motivasi:</strong>
<p class=\"text-gray-700\">${feedback.motivation}</p>
<strong class="text-green-700" data-i18n="ai_mentor.motivation">💪 Motivasi:</strong>
<p class="text-gray-700">${feedback.motivation}</p>
</div>
<div>
<strong class=\"text-purple-700\">💡 Tips Cepat:</strong>
<ul class=\"list-disc pl-5 text-gray-700\">
<strong class="text-purple-700" data-i18n="ai_mentor.quick_tips">💡 Tips Cepat:</strong>
<ul class="list-disc pl-5 text-gray-700">
${feedback.quick_tips.map(tip => `<li>${tip}</li>`).join('')}
</ul>
</div>
</div>
`;
} else {
contentDiv.innerHTML = `<div class=\"text-red-600\">❌ ${data.message}</div>`;
contentDiv.innerHTML = `<div class="text-red-600">❌ ${data.message}</div>`;
}
})
.catch(error => {
console.error('Error:', error);
contentDiv.innerHTML = '<div class=\"text-red-600\">❌ Gagal mendapatkan feedback. Silakan coba lagi.</div>';
contentDiv.innerHTML = '<div class="text-red-600" data-i18n="ai_mentor.failed_feedback">❌ Gagal mendapatkan feedback. Silakan coba lagi.</div>';
});
}
+26 -25
View File
@@ -1,5 +1,6 @@
{% extends "base.html" %}
{% block title_key %}ai_mentor.session_detail_title{% endblock %}
{% block title %}📈 Detail Sesi {{ session_date }} - AI Mentor{% endblock %}
{% block head_extra %}
@@ -141,17 +142,17 @@
<!-- Back Navigation -->
<a href="/ai-mentor/history" class="back-btn">
<i class="fas fa-arrow-left"></i>
Kembali ke Riwayat
<span data-i18n="ai_mentor.back_to_history">Kembali ke Riwayat</span>
</a>
<!-- Header -->
<div class="mentor-card">
<h1 class="text-3xl font-bold mb-2">📈 Detail Sesi Trading</h1>
<p class="text-blue-100 text-lg">{{ session_date }} - Analisis lengkap dari AI Mentor Indonesia</p>
<h1 class="text-3xl font-bold mb-2" data-i18n="ai_mentor.session_trading_detail">📈 Detail Sesi Trading</h1>
<p class="text-blue-100 text-lg">{{ session_date }} - <span data-i18n="ai_mentor.full_analysis">Analisis lengkap dari AI Mentor Indonesia</span></p>
<div class="mt-4 flex items-center space-x-4">
<span class="text-blue-200">🇮🇩 AI Analysis</span>
<span class="text-blue-200">• 🧠 Personal Insights</span>
<span class="text-blue-200">• 📊 Performance Review</span>
<span class="text-blue-200" data-i18n="ai_mentor.ai_analysis">🇮🇩 AI Analysis</span>
<span class="text-blue-200" data-i18n="ai_mentor.personal_insights">• 🧠 Personal Insights</span>
<span class="text-blue-200" data-i18n="ai_mentor.performance_review">• 📊 Performance Review</span>
</div>
</div>
@@ -161,12 +162,12 @@
<div class="text-2xl font-bold {{ 'profit-positive' if session_data.total_profit_loss > 0 else 'profit-negative' if session_data.total_profit_loss < 0 else 'text-gray-600' }}">
${{ "%.2f"|format(session_data.total_profit_loss or 0) }}
</div>
<div class="text-sm text-gray-500 mt-1">Total P&L</div>
<div class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.total_pnl">Total P&L</div>
</div>
<div class="perf-stat">
<div class="text-2xl font-bold text-blue-600">{{ session_data.trades|length if session_data.trades else 0 }}</div>
<div class="text-sm text-gray-500 mt-1">Total Trades</div>
<div class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.total_trades">Total Trades</div>
</div>
<div class="perf-stat">
@@ -176,7 +177,7 @@
<div class="text-2xl font-bold {{ 'text-green-600' if win_rate >= 60 else 'text-red-600' if win_rate < 40 else 'text-yellow-600' }}">
{{ "%.1f"|format(win_rate) }}%
</div>
<div class="text-sm text-gray-500 mt-1">Win Rate</div>
<div class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.win_rate">Win Rate</div>
</div>
<div class="perf-stat">
@@ -185,7 +186,7 @@
{% else %}
<div class="text-2xl font-bold text-gray-400">-</div>
{% endif %}
<div class="text-sm text-gray-500 mt-1">Emotional State</div>
<div class="text-sm text-gray-500 mt-1" data-i18n="ai_mentor.emotional_state">Emotional State</div>
</div>
</div>
@@ -193,7 +194,7 @@
<div class="section-grid">
<!-- Trading Summary -->
<div class="session-card">
<h3 class="text-xl font-semibold text-gray-800 mb-4">📊 Ringkasan Trading</h3>
<h3 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ai_mentor.trading_summary">📊 Ringkasan Trading</h3>
{% if session_data.trades %}
<div class="space-y-2">
@@ -219,23 +220,23 @@
{% else %}
<div class="text-center text-gray-500 py-8">
<i class="fas fa-chart-line text-4xl mb-4 opacity-50"></i>
<p>Tidak ada trade yang tercatat untuk sesi ini</p>
<p data-i18n="ai_mentor.no_trades_recorded">Tidak ada trade yang tercatat untuk sesi ini</p>
</div>
{% endif %}
</div>
<!-- Market Context -->
<div class="session-card">
<h3 class="text-xl font-semibold text-gray-800 mb-4">🌐 Konteks Pasar</h3>
<h3 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ai_mentor.market_context">🌐 Konteks Pasar</h3>
<div class="market-context">
<h4 class="font-semibold mb-2">Kondisi Pasar</h4>
<h4 class="font-semibold mb-2" data-i18n="ai_mentor.market_conditions">Kondisi Pasar</h4>
<p>{{ session_data.market_conditions|title if session_data.market_conditions else 'Normal' }}</p>
</div>
{% if session_data.personal_notes %}
<div class="mt-4">
<h4 class="font-semibold text-gray-700 mb-2">📝 Catatan Personal</h4>
<h4 class="font-semibold text-gray-700 mb-2" data-i18n="ai_mentor.personal_notes">📝 Catatan Personal</h4>
<div class="bg-gray-50 p-3 rounded-lg">
<p class="text-gray-700">{{ session_data.personal_notes }}</p>
</div>
@@ -247,7 +248,7 @@
<!-- AI Analysis Sections -->
{% if ai_report %}
<div class="ai-section">
<h3 class="text-xl font-semibold mb-4">🤖 Laporan AI Mentor</h3>
<h3 class="text-xl font-semibold mb-4" data-i18n="ai_mentor.ai_mentor_report">🤖 Laporan AI Mentor</h3>
<div class="prose max-w-none">
{{ ai_report|safe }}
</div>
@@ -258,15 +259,15 @@
<!-- Emotional Analysis -->
{% if analysis.emosi_vs_performa %}
<div class="ai-section">
<h3 class="text-xl font-semibold mb-4">💭 Analisis Emosi vs Performa</h3>
<h3 class="text-xl font-semibold mb-4" data-i18n="ai_mentor.emotional_analysis_vs_performance">💭 Analisis Emosi vs Performa</h3>
<div class="emotion-analysis">
<h4 class="font-semibold mb-2">Status Emosi: {{ session_data.emotions|title if session_data.emotions else 'Tidak Tercatat' }}</h4>
<h4 class="font-semibold mb-2" data-i18n="ai_mentor.emotional_status">Status Emosi: {{ session_data.emotions|title if session_data.emotions else 'Tidak Tercatat' }}</h4>
<p>{{ analysis.emosi_vs_performa.feedback if analysis.emosi_vs_performa.feedback else 'Analisis emosi tidak tersedia.' }}</p>
</div>
{% if analysis.emosi_vs_performa.saran %}
<div class="mt-4">
<h4 class="font-semibold text-gray-700 mb-2">💡 Saran Emosional</h4>
<h4 class="font-semibold text-gray-700 mb-2" data-i18n="ai_mentor.emotional_advice">💡 Saran Emosional</h4>
<ul class="recommendation-list">
{% if analysis.emosi_vs_performa.saran is string %}
<li>{{ analysis.emosi_vs_performa.saran }}</li>
@@ -284,7 +285,7 @@
<!-- Recommendations -->
{% if analysis.rekomendasi %}
<div class="ai-section">
<h3 class="text-xl font-semibold mb-4">🎯 Rekomendasi AI</h3>
<h3 class="text-xl font-semibold mb-4" data-i18n="ai_mentor.ai_recommendations">🎯 Rekomendasi AI</h3>
<ul class="recommendation-list">
{% if analysis.rekomendasi is string %}
<li>{{ analysis.rekomendasi }}</li>
@@ -300,7 +301,7 @@
<!-- Motivation -->
{% if analysis.motivasi %}
<div class="ai-section">
<h3 class="text-xl font-semibold mb-4">🚀 Motivasi & Inspirasi</h3>
<h3 class="text-xl font-semibold mb-4" data-i18n="ai_mentor.motivation_inspiration">🚀 Motivasi & Inspirasi</h3>
<div class="ai-insight">
<p class="text-lg font-medium text-blue-800">{{ analysis.motivasi }}</p>
</div>
@@ -310,7 +311,7 @@
<!-- Learning Points -->
{% if analysis.pembelajaran %}
<div class="ai-section">
<h3 class="text-xl font-semibold mb-4">📚 Poin Pembelajaran</h3>
<h3 class="text-xl font-semibold mb-4" data-i18n="ai_mentor.learning_points">📚 Poin Pembelajaran</h3>
<div class="bg-green-50 border border-green-200 rounded-lg p-4">
{% if analysis.pembelajaran is string %}
<p class="text-green-800">{{ analysis.pembelajaran }}</p>
@@ -329,13 +330,13 @@
<!-- Action Buttons -->
<div class="flex justify-center space-x-4 mt-8">
<a href="/ai-mentor/history" class="bg-gray-600 text-white px-6 py-3 rounded-lg hover:bg-gray-700 transition-colors">
← Kembali ke Riwayat
<span data-i18n="ai_mentor.back_to_history_button">← Kembali ke Riwayat</span>
</a>
<a href="/ai-mentor" class="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors">
Dashboard AI Mentor
<span data-i18n="ai_mentor.ai_mentor_dashboard">Dashboard AI Mentor</span>
</a>
<button onclick="generateNewAnalysis()" class="bg-purple-600 text-white px-6 py-3 rounded-lg hover:bg-purple-700 transition-colors">
🔄 Analisis Ulang
<span data-i18n="ai_mentor.reanalyze">🔄 Analisis Ulang</span>
</button>
</div>
</div>
+58 -57
View File
@@ -1,6 +1,7 @@
{% extends "base.html" %}
{% block title %}⚙️ Pengaturan AI Mentor - QuantumBotX{% endblock %}
{% block title_key %}ai_mentor.settings_title{% endblock %}
{% block title %}⚙️ AI Mentor Settings{% endblock %}
{% block head_extra %}
<style>
@@ -171,23 +172,23 @@ input:checked + .slider:before {
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<div class="mentor-card">
<h1 class="text-3xl font-bold mb-2">⚙️ Pengaturan AI Mentor</h1>
<p class="text-blue-100 text-lg">Kustomisasi pengalaman AI Trading Mentor sesuai preferensi Anda</p>
<h1 class="text-3xl font-bold mb-2" data-i18n="ai_mentor.settings_title">⚙️ Pengaturan AI Mentor</h1>
<p class="text-blue-100 text-lg" data-i18n="ai_mentor.settings_subtitle">Kustomisasi pengalaman AI Trading Mentor sesuai preferensi Anda</p>
<div class="mt-4 flex items-center space-x-4">
<span class="text-blue-200">🇮🇩 Indonesia Setup</span>
<span class="text-blue-200">• ⚡ Real-time Config</span>
<span class="text-blue-200">• 🎯 Personal</span>
<span class="text-blue-200" data-i18n="ai_mentor.indonesia_setup">🇮🇩 Indonesia Setup</span>
<span class="text-blue-200" data-i18n="ai_mentor.realtime_config">• ⚡ Real-time Config</span>
<span class="text-blue-200" data-i18n="ai_mentor.personal">• 🎯 Personal</span>
</div>
</div>
<!-- Language & Regional Settings -->
<div class="settings-section">
<h3 class="text-xl font-semibold text-gray-800 mb-4">🌏 Bahasa & Regional</h3>
<h3 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ai_mentor.language_regional">🌏 Bahasa & Regional</h3>
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Bahasa Interface</h4>
<p class="text-sm text-gray-500">Pilih bahasa untuk AI Mentor</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.interface_language">Bahasa Interface</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.language_choice">Pilih bahasa untuk AI Mentor</p>
</div>
<div class="language-selector">
<div class="language-option active" data-lang="id">🇮🇩 Indonesia</div>
@@ -197,18 +198,18 @@ input:checked + .slider:before {
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Zona Waktu</h4>
<p class="text-sm text-gray-500">Zona waktu untuk analisis trading</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.timezone">Zona Waktu</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.timezone_description">Zona waktu untuk analisis trading</p>
</div>
<div class="timezone-display">
<div class="timezone-display" data-i18n="ai_mentor.timezone_wib">
WIB (UTC+7) - Jakarta Time
</div>
</div>
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Format Mata Uang</h4>
<p class="text-sm text-gray-500">Format tampilan profit/loss</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.currency_format">Format Mata Uang</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.currency_display">Format tampilan profit/loss</p>
</div>
<select class="time-input" style="width: 150px;">
<option value="USD">USD ($)</option>
@@ -220,12 +221,12 @@ input:checked + .slider:before {
<!-- AI Behavior Settings -->
<div class="settings-section">
<h3 class="text-xl font-semibold text-gray-800 mb-4">🧠 Perilaku AI Mentor</h3>
<h3 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ai_mentor.ai_behavior">🧠 Perilaku AI Mentor</h3>
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Analisis Otomatis</h4>
<p class="text-sm text-gray-500">AI menganalisis setiap sesi trading secara otomatis</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.automatic_analysis">Analisis Otomatis</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.automatic_analysis_description">AI menganalisis setiap sesi trading secara otomatis</p>
</div>
<label class="setting-toggle">
<input type="checkbox" checked>
@@ -235,8 +236,8 @@ input:checked + .slider:before {
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Feedback Emosional</h4>
<p class="text-sm text-gray-500">AI memberikan feedback berdasarkan kondisi emosi</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.emotional_feedback">Feedback Emosional</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.emotional_feedback_description">AI memberikan feedback berdasarkan kondisi emosi</p>
</div>
<label class="setting-toggle">
<input type="checkbox" checked>
@@ -246,8 +247,8 @@ input:checked + .slider:before {
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Motivasi Harian</h4>
<p class="text-sm text-gray-500">Terima pesan motivasi setiap hari</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.daily_motivation">Motivasi Harian</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.daily_motivation_description">Terima pesan motivasi setiap hari</p>
</div>
<label class="setting-toggle">
<input type="checkbox" checked>
@@ -257,36 +258,36 @@ input:checked + .slider:before {
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Level Detail Analisis</h4>
<p class="text-sm text-gray-500">Tingkat detail feedback AI</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.analysis_detail_level">Level Detail Analisis</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.analysis_detail_description">Tingkat detail feedback AI</p>
</div>
<select class="time-input" style="width: 150px;">
<option value="basic">Basic</option>
<option value="detailed" selected>Detailed</option>
<option value="expert">Expert</option>
<option value="basic" data-i18n="ai_mentor.basic">Basic</option>
<option value="detailed" selected data-i18n="ai_mentor.detailed">Detailed</option>
<option value="expert" data-i18n="ai_mentor.expert">Expert</option>
</select>
</div>
</div>
<!-- Notification Settings -->
<div class="settings-section">
<h3 class="text-xl font-semibold text-gray-800 mb-4">🔔 Notifikasi</h3>
<h3 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ai_mentor.notifications">🔔 Notifikasi</h3>
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Laporan Harian</h4>
<p class="text-sm text-gray-500">Waktu pengiriman laporan AI harian</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.daily_report">Laporan Harian</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.daily_report_time">Waktu pengiriman laporan AI harian</p>
</div>
<div class="notification-time">
<input type="time" class="time-input" value="20:00">
<span class="text-sm text-gray-500">WIB</span>
<span class="text-sm text-gray-500" data-i18n="ai_mentor.timezone_wib_short">WIB</span>
</div>
</div>
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Reminder Trading</h4>
<p class="text-sm text-gray-500">Pengingat untuk input emosi dan catatan</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.trading_reminder">Reminder Trading</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.trading_reminder_description">Pengingat untuk input emosi dan catatan</p>
</div>
<label class="setting-toggle">
<input type="checkbox" checked>
@@ -296,8 +297,8 @@ input:checked + .slider:before {
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Alert Overtrading</h4>
<p class="text-sm text-gray-500">Peringatan jika terlalu banyak trading</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.overtrading_alert">Alert Overtrading</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.overtrading_alert_description">Peringatan jika terlalu banyak trading</p>
</div>
<label class="setting-toggle">
<input type="checkbox">
@@ -308,12 +309,12 @@ input:checked + .slider:before {
<!-- Cultural Preferences -->
<div class="settings-section">
<h3 class="text-xl font-semibold text-gray-800 mb-4">🎭 Preferensi Budaya</h3>
<h3 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ai_mentor.cultural_preferences">🎭 Preferensi Budaya</h3>
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Mode Liburan Otomatis</h4>
<p class="text-sm text-gray-500">Aktifkan mode Ramadan dan Natal secara otomatis</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.automatic_holiday_mode">Mode Liburan Otomatis</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.automatic_holiday_mode_description">Aktifkan mode Ramadan dan Natal secara otomatis</p>
</div>
<label class="setting-toggle">
<input type="checkbox" checked>
@@ -323,8 +324,8 @@ input:checked + .slider:before {
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Penyesuaian Risk Ramadan</h4>
<p class="text-sm text-gray-500">Kurangi risk otomatis selama bulan Ramadan</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.ramadan_risk_adjustment">Penyesuaian Risk Ramadan</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.ramadan_risk_adjustment_description">Kurangi risk otomatis selama bulan Ramadan</p>
</div>
<label class="setting-toggle">
<input type="checkbox" checked>
@@ -334,8 +335,8 @@ input:checked + .slider:before {
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Pause Trading Sahur/Iftar</h4>
<p class="text-sm text-gray-500">Hentikan trading otomatis saat waktu sahur dan iftar</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.sahur_iftar_pause">Pause Trading Sahur/Iftar</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.sahur_iftar_pause_description">Hentikan trading otomatis saat waktu sahur dan iftar</p>
</div>
<label class="setting-toggle">
<input type="checkbox">
@@ -346,47 +347,47 @@ input:checked + .slider:before {
<!-- Data & Privacy -->
<div class="settings-section">
<h3 class="text-xl font-semibold text-gray-800 mb-4">🔒 Data & Privacy</h3>
<h3 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ai_mentor.data_privacy">🔒 Data & Privacy</h3>
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Simpan Riwayat AI</h4>
<p class="text-sm text-gray-500">Berapa lama menyimpan analisis AI</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.save_ai_history">Simpan Riwayat AI</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.save_ai_history_description">Berapa lama menyimpan analisis AI</p>
</div>
<select class="time-input" style="width: 150px;">
<option value="30">30 Hari</option>
<option value="90" selected>90 Hari</option>
<option value="365">1 Tahun</option>
<option value="unlimited">Unlimited</option>
<option value="30" data-i18n="ai_mentor.30_days">30 Hari</option>
<option value="90" selected data-i18n="ai_mentor.90_days">90 Hari</option>
<option value="365" data-i18n="ai_mentor.1_year">1 Tahun</option>
<option value="unlimited" data-i18n="ai_mentor.unlimited">Unlimited</option>
</select>
</div>
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Export Data</h4>
<p class="text-sm text-gray-500">Download semua data AI Mentor Anda</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.export_data">Export Data</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.export_data_description">Download semua data AI Mentor Anda</p>
</div>
<button class="save-btn" onclick="exportData()">📥 Export</button>
<button class="save-btn" onclick="exportData()" data-i18n="ai_mentor.export">📥 Export</button>
</div>
<div class="setting-item">
<div>
<h4 class="font-medium text-gray-700">Reset Semua Data</h4>
<p class="text-sm text-gray-500">⚠️ Hapus semua riwayat dan analisis AI</p>
<h4 class="font-medium text-gray-700" data-i18n="ai_mentor.reset_all_data">Reset Semua Data</h4>
<p class="text-sm text-gray-500" data-i18n="ai_mentor.reset_all_data_description">⚠️ Hapus semua riwayat dan analisis AI</p>
</div>
<button class="danger-btn" onclick="resetAllData()">🗑️ Reset</button>
<button class="danger-btn" onclick="resetAllData()" data-i18n="ai_mentor.reset">🗑️ Reset</button>
</div>
</div>
<!-- Action Buttons -->
<div class="flex justify-center space-x-4 mt-8">
<button class="save-btn" onclick="saveSettings()">
<button class="save-btn" onclick="saveSettings()" data-i18n="ai_mentor.save_settings">
💾 Simpan Pengaturan
</button>
<button class="reset-btn" onclick="resetToDefaults()">
<button class="reset-btn" onclick="resetToDefaults()" data-i18n="ai_mentor.reset_to_defaults">
🔄 Reset ke Default
</button>
<a href="/ai-mentor" class="save-btn" style="text-decoration: none; display: inline-block;">
<a href="/ai-mentor" class="save-btn" style="text-decoration: none; display: inline-block;" data-i18n="ai_mentor.back_to_dashboard">
← Kembali ke Dashboard
</a>
</div>
+2 -1
View File
@@ -1,5 +1,6 @@
{% extends "base.html" %}
{% block title %}Riwayat Backtest{% endblock %}
{% block title_key %}backtest.history_title{% endblock %}
{% block title %}Backtest History{% endblock %}
{% block content %}
<h2 class="text-2xl font-bold text-gray-800 mb-4">Riwayat Hasil Backtest</h2>
+22 -23
View File
@@ -1,17 +1,15 @@
<!-- templates/backtesting.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title %}Backtester{% endblock %}
{% block title_key %}nav.backtest{% endblock %}
{% block title %}Strategy Backtester{% endblock %}
{% block content %}
<div class="flex justify-between items-center mb-4">
<h2 class="text-2xl font-bold text-gray-800">Strategy Backtester</h2>
<h2 class="text-2xl font-bold text-gray-800" data-i18n="nav.backtest">Strategy Backtester</h2>
<div class="flex space-x-2">
<button id="download-data-btn" class="bg-green-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-green-700 transition">
<i class="fas fa-download mr-2"></i>Download Data MT5
</button>
<a href="{{ url_for('backtest_history_page') }}" class="bg-gray-200 text-gray-800 py-2 px-4 rounded-lg font-medium hover:bg-gray-300">
<i class="fas fa-history mr-2"></i>Lihat Riwayat
<i class="fas fa-history mr-2"></i><span data-i18n="action.view">Lihat Riwayat</span>
</a>
</div>
</div>
@@ -23,14 +21,14 @@
<i class="fas fa-rocket text-blue-600 text-xl"></i>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-blue-800">🚀 Enhanced Backtesting Engine Active</h3>
<h3 class="text-sm font-medium text-blue-800" data-i18n="backtest.enhanced_engine_active">🚀 Enhanced Backtesting Engine Active</h3>
<div class="mt-1 text-sm text-blue-700">
<p>Your backtesting now includes:</p>
<p data-i18n="backtest.includes">Your backtesting now includes:</p>
<ul class="mt-1 list-disc list-inside text-xs space-y-1">
<li><strong>Realistic spread costs</strong> - Actual trading costs deducted</li>
<li><strong>ATR-based risk management</strong> - Dynamic position sizing</li>
<li><strong>Instrument protection</strong> - Gold trading safeguards</li>
<li><strong>Slippage simulation</strong> - More accurate execution modeling</li>
<li><strong data-i18n="backtest.realistic_spread">Realistic spread costs</strong> - <span data-i18n="backtest.actual_costs">Actual trading costs deducted</span></li>
<li><strong data-i18n="backtest.atr_risk">ATR-based risk management</strong> - <span data-i18n="backtest.dynamic_sizing">Dynamic position sizing</span></li>
<li><strong data-i18n="backtest.instrument_protection">Instrument protection</strong> - <span data-i18n="backtest.gold_safeguards">Gold trading safeguards</span></li>
<li><strong data-i18n="backtest.slippage">Slippage simulation</strong> - <span data-i18n="backtest.accurate_modeling">More accurate execution modeling</span></li>
</ul>
</div>
</div>
@@ -43,11 +41,11 @@
<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>
<label for="strategy-select" class="block text-sm font-medium text-gray-700" data-i18n="label.strategy">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>
<label for="data-file" class="block text-sm font-medium text-gray-700" data-i18n="backtest.upload_data">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>
@@ -55,28 +53,29 @@
<div class="grid grid-cols-2 gap-4 pt-4 border-t">
<div>
<label for="sl_atr_multiplier" class="block text-sm font-medium text-gray-700">
SL (ATR Multiplier)
<span class="text-xs text-blue-600">🚀 Enhanced</span>
<span data-i18n="label.sl_atr">SL (ATR Multiplier)</span>
<span class="text-xs text-blue-600" data-i18n="backtest.enhanced">🚀 Enhanced</span>
</label>
<input type="number" name="sl_atr_multiplier" id="sl_atr_multiplier" value="2.0" step="0.1" min="0.5" max="5.0"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
<p class="text-xs text-gray-500 mt-1">Dynamic SL based on volatility</p>
<p class="text-xs text-gray-500 mt-1" data-i18n="backtest.dynamic_sl">Dynamic SL based on volatility</p>
</div>
<div>
<label for="tp_atr_multiplier" class="block text-sm font-medium text-gray-700">
TP (ATR Multiplier)
<span class="text-xs text-blue-600">🚀 Enhanced</span>
<span data-i18n="label.tp_atr">TP (ATR Multiplier)</span>
<span class="text-xs text-blue-600" data-i18n="backtest.enhanced">🚀 Enhanced</span>
</label>
<input type="number" name="tp_atr_multiplier" id="tp_atr_multiplier" value="4.0" step="0.1" min="1.0" max="10.0"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
<p class="text-xs text-gray-500 mt-1">Dynamic TP based on volatility</p>
<p class="text-xs text-gray-500 mt-1" data-i18n="backtest.dynamic_tp">Dynamic TP based on volatility</p>
</div>
</div>
<div id="params-container" class="space-y-4 pt-4 border-t">
<!-- Parameter akan dimuat di sini -->
<span data-i18n="bots.strategy_params_loaded">Parameter strategi akan dimuat di sini</span>
</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">
<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" data-i18n="backtest.run">
Jalankan Backtest
</button>
</div>
@@ -85,7 +84,7 @@
<!-- 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>
<h3 class="text-xl font-bold mb-4" data-i18n="backtest.results">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>
@@ -94,7 +93,7 @@
</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>
<p class="ml-4 text-gray-600" data-i18n="backtest.running">Menjalankan simulasi...</p>
</div>
</div>
{% endblock %}
+6 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Dashboard{% endblock %} - QuantumBotX</title>
<title data-i18n-key="{% block title_key %}dashboard.title{% endblock %}">{% 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>
@@ -70,6 +70,11 @@
</div>
</div>
<div class="flex items-center space-x-4">
<!-- Global Language Switcher -->
<div class="flex items-center space-x-1 bg-white rounded-lg shadow-sm border px-2 py-1">
<button class="lang-btn px-2 py-1 text-xs rounded font-medium hover:bg-gray-100 transition-colors" data-lang="id">ID</button>
<button class="lang-btn px-2 py-1 text-xs rounded font-medium hover:bg-gray-100 transition-colors" data-lang="en">EN</button>
</div>
<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>
+1
View File
@@ -1,6 +1,7 @@
<!-- templates/bot_detail.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title_key %}bots.detail_title{% endblock %}
{% block title %}Detail Bot{% endblock %}
{% block content %}
+10 -9
View File
@@ -1,13 +1,14 @@
<!-- templates/forex.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title_key %}market.forex_title{% endblock %}
{% 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>
<h2 class="text-2xl font-bold text-gray-800" data-i18n="market.forex_title">Pasar Forex</h2>
<p class="text-gray-600" data-i18n="market.forex_description">Data harga real-time dari pasar valuta asing.</p>
</div>
</div>
@@ -15,15 +16,15 @@
<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>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="market.pair">Pasangan</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="market.bid_price">Harga Bid</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="market.ask_price">Harga Ask</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="market.spread">Spread</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="action.view">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>
<tr><td colspan="5" class="p-4 text-center text-gray-500" data-i18n="market.loading">Memuat data forex...</td></tr>
</tbody>
</table>
</div>
@@ -38,7 +39,7 @@
</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
<span data-i18n="market.close">Close</span>
</button>
</div>
</div>
+11 -10
View File
@@ -1,13 +1,14 @@
<!-- templates/history.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title %}Riwayat Transaksi{% endblock %}
{% block title_key %}history.title{% endblock %}
{% block title %}Riwayat Transaksi Global{% 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>
<h2 class="text-2xl font-bold text-gray-800" data-i18n="history.title">Riwayat Transaksi Global</h2>
<p class="text-gray-600" data-i18n="history.description">Semua transaksi yang telah ditutup dari akun MetaTrader 5.</p>
</div>
</div>
@@ -15,17 +16,17 @@
<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 cursor-pointer hover:bg-gray-100" onclick="sortTable('symbol')">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 cursor-pointer hover:bg-gray-100" onclick="sortTable('volume')">Volume</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortTable('profit')">Profit <span id="profit-sort-icon"></span></th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortTable('time')">Waktu Penutupan <span id="time-sort-icon"></span></th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortTable('magic')">Magic Number</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortTable('symbol')"><span data-i18n="history.symbol">Simbol</span> <span id="symbol-sort-icon"></span></th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortTable('type')"><span data-i18n="history.type">Tipe</span> <span id="type-sort-icon"></span></th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortTable('volume')"><span data-i18n="history.volume">Volume</span> <span id="volume-sort-icon"></span></th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortTable('profit')"><span data-i18n="history.profit">Profit</span> <span id="profit-sort-icon"></span></th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortTable('time')"><span data-i18n="history.close_time">Waktu Penutupan</span> <span id="time-sort-icon"></span></th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortTable('magic')"><span data-i18n="history.magic">Magic Number</span> <span id="magic-sort-icon"></span></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>
<tr><td colspan="6" class="p-4 text-center text-gray-500" data-i18n="history.loading">Memuat riwayat...</td></tr>
</tbody>
</table>
</div>
+79 -73
View File
@@ -107,13 +107,13 @@
<div class="mb-6">
<div class="flex justify-between items-start">
<div>
<h2 class="text-3xl font-bold text-gray-800">Dashboard Trading</h2>
<p class="text-gray-600">Selamat datang kembali! Berikut ringkasan trading Anda hari ini.</p>
<h2 class="text-3xl font-bold text-gray-800" data-i18n="dashboard.title">Dashboard Trading</h2>
<p class="text-gray-600" data-i18n="dashboard.welcome_back">Selamat datang kembali! Berikut ringkasan trading Anda hari ini.</p>
</div>
<div class="ai-mentor-widget p-4 text-right">
<div class="text-sm opacity-90">🤖 AI Mentor</div>
<div class="text-xl font-bold" id="ai-mentor-status">Siap Membantu</div>
<a href="/ai-mentor" class="text-sm underline opacity-90 hover:opacity-100">Lihat Analisis →</a>
<div class="text-sm opacity-90" data-i18n="dashboard.ai_mentor">🤖 AI Mentor</div>
<div class="text-xl font-bold" id="ai-mentor-status" data-i18n="dashboard.ai_mentor_ready">Siap Membantu</div>
<a href="/ai-mentor" class="text-sm underline opacity-90 hover:opacity-100" data-i18n="dashboard.view_analysis">Lihat Analisis →</a>
</div>
</div>
</div>
@@ -123,7 +123,7 @@
<div class="stat-card bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<h3 class="text-sm font-medium text-gray-500">Total Saldo (Equity)</h3>
<h3 class="text-sm font-medium text-gray-500" data-i18n="dashboard.total_equity">Total Saldo (Equity)</h3>
<p id="total-equity" class="mt-1 text-2xl font-semibold text-gray-800">
<i class="fas fa-spinner fa-spin text-gray-400"></i>
</p>
@@ -137,7 +137,7 @@
<div class="stat-card bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<h3 class="text-sm font-medium text-gray-500">Profit Hari Ini</h3>
<h3 class="text-sm font-medium text-gray-500" data-i18n="dashboard.today_profit">Profit Hari Ini</h3>
<p id="todays-profit" class="mt-1 text-2xl font-semibold text-gray-800">
<i class="fas fa-spinner fa-spin text-gray-400"></i>
</p>
@@ -151,7 +151,7 @@
<div class="stat-card bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<h3 class="text-sm font-medium text-gray-500">Bot Aktif</h3>
<h3 class="text-sm font-medium text-gray-500" data-i18n="dashboard.active_bots">Bot Aktif</h3>
<p id="active-bots-count" class="mt-1 text-2xl font-semibold text-gray-800">
<i class="fas fa-spinner fa-spin text-gray-400"></i>
</p>
@@ -165,7 +165,7 @@
<div class="stat-card bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between">
<div>
<h3 class="text-sm font-medium text-gray-500">Total Bot</h3>
<h3 class="text-sm font-medium text-gray-500" data-i18n="dashboard.total_bots">Total Bot</h3>
<p id="total-bots-count" class="mt-1 text-2xl font-semibold text-gray-800">
<i class="fas fa-spinner fa-spin text-gray-400"></i>
</p>
@@ -179,9 +179,9 @@
<!-- New AI Mentor Quick Stats -->
<div class="stat-card bg-gradient-to-br from-purple-500 to-blue-600 text-white rounded-lg shadow p-6">
<div class="text-center">
<h3 class="text-sm font-medium opacity-90">Status Emosi</h3>
<p id="emotion-status" class="mt-1 text-xl font-semibold">😌 Tenang</p>
<button onclick="quickEmotionCheck()" class="text-xs underline opacity-90 hover:opacity-100 mt-1">
<h3 class="text-sm font-medium opacity-90" data-i18n="dashboard.emotion_status">Status Emosi</h3>
<p id="emotion-status" class="mt-1 text-xl font-semibold" data-i18n="dashboard.emotion_calm">😌 Tenang</p>
<button onclick="quickEmotionCheck()" class="text-xs underline opacity-90 hover:opacity-100 mt-1" data-i18n="dashboard.update_status">
Update Status
</button>
</div>
@@ -195,9 +195,9 @@
<!-- Price Chart -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold text-gray-700">📈 Grafik Harga EUR/USD</h3>
<h3 class="text-lg font-semibold text-gray-700" data-i18n="dashboard.price_chart">📈 Grafik Harga EUR/USD</h3>
<div class="text-sm text-gray-500" id="last-updated">
<i class="fas fa-clock"></i> Memuat...
<i class="fas fa-clock"></i> <span data-i18n="dashboard.loading">Memuat...</span>
</div>
</div>
<div class="chart-container">
@@ -208,9 +208,9 @@
<!-- RSI Chart -->
<div class="bg-white rounded-lg shadow p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold text-gray-700">📊 RSI EUR/USD (H1)</h3>
<h3 class="text-lg font-semibold text-gray-700" data-i18n="dashboard.rsi_chart">📊 RSI EUR/USD (H1)</h3>
<div class="text-sm text-gray-500">
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800" data-i18n="dashboard.real_time">
Real-time
</span>
</div>
@@ -225,17 +225,17 @@
<div class="space-y-6">
<!-- AI Mentor Today -->
<div class="bg-gradient-to-br from-indigo-500 to-purple-600 text-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-3">🧠 AI Mentor Hari Ini</h3>
<h3 class="text-lg font-semibold mb-3" data-i18n="dashboard.ai_mentor_today">🧠 AI Mentor Hari Ini</h3>
<div id="ai-mentor-summary" class="space-y-3">
<div class="bg-white bg-opacity-20 rounded-lg p-3">
<div class="text-sm opacity-90">Analisis Trading:</div>
<div class="font-semibold" id="trading-analysis">Memuat...</div>
<div class="text-sm opacity-90" data-i18n="dashboard.trading_analysis">Analisis Trading:</div>
<div class="font-semibold" id="trading-analysis" data-i18n="dashboard.loading">Memuat...</div>
</div>
<div class="bg-white bg-opacity-20 rounded-lg p-3">
<div class="text-sm opacity-90">Tips Hari Ini:</div>
<div class="text-sm" id="daily-tip">Memuat...</div>
<div class="text-sm opacity-90" data-i18n="dashboard.daily_tip">Tips Hari Ini:</div>
<div class="text-sm" id="daily-tip" data-i18n="dashboard.loading">Memuat...</div>
</div>
<a href="/ai-mentor/today-report" class="block w-full bg-white bg-opacity-20 hover:bg-opacity-30 rounded-lg p-2 text-center text-sm font-medium transition">
<a href="/ai-mentor/today-report" class="block w-full bg-white bg-opacity-20 hover:bg-opacity-30 rounded-lg p-2 text-center text-sm font-medium transition" data-i18n="dashboard.view_full_report">
📊 Lihat Laporan Lengkap
</a>
</div>
@@ -244,10 +244,10 @@
<!-- Recent Activities -->
<div class="bg-white rounded-lg shadow">
<div class="p-4 border-b">
<h3 class="text-lg font-semibold text-gray-800">⚡ Aktivitas Terbaru</h3>
<h3 class="text-lg font-semibold text-gray-800" data-i18n="dashboard.recent_activities">⚡ Aktivitas Terbaru</h3>
</div>
<div id="recent-activities" class="max-h-80 overflow-y-auto">
<div class="p-4 text-gray-500 text-center">Memuat aktivitas...</div>
<div class="p-4 text-gray-500 text-center" data-i18n="dashboard.loading_activities">Memuat aktivitas...</div>
</div>
</div>
</div>
@@ -256,18 +256,18 @@
<!-- 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>
<h3 class="text-lg font-semibold text-gray-800" data-i18n="dashboard.active_trading_bots">Active Trading Bots</h3>
</div>
<div id="active-bots-list" class="divide-y">
<p class="p-4 text-gray-500">Memuat daftar bot...</p>
<p class="p-4 text-gray-500" data-i18n="dashboard.loading_bot_list">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>
<a href="/trading_bots" class="block w-full text-center text-blue-600 font-medium hover:text-blue-800 transition" data-i18n="dashboard.view_all_bots">View All Bots</a>
</div>
</div>
<!-- Floating AI Button -->
<button class="floating-ai-btn" onclick="window.location.href='/ai-mentor'" title="Chat dengan AI Mentor">
<button class="floating-ai-btn" onclick="window.location.href='/ai-mentor'" data-i18n-title="dashboard.chat_with_ai_mentor" title="Chat dengan AI Mentor">
🤖
</button>
{% endblock %}
@@ -453,7 +453,7 @@ function loadDashboardData() {
</div>
`).join('');
} else {
botsList.innerHTML = '<p class="p-4 text-gray-500">Tidak ada bot yang aktif</p>';
botsList.innerHTML = '<p class="p-4 text-gray-500" data-i18n="dashboard.no_active_bots">Tidak ada bot yang aktif</p>';
}
}
})
@@ -503,38 +503,38 @@ function loadAIMentorSummary() {
if (data.success) {
// Update emotion status
const emotionMap = {
'tenang': '😌 Tenang',
'serakah': '🤑 Serakah',
'takut': '😰 Takut',
'frustasi': '😤 Frustasi',
'netral': '😐 Netral'
'tenang': window.QuantumBotXI18n.t('dashboard.emotion_calm'),
'serakah': window.QuantumBotXI18n.t('dashboard.emotion_greedy'),
'takut': window.QuantumBotXI18n.t('dashboard.emotion_fear'),
'frustasi': window.QuantumBotXI18n.t('dashboard.emotion_frustrated'),
'netral': window.QuantumBotXI18n.t('dashboard.emotion_neutral')
};
document.getElementById('emotion-status').textContent =
emotionMap[data.today_emotions] || '😐 Netral';
emotionMap[data.today_emotions] || window.QuantumBotXI18n.t('dashboard.emotion_neutral');
// Update AI analysis
document.getElementById('trading-analysis').textContent =
data.trading_analysis || 'Belum ada data hari ini';
data.trading_analysis || window.QuantumBotXI18n.t('msg.no_data');
// Update daily tip
document.getElementById('daily-tip').textContent =
data.daily_tip || 'Mulai trading untuk mendapat tips personal!';
data.daily_tip || window.QuantumBotXI18n.t('msg.no_data');
// Update AI mentor status
const statusElement = document.getElementById('ai-mentor-status');
if (data.today_has_data) {
statusElement.textContent = 'Aktif Menganalisis';
statusElement.textContent = window.QuantumBotXI18n.t('dashboard.ai_mentor_active');
statusElement.className = 'text-xl font-bold text-green-100';
} else {
statusElement.textContent = 'Siap Membantu';
statusElement.textContent = window.QuantumBotXI18n.t('dashboard.ai_mentor_ready');
statusElement.className = 'text-xl font-bold';
}
}
})
.catch(error => {
console.error('Error loading AI mentor summary:', error);
document.getElementById('trading-analysis').textContent = 'Error loading';
document.getElementById('daily-tip').textContent = 'Error loading';
document.getElementById('trading-analysis').textContent = window.QuantumBotXI18n.t('error.loading');
document.getElementById('daily-tip').textContent = window.QuantumBotXI18n.t('error.loading');
});
}
@@ -558,26 +558,32 @@ function loadRecentActivities() {
`).join('');
document.getElementById('recent-activities').innerHTML =
activitiesHtml || '<div class="p-4 text-gray-500 text-center">Belum ada aktivitas</div>';
activitiesHtml || '<div class="p-4 text-gray-500 text-center" data-i18n="msg.no_data">Belum ada aktivitas</div>';
}
})
.catch(error => {
console.error('Error loading recent activities:', error);
document.getElementById('recent-activities').innerHTML =
'<div class="p-4 text-red-500 text-center">Error loading activities</div>';
'<div class="p-4 text-red-500 text-center" data-i18n="error.loading">Error loading activities</div>';
});
}
// Quick Emotion Check
function quickEmotionCheck() {
const emotions = ['tenang', 'serakah', 'takut', 'frustasi', 'netral'];
const emotionNames = ['😌 Tenang', '🤑 Serakah', '😰 Takut', '😤 Frustasi', '😐 Netral'];
const emotionNames = [
window.QuantumBotXI18n.t('dashboard.emotion_calm'),
window.QuantumBotXI18n.t('dashboard.emotion_greedy'),
window.QuantumBotXI18n.t('dashboard.emotion_fear'),
window.QuantumBotXI18n.t('dashboard.emotion_frustrated'),
window.QuantumBotXI18n.t('dashboard.emotion_neutral')
];
const modal = document.createElement('div');
modal.className = 'fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center';
modal.innerHTML = `
<div class="bg-white rounded-lg p-6 max-w-sm w-full mx-4">
<h3 class="text-lg font-semibold mb-4">🎯 Update Status Emosi</h3>
<h3 class="text-lg font-semibold mb-4" data-i18n="dashboard.update_emotion_status">🎯 Update Status Emosi</h3>
<div class="space-y-2">
${emotions.map((emotion, index) => `
<button onclick="updateEmotion('${emotion}')"
@@ -587,7 +593,7 @@ function quickEmotionCheck() {
`).join('')}
</div>
<button onclick="closeEmotionModal()"
class="mt-4 w-full bg-gray-200 text-gray-800 py-2 rounded-lg hover:bg-gray-300 transition-colors">
class="mt-4 w-full bg-gray-200 text-gray-800 py-2 rounded-lg hover:bg-gray-300 transition-colors" data-i18n="dashboard.cancel">
Batal
</button>
</div>
@@ -611,26 +617,26 @@ function updateEmotion(emotion) {
if (data.success) {
// Update UI immediately
const emotionMap = {
'tenang': '😌 Tenang',
'serakah': '🤑 Serakah',
'takut': '😰 Takut',
'frustasi': '😤 Frustasi',
'netral': '😐 Netral'
'tenang': window.QuantumBotXI18n.t('dashboard.emotion_calm'),
'serakah': window.QuantumBotXI18n.t('dashboard.emotion_greedy'),
'takut': window.QuantumBotXI18n.t('dashboard.emotion_fear'),
'frustasi': window.QuantumBotXI18n.t('dashboard.emotion_frustrated'),
'netral': window.QuantumBotXI18n.t('dashboard.emotion_neutral')
};
document.getElementById('emotion-status').textContent = emotionMap[emotion];
// Show success message
showNotification('Status emosi berhasil diupdate!', 'success');
showNotification(window.QuantumBotXI18n.t('dashboard.emotion_updated'), 'success');
// Refresh AI mentor summary
loadAIMentorSummary();
} else {
showNotification('Gagal update status emosi', 'error');
showNotification(window.QuantumBotXI18n.t('dashboard.emotion_update_failed'), 'error');
}
})
.catch(error => {
console.error('Error updating emotion:', error);
showNotification('Error: Gagal update status emosi', 'error');
showNotification(window.QuantumBotXI18n.t('dashboard.error_updating_emotion'), 'error');
})
.finally(() => {
closeEmotionModal();
@@ -737,7 +743,7 @@ function updateRamadanWidgets(holidayData) {
ramadanHtml += `
<div class="holiday-widget ramadan-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">⏱️ Hitung Mundur Iftar</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.countdown.iftar">⏱️ Hitung Mundur Iftar</h3>
<i class="fas fa-moon"></i>
</div>
<div class="ramadan-countdown">
@@ -755,7 +761,7 @@ function updateRamadanWidgets(holidayData) {
ramadanHtml += `
<div class="holiday-widget ramadan-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">🧘‍♂️ Pengingat Kesabaran</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.patience_reminder">🧘‍♂️ Pengingat Kesabaran</h3>
<i class="fas fa-heart"></i>
</div>
<div class="text-sm italic text-center">
@@ -771,12 +777,12 @@ function updateRamadanWidgets(holidayData) {
ramadanHtml += `
<div class="holiday-widget ramadan-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">🛡️ Penyesuaian Risiko</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.risk_adjustment">🛡️ Penyesuaian Risiko</h3>
<i class="fas fa-shield-alt"></i>
</div>
<div class="text-center">
<div class="text-2xl font-bold">${riskReduction.toFixed(0)}%</div>
<div class="text-sm opacity-90">Pengurangan risiko otomatis</div>
<div class="text-sm opacity-90" data-i18n="holiday.risk_reduction">Pengurangan risiko otomatis</div>
</div>
</div>
`;
@@ -787,7 +793,7 @@ function updateRamadanWidgets(holidayData) {
ramadanHtml += `
<div class="holiday-widget ramadan-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">⏰ Waktu Trading Optimal</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.optimal_trading_hours">⏰ Waktu Trading Optimal</h3>
<i class="fas fa-clock"></i>
</div>
<div class="space-y-2">
@@ -822,14 +828,14 @@ function updateChristmasWidgets(holidayData) {
christmasHtml += `
<div class="holiday-widget christmas-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">🎄 Hitung Mundur Natal</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.countdown.christmas">🎄 Hitung Mundur Natal</h3>
<i class="fas fa-gift"></i>
</div>
<div class="ramadan-countdown">
${daysDiff}
</div>
<div class="text-center text-sm opacity-90 mt-2">
hari lagi sampai Natal
<span data-i18n="holiday.countdown.days_until">hari lagi sampai</span> Natal
</div>
</div>
`;
@@ -841,12 +847,12 @@ function updateChristmasWidgets(holidayData) {
christmasHtml += `
<div class="holiday-widget christmas-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">🛡️ Penyesuaian Risiko</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.risk_adjustment">🛡️ Penyesuaian Risiko</h3>
<i class="fas fa-shield-alt"></i>
</div>
<div class="text-center">
<div class="text-2xl font-bold">${riskReduction.toFixed(0)}%</div>
<div class="text-sm opacity-90">Pengurangan risiko otomatis</div>
<div class="text-sm opacity-90" data-i18n="holiday.risk_reduction">Pengurangan risiko otomatis</div>
</div>
</div>
`;
@@ -857,7 +863,7 @@ function updateChristmasWidgets(holidayData) {
christmasHtml += `
<div class="holiday-widget christmas-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">✨ Salam Natal</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.christmas_greeting">✨ Salam Natal</h3>
<i class="fas fa-star"></i>
</div>
<div class="text-sm text-center italic">
@@ -873,12 +879,12 @@ function updateChristmasWidgets(holidayData) {
christmasHtml += `
<div class="holiday-widget christmas-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">📏 Penyesuaian Lot</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.lot_adjustment">📏 Penyesuaian Lot</h3>
<i class="fas fa-ruler"></i>
</div>
<div class="text-center">
<div class="text-2xl font-bold">${lotSizeReduction.toFixed(0)}%</div>
<div class="text-sm opacity-90">Pengurangan ukuran lot</div>
<div class="text-sm opacity-90" data-i18n="holiday.lot_reduction">Pengurangan ukuran lot</div>
</div>
</div>
`;
@@ -903,14 +909,14 @@ function updateNewYearWidgets(holidayData) {
newYearHtml += `
<div class="holiday-widget new-year-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">🎆 Hitung Mundur Tahun Baru</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.countdown.new_year">🎆 Hitung Mundur Tahun Baru</h3>
<i class="fas fa-champagne-glasses"></i>
</div>
<div class="ramadan-countdown">
${daysDiff}
</div>
<div class="text-center text-sm opacity-90 mt-2">
hari lagi sampai Tahun Baru
<span data-i18n="holiday.countdown.days_until">hari lagi sampai</span> Tahun Baru
</div>
</div>
`;
@@ -921,12 +927,12 @@ function updateNewYearWidgets(holidayData) {
newYearHtml += `
<div class="holiday-widget new-year-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">🛡️ Penyesuaian Risiko</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.risk_adjustment">🛡️ Penyesuaian Risiko</h3>
<i class="fas fa-shield-alt"></i>
</div>
<div class="text-center">
<div class="text-2xl font-bold">${riskReduction.toFixed(0)}%</div>
<div class="text-sm opacity-90">Pengurangan risiko otomatis</div>
<div class="text-sm opacity-90" data-i18n="holiday.risk_reduction">Pengurangan risiko otomatis</div>
</div>
</div>
`;
@@ -936,10 +942,10 @@ function updateNewYearWidgets(holidayData) {
newYearHtml += `
<div class="holiday-widget new-year-widget p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">🎯 Resolusi Trading</h3>
<h3 class="text-lg font-semibold" data-i18n="holiday.new_year_resolution">🎯 Resolusi Trading</h3>
<i class="fas fa-bullseye"></i>
</div>
<div class="text-sm text-center">
<div class="text-sm text-center" data-i18n="holiday.new_year_goal_setting">
Waktu yang tepat untuk menetapkan tujuan trading tahun ini
</div>
</div>
@@ -955,7 +961,7 @@ document.addEventListener('DOMContentLoaded', function() {
const floatingBtn = document.createElement('button');
floatingBtn.className = 'floating-ai-btn';
floatingBtn.innerHTML = '🤖';
floatingBtn.title = 'Chat dengan AI Mentor';
floatingBtn.title = window.QuantumBotXI18n.t('dashboard.chat_with_ai_mentor');
floatingBtn.onclick = () => window.location.href = '/ai-mentor';
document.body.appendChild(floatingBtn);
}
+5 -3
View File
@@ -1,17 +1,19 @@
<!-- templates/notifications.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title_key %}nav.notifications{% endblock %}
{% block title %}Notifikasi{% endblock %}
{% block content %}
<h2 class="text-2xl font-bold text-gray-800 mb-6">Notifikasi</h2>
<h2 class="text-2xl font-bold text-gray-800 mb-6" data-i18n="nav.notifications">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>
<p class="p-6 text-center text-gray-500" data-i18n="notifications.loading">Memuat notifikasi...</p>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
{% endblock %}
{% endblock %}
+13 -12
View File
@@ -1,12 +1,13 @@
<!-- templates/portfolio.html -->
{% extends "base.html" %}
{% block title_key %}portfolio.title{% endblock %}
{% block title %}Portfolio Real-Time{% 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>
<h2 class="text-2xl font-bold text-gray-800" data-i18n="portfolio.title">Portfolio Real-Time</h2>
<p class="text-gray-600" data-i18n="portfolio.description">Posisi trading yang sedang terbuka di akun MetaTrader 5.</p>
</div>
<div id="portfolio-summary" class="text-right">
<!-- Summary akan dimuat di sini -->
@@ -17,13 +18,13 @@
<div class="grid grid-cols-1 lg:grid-cols-5 gap-6 mb-6">
<!-- Chart P/L (lebih lebar) -->
<div class="lg:col-span-3 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>
<h3 class="text-lg font-semibold text-gray-800 mb-2" data-i18n="portfolio.pnl_chart">Grafik Profit/Loss Terbuka (Real-Time)</h3>
<div style="height: 250px;"><canvas id="pnlChart"></canvas></div>
</div>
<!-- Chart Alokasi Aset (lebih kecil) -->
<div class="lg:col-span-2 bg-white rounded-lg shadow p-4">
<h3 class="text-lg font-semibold text-gray-800 mb-2">Alokasi Aset</h3>
<h3 class="text-lg font-semibold text-gray-800 mb-2" data-i18n="portfolio.allocation_chart">Alokasi Aset</h3>
<div style="height: 250px;"><canvas id="assetAllocationChart"></canvas></div>
</div>
</div>
@@ -32,16 +33,16 @@
<table class="min-w-full bg-white rounded-lg shadow overflow-hidden">
<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>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="portfolio.symbol">Simbol</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="portfolio.type">Tipe</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="portfolio.volume">Volume</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="portfolio.open_price">Harga Buka</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="portfolio.profit_loss">Profit/Loss</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="portfolio.magic">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>
<tr><td colspan="6" class="p-4 text-center text-gray-500" data-i18n="portfolio.loading">Memuat posisi terbuka...</td></tr>
</tbody>
</table>
@@ -52,4 +53,4 @@
<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="{{ url_for('static', filename='js/portfolio.js') }}"></script>
{% endblock %}
{% endblock %}
+22 -25
View File
@@ -1,43 +1,40 @@
<!-- templates/profile.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title_key %}profile.title{% endblock %}
{% block title %}Profil{% endblock %}
{% 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>
<h2 class="text-2xl font-bold text-gray-800 mb-6" data-i18n="profile.my_profile">Profil Saya</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-1">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex flex-col items-center">
<div class="w-24 h-24 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 text-4xl font-bold mb-4">RC</div>
<h3 id="profile-display-name" class="text-xl font-bold text-gray-800" data-i18n="profile.loading">Memuat...</h3>
<p id="profile-join-date" class="text-xs text-gray-400 mt-2" data-i18n="profile.joined_since">Bergabung sejak: Juli 2025</p>
</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>
<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 class="lg:col-span-2">
<div class="bg-white rounded-lg shadow">
<div class="p-6 border-b">
<h3 class="text-lg font-semibold text-gray-800" data-i18n="settings.profile">Profil</h3>
</div>
<form id="profile-form" class="p-6 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>
<label for="profile-name" class="text-sm font-medium text-gray-700" data-i18n="label.full_name">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 focus:outline-none focus:ring-blue-500 focus:border-blue-500" 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>
<label for="profile-email" class="text-sm font-medium text-gray-700" data-i18n="label.email">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 shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500" required readonly>
</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>
<label for="profile-password" class="text-sm font-medium text-gray-700" data-i18n="label.password">Ganti Password (kosongkan jika tidak ingin diubah)</label>
<input type="password" id="profile-password" data-i18n-placeholder="profile.password_placeholder" placeholder="••••••••" 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>
<button type="submit" id="save-profile-btn" class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700" data-i18n="msg.save_changes">Simpan Perubahan</button>
</form>
</div>
</div>
+39 -40
View File
@@ -1,17 +1,17 @@
{% extends "base.html" %}
{% block title %}Ramadan Trading Mode{% endblock %}
{% block title_key %}ramadan.page_title{% endblock %}
{% block title %}🌙 Ramadan Trading Mode{% endblock %}
{% block content %}
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<div class="mb-8 text-center">
<h1 class="text-3xl font-bold text-gray-800 mb-2">🌙 Ramadan Trading Mode</h1>
<p class="text-gray-600">Bulan suci dengan fitur trading yang menghormati ibadah puasa</p>
<h1 class="text-3xl font-bold text-gray-800 mb-2" data-i18n="ramadan.title">🌙 Ramadan Trading Mode</h1>
<p class="text-gray-600" data-i18n="ramadan.subtitle">Bulan suci dengan fitur trading yang menghormati ibadah puasa</p>
<div class="mt-4 p-4 bg-blue-50 rounded-lg border border-blue-200">
<p class="text-blue-800">
<i class="fas fa-info-circle"></i> Mode ini aktif secara otomatis selama bulan Ramadan dan akan
menyesuaikan pengaturan trading Anda untuk menghormati waktu ibadah. Anda tidak perlu mengaktifkannya secara manual.
<i class="fas fa-info-circle"></i> <span data-i18n="ramadan.info">Mode ini aktif secara otomatis selama bulan Ramadan dan akan menyesuaikan pengaturan trading Anda untuk menghormati waktu ibadah. Anda tidak perlu mengaktifkannya secara manual.</span>
</p>
</div>
</div>
@@ -19,23 +19,23 @@
<!-- Current Status Card -->
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-800">Status Ramadan</h2>
<span id="ramadan-status" class="px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800">
<h2 class="text-xl font-semibold text-gray-800" data-i18n="ramadan.status">Status Ramadan</h2>
<span id="ramadan-status" class="px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800" data-i18n="ramadan.loading">
Loading...
</span>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="border rounded-lg p-4">
<div class="text-sm text-gray-500 mb-1">Periode Ramadan</div>
<div id="ramadan-period" class="text-lg font-semibold text-blue-600">
<div class="text-sm text-gray-500 mb-1" data-i18n="ramadan.period">Periode Ramadan</div>
<div id="ramadan-period" class="text-lg font-semibold text-blue-600" data-i18n="ramadan.loading">
Loading...
</div>
</div>
<div class="border rounded-lg p-4">
<div class="text-sm text-gray-500 mb-1">Salam Ramadan</div>
<div id="ramadan-greeting" class="text-lg font-semibold text-green-600">
<div class="text-sm text-gray-500 mb-1" data-i18n="ramadan.greeting">Salam Ramadan</div>
<div id="ramadan-greeting" class="text-lg font-semibold text-green-600" data-i18n="ramadan.loading">
Loading...
</div>
</div>
@@ -44,79 +44,79 @@
<!-- Iftar Countdown -->
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-800 mb-4">⏱️ Hitung Mundur Iftar</h2>
<h2 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ramadan.iftar_countdown">⏱️ Hitung Mundur Iftar</h2>
<div class="flex justify-center items-center">
<div class="text-center mx-4">
<div id="iftar-hours" class="text-4xl font-bold text-gray-800">00</div>
<div class="text-sm text-gray-500">Jam</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.hours">Jam</div>
</div>
<div class="text-4xl font-bold text-gray-300">:</div>
<div class="text-center mx-4">
<div id="iftar-minutes" class="text-4xl font-bold text-gray-800">00</div>
<div class="text-sm text-gray-500">Menit</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.minutes">Menit</div>
</div>
</div>
<div class="text-center mt-4 text-gray-600">
Sampai <span id="next-prayer">Iftar</span>
<span data-i18n="ramadan.until">Sampai</span> <span id="next-prayer" data-i18n="ramadan.iftar">Iftar</span>
</div>
</div>
<!-- Trading Adjustments -->
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-800 mb-4">⚙️ Penyesuaian Trading</h2>
<h2 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ramadan.trading_adjustments">⚙️ Penyesuaian Trading</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 class="text-lg font-medium text-gray-700 mb-3">Waktu Istirahat Puasa</h3>
<h3 class="text-lg font-medium text-gray-700 mb-3" data-i18n="ramadan.fasting_times">Waktu Istirahat Puasa</h3>
<div class="space-y-3">
<div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-yellow-500 mr-3"></div>
<div>
<div class="font-medium">Sahur</div>
<div class="text-sm text-gray-500">03:30 - 05:00 WIB</div>
<div class="font-medium" data-i18n="ramadan.suhoor">Sahur</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.suhoor_time">03:30 - 05:00 WIB</div>
</div>
</div>
<div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-orange-500 mr-3"></div>
<div>
<div class="font-medium">Iftar</div>
<div class="text-sm text-gray-500">18:00 - 19:30 WIB</div>
<div class="font-medium" data-i18n="ramadan.iftar_time">Iftar</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.iftar_time_range">18:00 - 19:30 WIB</div>
</div>
</div>
<div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-purple-500 mr-3"></div>
<div>
<div class="font-medium">Tarawih</div>
<div class="text-sm text-gray-500">20:00 - 21:30 WIB</div>
<div class="font-medium" data-i18n="ramadan.tarawih">Tarawih</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.tarawih_time">20:00 - 21:30 WIB</div>
</div>
</div>
</div>
</div>
<div>
<h3 class="text-lg font-medium text-gray-700 mb-3">Penyesuaian Risiko</h3>
<h3 class="text-lg font-medium text-gray-700 mb-3" data-i18n="ramadan.risk_adjustments">Penyesuaian Risiko</h3>
<div class="space-y-3">
<div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-red-500 mr-3"></div>
<div>
<div class="font-medium">Reduced Risk Mode</div>
<div class="text-sm text-gray-500">20% pengurangan risiko selama puasa</div>
<div class="font-medium" data-i18n="ramadan.reduced_risk_mode">Reduced Risk Mode</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.reduced_risk_description">20% pengurangan risiko selama puasa</div>
</div>
</div>
<div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-blue-500 mr-3"></div>
<div>
<div class="font-medium">Patience Mode</div>
<div class="text-sm text-gray-500">Fokus pada kualitas bukan kuantitas</div>
<div class="font-medium" data-i18n="ramadan.patience_mode">Patience Mode</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.patience_mode_description">Fokus pada kualitas bukan kuantitas</div>
</div>
</div>
<div class="flex items-center">
<div class="w-3 h-3 rounded-full bg-green-500 mr-3"></div>
<div>
<div class="font-medium">Optimal Hours</div>
<div class="text-sm text-gray-500">22:00 - 03:00 WIB</div>
<div class="font-medium" data-i18n="ramadan.optimal_hours">Optimal Hours</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.optimal_hours_time">22:00 - 03:00 WIB</div>
</div>
</div>
</div>
@@ -126,33 +126,32 @@
<!-- Zakat Calculator -->
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-800 mb-4">💰 Kalkulator Zakat Trading</h2>
<h2 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ramadan.zakat_calculator">💰 Kalkulator Zakat Trading</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 class="text-lg font-medium text-gray-700 mb-3">Informasi Zakat</h3>
<h3 class="text-lg font-medium text-gray-700 mb-3" data-i18n="ramadan.zakat_information">Informasi Zakat</h3>
<div class="space-y-3">
<div class="border rounded-lg p-3">
<div class="text-sm text-gray-500">Nisab Emas</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.gold_nisab">Nisab Emas</div>
<div class="text-lg font-semibold">85 gram</div>
</div>
<div class="border rounded-lg p-3">
<div class="text-sm text-gray-500">Nisab Perak</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.silver_nisab">Nisab Perak</div>
<div class="text-lg font-semibold">595 gram</div>
</div>
<div class="border rounded-lg p-3">
<div class="text-sm text-gray-500">Persentase Zakat</div>
<div class="text-sm text-gray-500" data-i18n="ramadan.zakat_percentage">Persentase Zakat</div>
<div class="text-lg font-semibold">2.5%</div>
</div>
</div>
</div>
<div>
<h3 class="text-lg font-medium text-gray-700 mb-3">Pengingat Zakat</h3>
<h3 class="text-lg font-medium text-gray-700 mb-3" data-i18n="ramadan.zakat_reminder">Pengingat Zakat</h3>
<div class="border rounded-lg p-4 bg-blue-50">
<p class="text-gray-700">
Zakat perdagangan: 2.5% dari profit trading selama 1 tahun hijriah.
Jangan lupa menghitung zakat dari profit trading Anda selama Ramadan.
<span data-i18n="ramadan.zakat_trading_reminder">Zakat perdagangan: 2.5% dari profit trading selama 1 tahun hijriah. Jangan lupa menghitung zakat dari profit trading Anda selama Ramadan.</span>
</p>
</div>
</div>
@@ -161,10 +160,10 @@
<!-- Patience Reminder -->
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">🧘‍♂️ Pengingat Kesabaran</h2>
<h2 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="ramadan.patience_reminder">🧘‍♂️ Pengingat Kesabaran</h2>
<div class="border rounded-lg p-6 bg-green-50 text-center">
<div id="patience-reminder" class="text-lg text-gray-700 italic">
<div id="patience-reminder" class="text-lg text-gray-700 italic" data-i18n="ramadan.loading">
Loading...
</div>
</div>
+24 -23
View File
@@ -1,51 +1,52 @@
<!-- templates/settings.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title_key %}settings.title{% endblock %}
{% block title %}Pengaturan{% endblock %}
{% block content %}
<h2 class="text-2xl font-bold text-gray-800 mb-6">Pengaturan</h2>
<h2 class="text-2xl font-bold text-gray-800 mb-6" data-i18n="settings.title">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" id="profile-title">Profil</h3>
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4" id="profile-title" data-i18n="settings.profile">Profil</h3>
<div class="space-y-4">
<div>
<label class="text-sm font-medium text-gray-700" id="full-name-label">Nama Lengkap</label>
<label class="text-sm font-medium text-gray-700" id="full-name-label" data-i18n="label.full_name">Nama Lengkap</label>
<input type="text" value="Reynov Christian" id="full-name-input" 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" id="email-label">Alamat Email</label>
<label class="text-sm font-medium text-gray-700" id="email-label" data-i18n="label.email">Alamat Email</label>
<input type="email" value="contact@chrisnov.com" id="email-input" class="mt-1 block w-full px-3 py-2 bg-gray-100 border border-gray-300 rounded-md shadow-sm" readonly>
</div>
<button id="save-profile-btn" class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700">Simpan Perubahan</button>
<button id="save-profile-btn" class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700" data-i18n="msg.save_changes">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" id="preferences-title">Preferensi</h3>
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4" id="preferences-title" data-i18n="settings.preferences">Preferensi</h3>
<div class="space-y-4">
<div>
<label class="text-sm font-medium text-gray-700" id="language-label">Bahasa</label>
<label class="text-sm font-medium text-gray-700" id="language-label" data-i18n="settings.language">Bahasa</label>
<select id="language-select" 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">
<option value="id">Indonesia</option>
<option value="en">English</option>
<option value="id" data-i18n="settings.indonesian">Indonesia</option>
<option value="en" data-i18n="settings.english">English</option>
</select>
<p class="text-xs text-gray-500 mt-1">Application language settings</p>
</div>
<div>
<label class="text-sm font-medium text-gray-700" id="theme-label">Tema</label>
<label class="text-sm font-medium text-gray-700" id="theme-label" data-i18n="settings.theme">Tema</label>
<select id="theme-select" 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">
<option value="light">Terang</option>
<option value="dark">Gelap (Segera Hadir)</option>
<option value="light" data-i18n="settings.light">Terang</option>
<option value="dark" data-i18n="settings.dark">Gelap (Segera Hadir)</option>
</select>
</div>
<button id="save-preferences-btn" class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700">Simpan Preferensi</button>
<button id="save-preferences-btn" class="bg-blue-600 text-white py-2 px-4 rounded-lg font-medium hover:bg-blue-700" data-i18n="msg.save_preferences">Simpan Preferensi</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" id="api-keys-title">API Keys Bursa</h3>
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4" id="api-keys-title" data-i18n="settings.api_keys">API Keys Bursa</h3>
<div class="space-y-4">
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<div class="flex">
@@ -53,13 +54,13 @@
<i class="fas fa-info-circle text-yellow-400"></i>
</div>
<div class="ml-3">
<p class="text-sm text-yellow-800" id="api-info-text">
<p class="text-sm text-yellow-800" id="api-info-text" data-i18n="settings.api_info">
API Keys akan digunakan oleh versi QuantumBotX berikutnya untuk mengakses broker non-MT5.
</p>
</div>
</div>
</div>
<div class="text-sm text-gray-600" id="api-placeholder-text">
<div class="text-sm text-gray-600" id="api-placeholder-text" data-i18n="settings.api_placeholder">
Fitur ini akan tersedia di QuantumBotX API versi mendatang.
</div>
</div>
@@ -68,24 +69,24 @@
<div class="lg:col-span-1 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" id="quick-settings-title">Pengaturan Cepat</h3>
<h3 class="text-lg font-semibold text-gray-800 border-b pb-4 mb-4" id="quick-settings-title" data-i18n="settings.quick_settings">Pengaturan Cepat</h3>
<div class="space-y-4">
<div class="flex items-center justify-between">
<span class="text-sm text-gray-700" id="notifications-label">Notifikasi Email</span>
<span class="text-sm text-gray-700" id="notifications-label" data-i18n="settings.email_notifications">Notifikasi Email</span>
<input type="checkbox" id="email-notifications" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
</div>
<div class="flex items-center justify-between">
<span class="text-sm text-gray-700" id="auto-update-label">Update Otomatis Strategi</span>
<span class="text-sm text-gray-700" id="auto-update-label" data-i18n="settings.auto_update">Update Otomatis Strategi</span>
<input type="checkbox" id="auto-update" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
</div>
<div class="flex items-center justify-between">
<span class="text-sm text-gray-700" id="demo-mode-label">Mode Demo</span>
<span class="text-sm text-gray-700" id="demo-mode-label" data-i18n="settings.demo_mode">Mode Demo</span>
<input type="checkbox" id="demo-mode" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
</div>
<hr class="my-4">
<div class="text-sm text-gray-600" id="version-info">
<strong>Versi:</strong> 2.0.0<br>
<strong>Terakhir Update:</strong> September 2025
<strong data-i18n="settings.version">Versi:</strong> 2.0.0<br>
<strong data-i18n="settings.last_updated">Terakhir Update:</strong> September 2025
</div>
</div>
</div>
@@ -95,4 +96,4 @@
{% block scripts %}
<script src="{{ url_for('static', filename='js/settings.js') }}"></script>
{% endblock %}
{% endblock %}
+10 -9
View File
@@ -1,13 +1,14 @@
<!-- templates/stocks.html (Setelah Refactor) -->
{% extends "base.html" %}
{% block title_key %}market.stocks_title{% endblock %}
{% 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>
<h2 class="text-2xl font-bold text-gray-800" data-i18n="market.stocks_title">Pasar Saham</h2>
<p class="text-gray-600" data-i18n="market.stocks_description">Data harga real-time dari pasar saham global.</p>
</div>
</div>
@@ -15,15 +16,15 @@
<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>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="market.stock">Saham</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="market.price">Harga</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="market.change_24h">Perubahan 24j</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="market.time">Waktu</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="action.view">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>
<tr><td colspan="5" class="p-4 text-center text-gray-500" data-i18n="market.loading_stocks">Memuat data saham...</td></tr>
</tbody>
</table>
</div>
@@ -38,7 +39,7 @@
</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
<span data-i18n="market.close">Close</span>
</button>
</div>
</div>
+44 -44
View File
@@ -1,25 +1,25 @@
<!-- templates/strategy_switcher/dashboard.html -->
{% extends "base.html" %}
{% block title_key %}strategy_switcher.title{% endblock %}
{% block title %}Strategy Switcher Dashboard{% endblock %}
{% block content %}
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-800 mb-2">🔄 Automatic Strategy Switcher</h1>
<p class="text-gray-600">Real-time monitoring and performance analytics for automatic strategy switching</p>
<h1 class="text-3xl font-bold text-gray-800 mb-2" data-i18n="strategy_switcher.header">🔄 Automatic Strategy Switcher</h1>
<p class="text-gray-600" data-i18n="strategy_switcher.subtitle">Real-time monitoring and performance analytics for automatic strategy switching</p>
</div>
<!-- Current Status Card -->
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-800">Current Status</h2>
<h2 class="text-xl font-semibold text-gray-800" data-i18n="strategy_switcher.current_status">Current Status</h2>
<div class="flex space-x-2">
<button id="manual-trigger" class="px-3 py-1 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm">
<button id="manual-trigger" class="px-3 py-1 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm" data-i18n="strategy_switcher.manual_evaluate">
Manual Evaluate
</button>
<span id="cooldown-status" class="px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800">
<span id="cooldown-status" class="px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800" data-i18n="strategy_switcher.loading">
Loading...
</span>
</div>
@@ -27,22 +27,22 @@
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="border rounded-lg p-4">
<div class="text-sm text-gray-500 mb-1">Active Strategy</div>
<div id="current-strategy" class="text-lg font-semibold text-blue-600">
<div class="text-sm text-gray-500 mb-1" data-i18n="strategy_switcher.active_strategy">Active Strategy</div>
<div id="current-strategy" class="text-lg font-semibold text-blue-600" data-i18n="strategy_switcher.loading">
Loading...
</div>
</div>
<div class="border rounded-lg p-4">
<div class="text-sm text-gray-500 mb-1">Active Symbol</div>
<div id="current-symbol" class="text-lg font-semibold text-blue-600">
<div class="text-sm text-gray-500 mb-1" data-i18n="strategy_switcher.active_symbol">Active Symbol</div>
<div id="current-symbol" class="text-lg font-semibold text-blue-600" data-i18n="strategy_switcher.loading">
Loading...
</div>
</div>
<div class="border rounded-lg p-4">
<div class="text-sm text-gray-500 mb-1">Last Switch</div>
<div id="last-switch" class="text-lg font-semibold">
<div class="text-sm text-gray-500 mb-1" data-i18n="strategy_switcher.last_switch">Last Switch</div>
<div id="last-switch" class="text-lg font-semibold" data-i18n="strategy_switcher.loading">
Loading...
</div>
</div>
@@ -52,8 +52,8 @@
<!-- Strategy Rankings -->
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-800">📊 Strategy Performance Rankings</h2>
<button id="refresh-rankings" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm">
<h2 class="text-xl font-semibold text-gray-800" data-i18n="strategy_switcher.performance_rankings">📊 Strategy Performance Rankings</h2>
<button id="refresh-rankings" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm" data-i18n="strategy_switcher.refresh_rankings">
Refresh Rankings
</button>
</div>
@@ -62,17 +62,17 @@
<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">Rank</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Strategy/Symbol</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Composite Score</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Profitability</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Risk Control</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Market Fit</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="strategy_switcher.rank">Rank</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="strategy_switcher.strategy_symbol">Strategy/Symbol</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="strategy_switcher.composite_score">Composite Score</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="strategy_switcher.profitability">Profitability</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="strategy_switcher.risk_control">Risk Control</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="strategy_switcher.market_fit">Market Fit</th>
</tr>
</thead>
<tbody id="rankings-table" class="bg-white divide-y divide-gray-200">
<tr>
<td colspan="6" class="px-6 py-4 text-center text-gray-500">
<td colspan="6" class="px-6 py-4 text-center text-gray-500" data-i18n="strategy_switcher.loading_strategy_rankings">
Loading strategy rankings...
</td>
</tr>
@@ -84,14 +84,14 @@
<!-- Recent Switches -->
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold text-gray-800">⚡ Recent Strategy Switches</h2>
<button id="refresh-switches" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm">
<h2 class="text-xl font-semibold text-gray-800" data-i18n="strategy_switcher.recent_switches">⚡ Recent Strategy Switches</h2>
<button id="refresh-switches" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm" data-i18n="strategy_switcher.refresh_switches">
Refresh Switches
</button>
</div>
<div id="recent-switches-container">
<div class="text-center py-8 text-gray-500">
<div class="text-center py-8 text-gray-500" data-i18n="strategy_switcher.loading_recent_switches">
Loading recent switches...
</div>
</div>
@@ -99,22 +99,22 @@
<!-- Monitored Instruments -->
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">🔍 Monitored Instruments & Strategies</h2>
<h2 class="text-xl font-semibold text-gray-800 mb-4" data-i18n="strategy_switcher.monitored_instruments">🔍 Monitored Instruments & Strategies</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 class="text-lg font-medium text-gray-700 mb-3"> Instruments</h3>
<h3 class="text-lg font-medium text-gray-700 mb-3" data-i18n="strategy_switcher.instruments"> Instruments</h3>
<div id="monitored-instruments" class="flex flex-wrap gap-2">
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800">
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800" data-i18n="strategy_switcher.loading">
Loading...
</span>
</div>
</div>
<div>
<h3 class="text-lg font-medium text-gray-700 mb-3">Strategies</h3>
<h3 class="text-lg font-medium text-gray-700 mb-3" data-i18n="strategy_switcher.strategies">Strategies</h3>
<div id="test-strategies" class="flex flex-wrap gap-2">
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800">
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800" data-i18n="strategy_switcher.loading">
Loading...
</span>
</div>
@@ -142,16 +142,16 @@ function updateStatus(status) {
if (status.last_switch_time) {
lastSwitchElement.textContent = formatDateTime(status.last_switch_time);
} else {
lastSwitchElement.innerHTML = '<span class="text-gray-400">Never</span>';
lastSwitchElement.innerHTML = '<span class="text-gray-400" data-i18n="strategy_switcher.never">Never</span>';
}
const cooldownElement = document.getElementById('cooldown-status');
if (status.in_cooldown) {
cooldownElement.className = 'px-3 py-1 rounded-full text-sm font-medium bg-yellow-100 text-yellow-800';
cooldownElement.textContent = 'Cooldown Active';
cooldownElement.textContent = window.QuantumBotXI18n.t('strategy_switcher.cooldown_active');
} else {
cooldownElement.className = 'px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800';
cooldownElement.textContent = 'Active';
cooldownElement.textContent = window.QuantumBotXI18n.t('strategy_switcher.active');
}
}
@@ -162,7 +162,7 @@ function updateRankings(rankings) {
if (!rankings || rankings.length === 0) {
tableBody.innerHTML = `
<tr>
<td colspan="6" class="px-6 py-4 text-center text-gray-500">
<td colspan="6" class="px-6 py-4 text-center text-gray-500" data-i18n="strategy_switcher.no_strategy_rankings">
No strategy rankings available
</td>
</tr>
@@ -217,7 +217,7 @@ function updateRecentSwitches(switches) {
if (!switches || switches.length === 0) {
container.innerHTML = `
<div class="text-center py-8 text-gray-500">
<div class="text-center py-8 text-gray-500" data-i18n="strategy_switcher.no_strategy_switches">
<p>No strategy switches recorded yet.</p>
</div>
`;
@@ -233,16 +233,16 @@ function updateRecentSwitches(switches) {
if (decision.action === 'STRATEGY_SWITCH') {
switchDetails = `
<div class="text-sm">
<span class="font-medium">Switched from</span>
<span class="font-medium" data-i18n="strategy_switcher.switch_from">Switched from</span>
<span class="text-red-600">${decision.old_strategy}/${decision.old_symbol}</span>
<span class="font-medium">to</span>
<span class="font-medium" data-i18n="strategy_switcher.switch_to">to</span>
<span class="text-green-600">${decision.new_strategy}/${decision.new_symbol}</span>
</div>
`;
} else {
switchDetails = `
<div class="text-sm">
<span class="font-medium">Initialized with</span>
<span class="font-medium" data-i18n="strategy_switcher.initialized_with">Initialized with</span>
<span class="text-green-600">${decision.new_strategy}/${decision.new_symbol}</span>
</div>
`;
@@ -269,7 +269,7 @@ function updateRecentSwitches(switches) {
<div class="text-right">
<div class="text-sm font-medium text-gray-900">
Score: ${decision.confidence.toFixed(3)}
<span data-i18n="strategy_switcher.score">Score:</span> ${decision.confidence.toFixed(3)}
</div>
${improvementHtml}
</div>
@@ -294,7 +294,7 @@ function updateConfiguration(config) {
</span>`
).join(' ');
} else {
instrumentsContainer.innerHTML = '<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800">None</span>';
instrumentsContainer.innerHTML = '<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800" data-i18n="strategy_switcher.none">None</span>';
}
if (config.test_strategies && config.test_strategies.length > 0) {
@@ -304,7 +304,7 @@ function updateConfiguration(config) {
</span>`
).join(' ');
} else {
strategiesContainer.innerHTML = '<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800">None</span>';
strategiesContainer.innerHTML = '<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800" data-i18n="strategy_switcher.none">None</span>';
}
}
@@ -322,16 +322,16 @@ async function manualTrigger() {
if (result.success) {
// Show success message
alert('Manual strategy evaluation completed successfully!');
alert(window.QuantumBotXI18n.t('msg.success'));
// Refresh all data
fetchData();
} else {
// Show error message
alert(`Error: ${result.error}`);
alert(`${window.QuantumBotXI18n.t('msg.error')}: ${result.error}`);
}
} catch (error) {
console.error('Error triggering manual evaluation:', error);
alert('Error triggering manual evaluation. Check console for details.');
alert(`${window.QuantumBotXI18n.t('error.connection')}. Check console for details.`);
}
}
@@ -386,7 +386,7 @@ document.addEventListener('DOMContentLoaded', function() {
// Set up manual trigger button
document.getElementById('manual-trigger').addEventListener('click', function() {
if (confirm('Are you sure you want to manually trigger strategy evaluation?')) {
if (confirm(window.QuantumBotXI18n.t('strategy_switcher.manual_trigger_confirm'))) {
manualTrigger();
}
});
+39 -37
View File
@@ -1,5 +1,6 @@
{% extends "base.html" %}
{% block title_key %}bots.title{% endblock %}
{% block title %}Trading Bots{% endblock %}
{% block head_extra %}
@@ -10,26 +11,26 @@
<!-- 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>
<h2 class="text-2xl font-bold text-gray-800" data-i18n="bots.title">Trading Bots</h2>
<p class="text-gray-600" data-i18n="bots.manage">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>
<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" data-i18n-title="bots.start_all_title" title="Jalankan semua bot yang dijeda">
<i class="fas fa-play mr-2"></i> <span data-i18n="action.start_all">Start All</span></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" data-i18n-title="bots.stop_all_title" title="Hentikan semua bot yang aktif">
<i class="fas fa-stop mr-2"></i> <span data-i18n="action.stop_all">Stop All</span></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> <span data-i18n="action.create_new_bot">Buat Bot Baru</span></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>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="bots.name_market">Nama / Pasar</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="bots.parameters">Parameter</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="bots.configuration">Konfigurasi</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="label.status">Status</th>
<th scope="col" class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="bots.actions">Aksi</th>
</tr>
</thead>
<tbody id="bots-table-body" class="bg-white divide-y divide-gray-200"></tbody>
@@ -46,12 +47,12 @@
<!-- Header Modal (Tidak akan ikut ter-scroll) -->
<div class="flex-shrink-0 flex items-start justify-between p-5 border-b rounded-t">
<h3 id="modal-title" class="text-xl font-semibold text-gray-900">
<h3 id="modal-title" class="text-xl font-semibold text-gray-900" data-i18n="bots.create_new">
🚀 Buat Bot Baru
</h3>
<button type="button" id="cancel-create" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center">
<i class="fas fa-times"></i>
<span class="sr-only">Tutup modal</span>
<span class="sr-only" data-i18n="bots.close_modal">Tutup modal</span>
</button>
</div>
@@ -61,45 +62,45 @@
<!-- Baris 1: Nama & Pasar -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="name" class="block mb-2 text-sm font-medium text-gray-900">Nama Bot</label>
<input type="text" name="name" id="name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" placeholder="Contoh: XAUUSD Hybrid H1" required>
<label for="name" class="block mb-2 text-sm font-medium text-gray-900" data-i18n="label.bot_name">Nama Bot</label>
<input type="text" name="name" id="name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" data-i18n-placeholder="bots.example_name" placeholder="Contoh: XAUUSD Hybrid H1" required>
</div>
<div>
<label for="market" class="block mb-2 text-sm font-medium text-gray-900">Pasar</label>
<input type="text" name="market" id="market" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" placeholder="Contoh: XAUUSD atau EURUSD" required>
<label for="market" class="block mb-2 text-sm font-medium text-gray-900" data-i18n="label.market">Pasar</label>
<input type="text" name="market" id="market" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" data-i18n-placeholder="bots.example_market" placeholder="Contoh: XAUUSD atau EURUSD" required>
</div>
</div>
<!-- Baris 2: Lot, SL, TP -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<label for="risk_percent" class="block mb-2 text-sm font-medium text-gray-900">Risk per Trade (%)</label>
<label for="risk_percent" class="block mb-2 text-sm font-medium text-gray-900" data-i18n="label.risk_per_trade">Risk per Trade (%)</label>
<input type="number" name="risk_percent" id="risk_percent" value="1.0" step="0.1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required>
</div>
<div>
<label for="sl_atr_multiplier" class="block mb-2 text-sm font-medium text-gray-900">SL (ATR Multiplier)</label>
<label for="sl_atr_multiplier" class="block mb-2 text-sm font-medium text-gray-900" data-i18n="label.sl_atr">SL (ATR Multiplier)</label>
<input type="number" name="sl_atr_multiplier" id="sl_atr_multiplier" value="2.0" step="0.1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required>
</div>
<div>
<label for="tp_atr_multiplier" class="block mb-2 text-sm font-medium text-gray-900">TP (ATR Multiplier)</label>
<label for="tp_atr_multiplier" class="block mb-2 text-sm font-medium text-gray-900" data-i18n="label.tp_atr">TP (ATR Multiplier)</label>
<input type="number" name="tp_atr_multiplier" id="tp_atr_multiplier" value="4.0" step="0.1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required>
</div>
</div>
<!-- Baris 3: Timeframe & Interval -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="timeframe" class="block mb-2 text-sm font-medium text-gray-900">Timeframe</label>
<label for="timeframe" class="block mb-2 text-sm font-medium text-gray-900" data-i18n="label.timeframe">Timeframe</label>
<select id="timeframe" name="timeframe" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
<option value="M1">1 Menit</option>
<option value="M5">5 Menit</option>
<option value="M15">15 Menit</option>
<option value="M30">30 Menit</option>
<option value="H1" selected>1 Jam</option>
<option value="H4">4 Jam</option>
<option value="D1">1 Hari</option>
<option value="M1" data-i18n="bots.timeframe_1m">1 Menit</option>
<option value="M5" data-i18n="bots.timeframe_5m">5 Menit</option>
<option value="M15" data-i18n="bots.timeframe_15m">15 Menit</option>
<option value="M30" data-i18n="bots.timeframe_30m">30 Menit</option>
<option value="H1" selected data-i18n="bots.timeframe_1h">1 Jam</option>
<option value="H4" data-i18n="bots.timeframe_4h">4 Jam</option>
<option value="D1" data-i18n="bots.timeframe_1d">1 Hari</option>
</select>
</div>
<div>
<label for="check_interval_seconds" class="block mb-2 text-sm font-medium text-gray-900">Interval Cek (detik)</label>
<label for="check_interval_seconds" class="block mb-2 text-sm font-medium text-gray-900" data-i18n="label.check_interval">Interval Cek (detik)</label>
<input type="number" name="check_interval_seconds" id="check_interval_seconds" value="60" step="1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required>
</div>
</div>
@@ -107,27 +108,28 @@
<div class="border-t pt-4">
<div class="flex items-center">
<input type="checkbox" id="enable_strategy_switching" name="enable_strategy_switching" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500">
<label for="enable_strategy_switching" class="ml-2 text-sm font-medium text-gray-900">Aktifkan Automatic Strategy Switching</label>
<label for="enable_strategy_switching" class="ml-2 text-sm font-medium text-gray-900" data-i18n="bots.enable_strategy_switching">Aktifkan Automatic Strategy Switching</label>
</div>
<p class="mt-1 text-xs text-gray-500">Jika diaktifkan, bot akan secara otomatis beralih ke strategi terbaik berdasarkan kinerja terkini.</p>
<p class="mt-1 text-xs text-gray-500" data-i18n="bots.strategy_switching_info">Jika diaktifkan, bot akan secara otomatis beralih ke strategi terbaik berdasarkan kinerja terkini.</p>
</div>
<!-- Baris 4: Strategi & Parameternya -->
<div>
<label for="strategy" class="block mb-2 text-sm font-medium text-gray-900">Strategi</label>
<label for="strategy" class="block mb-2 text-sm font-medium text-gray-900" data-i18n="label.strategy">Strategi</label>
<select id="strategy" name="strategy" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required>
<option value="" disabled selected>Pilih sebuah strategi</option>
<option value="" disabled selected data-i18n="bots.select_strategy">Pilih sebuah strategi</option>
</select>
</div>
<div id="strategy-params-container" class="grid grid-cols-1 md:grid-cols-2 gap-6 pt-4 border-t">
<!-- Parameter strategi akan dimuat di sini oleh JavaScript -->
<span data-i18n="bots.strategy_params_loaded">Parameter strategi akan dimuat di sini oleh JavaScript</span>
</div>
</div>
</form>
<!-- Footer Modal (Tidak akan ikut ter-scroll) -->
<div class="flex-shrink-0 flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b">
<button type="submit" id="submit-bot-btn" form="create-bot-form" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center">Buat Bot</button>
<button type="button" id="cancel-create-footer" class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10">Batal</button>
<button type="submit" id="submit-bot-btn" form="create-bot-form" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center" data-i18n="bots.create_bot">Buat Bot</button>
<button type="button" id="cancel-create-footer" class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10" data-i18n="bots.cancel">Batal</button>
</div>
</div>
</div>
@@ -135,4 +137,4 @@
{% block scripts %}
<script src="{{ url_for('static', filename='js/trading_bots.js') }}"></script>
{% endblock %}
{% endblock %}
+21
View File
@@ -0,0 +1,21 @@
{
"version": 2,
"builds": [
{
"src": "api/app.py",
"use": "@vercel/python",
"config": {
"runtime": "python3.12"
}
}
],
"routes": [
{
"src": "/(.*)",
"dest": "api/app.py"
}
],
"env": {
"SKIP_MT5_INIT": "1"
}
}