import ccxt
import time
import pandas as pd
import numpy as np
import tensorflow as tf
from [Link] import MinMaxScaler
from sklearn.model_selection import train_test_split, KFold # Ditambahkan KFold
untuk validasi
import xgboost as xgb
import lightgbm as lgb
import json
import os # Ditambahkan untuk membaca variabel lingkungan
import uuid # Ditambahkan untuk ID pengguna anonim
# --- Konfigurasi Global & Kredensial (Sensitif, harus dimuat dari variabel
lingkungan) ---
# Mengambil kredensial dari variabel lingkungan untuk keamanan dan fleksibilitas
deployment
API_KEY = [Link]("API_KEY", "YOUR_BINANCE_API_KEY") # Default untuk dev,
gunakan env var di prod
SECRET_KEY = [Link]("SECRET_KEY", "YOUR_BINANCE_SECRET_KEY") # Default
untuk dev, gunakan env var di prod
EXCHANGE_ID = [Link]("EXCHANGE_ID", 'binance') # Default 'binance', bisa
'bybit'
APP_ID = [Link]("RAILWAY_APP_ID", [Link]("APP_ID", 'default-app-id-
railway')) # Gunakan ID dari Railway atau APP_ID umum
# Firebase Konfigurasi (jika ingin menyimpan data di Firestore)
FIREBASE_CONFIG_STR = [Link]("FIREBASE_CONFIG", '{}')
FIREBASE_CONFIG = {}
try:
if FIREBASE_CONFIG_STR:
FIREBASE_CONFIG = [Link](FIREBASE_CONFIG_STR)
except [Link]:
print("Error parsing FIREBASE_CONFIG. Ensure it's valid JSON.")
# Import Firebase jika FIREBASE_CONFIG tidak kosong
db = None
if FIREBASE_CONFIG:
try:
import firebase_admin
from firebase_admin import credentials, initialize_app, firestore
if not firebase_admin._apps:
cred = [Link](FIREBASE_CONFIG)
initialize_app(cred)
db = [Link]()
print("Firebase Firestore initialized.")
except Exception as e:
print(f"Error initializing Firebase: {e}")
else:
print("Firebase Firestore not configured via environment variable.")
# --- Konstanta Trading ---
INITIAL_CAPITAL_USD = 5.0 # Modal awal yang sangat kecil
MAX_RISK_PER_TRADE_PERCENT = 0.01 # Max 1% risiko per posisi
RISK_LOCK_DAILY_MAX_LOSS_PERCENT = 0.05 # Max 5% cutloss harian
MAX_DAILY_TRADES = 10 # Batasan overtrade per hari
MICRO_COMPOUND_PERCENT = 0.50 # 50% dari profit micro akan dicompound
MAX_COMPOUND_RISK_INCREASE_PERCENT = 0.02 # Max 2% peningkatan risiko per compound
(total 1% + 2% = 3% max)
# --- Utilitas Dasar (Hindari repainting, perhitungan presisi) ---
def calculate_ema(data, period):
"""Menghitung Exponential Moving Average (EMA)."""
return [Link](span=period, adjust=False).mean()
def calculate_atr(high, low, close, period=14):
"""Menghitung Average True Range (ATR)."""
high_low = high - low
high_prev_close = abs(high - [Link](1))
low_prev_close = abs(low - [Link](1))
true_range = [Link]([high_low, high_prev_close, low_prev_close],
axis=1).max(axis=1)
atr = true_range.ewm(span=period, adjust=False).mean()
return atr
def calculate_bollinger_bands(data, window=20, num_std_dev=2):
"""Menghitung Bollinger Bands."""
sma = [Link](window=window).mean()
std_dev = [Link](window=window).std()
upper_band = sma + (std_dev * num_std_dev)
lower_band = sma - (std_dev * num_std_dev)
return upper_band, lower_band, sma, std_dev
def calculate_bollinger_bandwidth(data, window=20):
"""Menghitung Bollinger Band Width."""
_, _, _, std_dev = calculate_bollinger_bands(data, window)
sma = [Link](window=window).mean()
bb_width = ((2 * std_dev) / sma) * 100 # Persentase bandwidth
return bb_width
def calculate_keltner_channels(high, low, close, atr_period=10, multiplier=2):
"""Menghitung Keltner Channels."""
tr = [Link]([high - low, abs(high - [Link](1)), abs(low -
[Link](1))], axis=1).max(axis=1)
atr = [Link](span=atr_period, adjust=False).mean()
ema = [Link](span=20, adjust=False).mean() # Typical for Keltner
upper_channel = ema + (atr * multiplier)
lower_channel = ema - (atr * multiplier)
return upper_channel, lower_channel, ema
# Implementasi QQE Mod yang lebih akurat
def calculate_qqe_mod(data, rsi_period=14, sf=5, qqe_period=4):
"""
Menghitung QQE Mod.
Memerlukan perhitungan RSI, Wilder's smoothing, dan EMA ganda.
"""
# RSI Calculation
delta = [Link]()
gain = [Link](delta > 0, 0)
loss = -[Link](delta < 0, 0)
avg_gain = [Link](span=rsi_period, adjust=False).mean()
avg_loss = [Link](span=rsi_period, adjust=False).mean()
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
# Wilder's Smoothing for RSI (SMA for first part, then EMA)
wilder_alpha = 1 / rsi_period
ema_rsi = [Link](span=sf, adjust=False).mean()
ema_rsi2 = ema_rsi.ewm(span=sf, adjust=False).mean()
# QQE Calculation
qqe_line = ema_rsi.ewm(span=qqe_period, adjust=False).mean()
qqe_line_signal = ema_rsi2.ewm(span=qqe_period, adjust=False).mean()
return qqe_line # Mengembalikan hanya QQE Line sebagai nilai utama
# --- Add Auto Feature Engineering (AutoFE) ---
def calculate_auto_features(df):
"""
Menghitung fitur-fitur baru secara otomatis untuk Auto Feature Engineering.
"""
# EMA Crossover Ratio: Jarak relatif antara EMA cepat dan lambat
df['ema_cross_ratio_9_21'] = (df['ema9'] - df['ema21']) / df['close']
df['ema_cross_ratio_21_50'] = (df['ema21'] - df['ema50']) / df['close']
# ATR-to-Range Ratio: Mengukur seberapa besar ATR relatif terhadap range candle
df['atr_to_range_ratio'] = df['atr'] / (df['high'] - df['low'])
df['atr_to_range_ratio'].replace([[Link], -[Link]], [Link], inplace=True) #
Handle division by zero
# RSI Derivative: Perubahan RSI, mengindikasikan momentum RSI
# Membutuhkan RSI terlebih dahulu
# rsi_temp = 100 - (100 / (1 + df['close'].diff().apply(lambda x: x if x > 0
else 0).ewm(span=14, adjust=False).mean() / \
# df['close'].diff().apply(lambda x: abs(x) if x < 0 else
0).ewm(span=14, adjust=False).mean()))
# df['rsi_derivative'] = rsi_temp.diff()
# Placeholder for a simpler derivative calculation based on QQE Mod for now
df['qqe_mod_derivative'] = df['qqe_mod'].diff()
# Candle Body Percentage
df['body_percent'] = abs(df['close'] - df['open']) / (df['high'] - df['low'])
df['body_percent'].replace([[Link], -[Link]], [Link], inplace=True)
df['upper_shadow_percent'] = (df['high'] - df[['open', 'close']].max(axis=1)) /
(df['high'] - df['low'])
df['lower_shadow_percent'] = (df[['open', 'close']].min(axis=1) - df['low']) /
(df['high'] - df['low'])
df['upper_shadow_percent'].replace([[Link], -[Link]], [Link], inplace=True)
df['lower_shadow_percent'].replace([[Link], -[Link]], [Link], inplace=True)
return df
# --- Kelas Dasar untuk Analisis Pasar ---
class MarketAnalyzer:
def __init__(self, intervals=['1m', '5m', '1h']):
[Link] = intervals
[Link] = {interval: [Link]() for interval in intervals}
def fetch_data(self, exchange, symbol, limit=500):
"""Mengambil data OHLCV historis dan real-time."""
for interval in [Link]:
try:
ohlcv = exchange.fetch_ohlcv(symbol, interval, limit=limit)
df = [Link](ohlcv, columns=['timestamp', 'open', 'high',
'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index('timestamp', inplace=True)
[Link][interval] = df
except Exception as e:
print(f"Error fetching {symbol} {interval} data: {e}")
return [Link]
def apply_indicators(self):
"""Menerapkan semua indikator ke data di berbagai timeframe."""
for interval, df in [Link]():
if [Link]:
continue
# --- Price Action Deteksi ---
df['engulfing'] = self._detect_engulfing(df)
df['inside_bar'] = self._detect_inside_bar(df)
df['pin_bar'] = self._detect_pin_bar(df)
# --- Support & Resistance (Fractal + ATR) ---
df['fractal_high'] = df['high'][(df['high'].shift(2) < df['high']) &
(df['high'].shift(1) < df['high']) & \
(df['high'].shift(-1) < df['high']) &
(df['high'].shift(-2) < df['high'])]
df['fractal_low'] = df['low'][(df['low'].shift(2) > df['low']) &
(df['low'].shift(1) > df['low']) & \
(df['low'].shift(-1) > df['low']) &
(df['low'].shift(-2) > df['low'])]
df['atr'] = calculate_atr(df['high'], df['low'], df['close']) # ATR
untuk S/R dan SL/TP
# --- Breakout / Fakeout Deteksi ---
df['bb_upper'], df['bb_lower'], _, df['bb_std'] =
calculate_bollinger_bands(df['close'])
df['bb_width'] = calculate_bollinger_bandwidth(df['close'])
df['keltner_upper'], df['keltner_lower'], _ =
calculate_keltner_channels(df['high'], df['low'], df['close'])
# Logika breakout/fakeout yang lebih canggih
# Breakout: close di luar BB, volume spike, dan harga sebelumnya di
dalam BB
df['is_breakout_up'] = (df['close'] > df['bb_upper']) & (df['volume'] >
df['volume'].rolling(window=20).mean() * 1.5) & \
(df['close'].shift(1) < df['bb_upper'].shift(1))
df['is_breakout_down'] = (df['close'] < df['bb_lower']) & (df['volume']
> df['volume'].rolling(window=20).mean() * 1.5) & \
(df['close'].shift(1) > df['bb_lower'].shift(1))
# Fakeout: Harga menembus lalu kembali ke dalam BB
df['is_fakeout_up'] = (df['close'].shift(1) > df['bb_upper'].shift(1))
& (df['close'] < df['bb_upper'])
df['is_fakeout_down'] = (df['close'].shift(1) <
df['bb_lower'].shift(1)) & (df['close'] > df['bb_lower'])
# --- MTF Indicators (Supertrend, EMA, QQE Mod) ---
df['ema9'] = calculate_ema(df['close'], 9)
df['ema21'] = calculate_ema(df['close'], 21)
df['ema50'] = calculate_ema(df['close'], 50)
df['ema200'] = calculate_ema(df['close'], 200)
# Placeholder untuk Supertrend (memerlukan implementasi penuh)
# Anda bisa menggunakan library ta-lib atau mengimplementasikan secara
manual.
# df['supertrend'] = self._calculate_supertrend(df)
df['qqe_mod'] = calculate_qqe_mod(df['close'])
# --- Volume & Order Flow Filter ---
df['volume_spike'] = df['volume'] >
df['volume'].rolling(window=50).mean() * 2 # Volume spike > 2x rata-rata 50 bar
df['volume_spike_extreme'] = df['volume'] >
df['volume'].rolling(window=50).mean() * 3 # Volume spike > 3x rata-rata 50 bar
# CVD (Cumulative Volume Delta) adalah kompleks dan biasanya memerlukan
akses data level 2.
# Ini adalah placeholder. Untuk implementasi nyata, Anda akan
membutuhkan data tick by tick.
df['cvd'] = 0 # Placeholder for CVD.
# Tambahkan placeholder untuk Funding Rate, Long/Short Ratio, Open
Interest Delta
# Untuk implementasi nyata, data ini perlu di-fetch dari API bursa atau
penyedia data lain.
df['funding_rate'] = 0
df['long_short_ratio'] = 0
df['open_interest_delta'] = 0
# --- Auto Feature Engineering ---
df = calculate_auto_features(df)
[Link][interval] = df
return [Link]
# --- Deteksi Pola Candle ---
def _detect_engulfing(self, df):
"""Deteksi pola Engulfing."""
bullish = ((df['close'] > df['open']) & (df['close'].shift(1) <
df['open'].shift(1)) &
(df['close'] > df['open'].shift(1)) & (df['open'] <
df['close'].shift(1)))
bearish = ((df['close'] < df['open']) & (df['close'].shift(1) >
df['open'].shift(1)) &
(df['close'] < df['open'].shift(1)) & (df['open'] >
df['close'].shift(1)))
return [Link](bullish, 'bullish', [Link](bearish, 'bearish', None))
def _detect_inside_bar(self, df):
"""Deteksi pola Inside Bar."""
inside = ((df['high'] < df['high'].shift(1)) & (df['low'] >
df['low'].shift(1)))
return [Link](inside, 'inside', None)
def _detect_pin_bar(self, df, body_ratio=0.3, shadow_ratio=2):
"""Deteksi pola Pin Bar."""
bullish = ((df['close'] > df['open']) &
((df['close'] - df['open']) < (df['high'] - df['low']) *
body_ratio) &
(df['low'].shift(1) - df['low']) > (df['high'] - df['low']) *
shadow_ratio)
bearish = ((df['close'] < df['open']) &
((df['open'] - df['close']) < (df['high'] - df['low']) *
body_ratio) &
(df['high'] - df['high'].shift(1)) > (df['high'] - df['low']) *
shadow_ratio)
return [Link](bullish, 'bullish_pin', [Link](bearish, 'bearish_pin',
None))
def _calculate_supertrend(self, df):
"""Placeholder untuk Supertrend."""
return 'up' # Harus diimplementasikan dengan benar.
# --- Kelas AI/ML Prediction Engine ---
class AIMLPredictor:
def __init__(self):
[Link] = MinMaxScaler()
self.lstm_model = None
self.transformer_model = None # Ditambahkan model Transformer
self.xgboost_model = None
self.lightgbm_model = None
self.reinforcement_agent = ReinforcementAgent() # Menginisialisasi agen RL
self._load_models()
def _load_models(self):
"""Memuat model AI/ML yang telah dilatih."""
try:
self.lstm_model = [Link].load_model('models/lstm_model.h5')
self.transformer_model =
[Link].load_model('models/transformer_model.h5') # Muat Transformer
self.xgboost_model = [Link]()
self.xgboost_model.load_model('models/xgboost_model.json')
self.lightgbm_model =
[Link](model_file='models/lightgbm_model.txt')
print("AI/ML models loaded successfully.")
except Exception as e:
print(f"Error loading AI/ML models: {e}. Models will be initialized for
training.")
self.lstm_model = None
self.transformer_model = None
self.xgboost_model = None
self.lightgbm_model = None
def _preprocess_data_for_sequence_models(self, df, sequence_length=60,
features=None):
"""Mempersiapkan data untuk model LSTM/Transformer."""
if features is None:
features = ['open', 'high', 'low', 'close', 'volume', 'ema9', 'ema21',
'atr', 'bb_width',
'qqe_mod', 'ema_cross_ratio_9_21', 'ema_cross_ratio_21_50',
'atr_to_range_ratio', 'qqe_mod_derivative', 'body_percent',
'upper_shadow_percent', 'lower_shadow_percent',
'funding_rate', 'long_short_ratio', 'open_interest_delta']
# Tambah fitur AutoFE & Behavioral AI
df_clean = df[features].dropna()
if df_clean.empty or len(df_clean) < sequence_length:
return [Link]([]), [Link]([])
scaled_data = [Link].fit_transform(df_clean)
X, y = [], []
for i in range(sequence_length, len(scaled_data)):
[Link](scaled_data[i-sequence_length:i])
[Link](scaled_data[i, [Link]('close')]) # Memprediksi harga
'close' berikutnya
return [Link](X), [Link](y)
def train_lstm(self, df_1m):
"""Melatih model LSTM."""
X, y = self._preprocess_data_for_sequence_models(df_1m)
if [Link] == 0:
print("Tidak cukup data untuk melatih LSTM.")
return
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
random_state=42)
self.lstm_model = [Link]([
[Link](shape=(X_train.shape[1], X_train.shape[2])),
[Link](100, return_sequences=True), # Lebih banyak unit
[Link](0.3), # Dropout rate tuning
[Link](100),
[Link](0.3),
[Link](1)
])
self.lstm_model.compile(optimizer='adam', loss='mse')
# Early stopping callback
early_stopping = [Link](monitor='val_loss',
patience=10, restore_best_weights=True)
self.lstm_model.fit(X_train, y_train, epochs=100, batch_size=32,
validation_data=(X_test, y_test),
callbacks=[early_stopping], verbose=0)
self.lstm_model.save('models/lstm_model.h5')
print("LSTM model trained and saved.")
# Tambahkan CNN sebagai fitur ekstraktor sebelum LSTM/Transformer
def _create_cnn_extractor(self, input_shape):
model = [Link]([
[Link].Conv1D(filters=32, kernel_size=3, activation='relu',
input_shape=input_shape),
[Link].MaxPooling1D(pool_size=2),
[Link].Conv1D(filters=64, kernel_size=3, activation='relu'),
[Link].MaxPooling1D(pool_size=2),
[Link]()
])
return model
# Contoh sederhana Transformer Encoder (pengganti atau pelengkap LSTM)
def train_transformer(self, df_1m):
"""Melatih model Transformer (sederhana)."""
X, y = self._preprocess_data_for_sequence_models(df_1m)
if [Link] == 0:
print("Tidak cukup data untuk melatih Transformer.")
return
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
random_state=42)
# Basic Transformer Encoder - ini adalah representasi yang disederhanakan
# TST, Informer, Temporal Fusion Transformer jauh lebih kompleks.
# Implementasi penuh memerlukan layer MultiHeadSelfAttention,
LayerNormalization, PositionalEncoding, dll.
inputs = [Link](shape=(X_train.shape[1], X_train.shape[2]))
# Opsi: Integrasi CNN sebelum Transformer
# cnn_features = self._create_cnn_extractor((X_train.shape[1],
X_train.shape[2]))(inputs)
# x = [Link]((1, cnn_features.shape[-1]))(cnn_features) #
Reshape untuk Transformer jika CNN digunakan
x = inputs # Tanpa CNN ekstraktor untuk contoh ini
attention_output = [Link](num_heads=2,
key_dim=X_train.shape[2])(query=x, value=x)
x = [Link]()([x, attention_output])
x = [Link](epsilon=1e-6)(x)
ff_output = [Link](X_train.shape[2] * 2, activation='relu')
(x)
ff_output = [Link](X_train.shape[2])(ff_output)
x = [Link]()([x, ff_output])
x = [Link](epsilon=1e-6)(x)
x = [Link]()(x) # Flatten untuk output Dense
outputs = [Link](1)(x)
self.transformer_model = [Link](inputs=inputs, outputs=outputs)
self.transformer_model.compile(optimizer='adam', loss='mse')
early_stopping = [Link](monitor='val_loss',
patience=10, restore_best_weights=True)
self.transformer_model.fit(X_train, y_train, epochs=100, batch_size=32,
validation_data=(X_test, y_test),
callbacks=[early_stopping], verbose=0)
self.transformer_model.save('models/transformer_model.h5')
print("Transformer model trained and saved.")
def predict_lstm(self, df_1m):
"""Memprediksi arah harga menggunakan LSTM."""
if self.lstm_model is None:
self.train_lstm(df_1m)
if self.lstm_model is None: return 0 # Gagal melatih
features_for_seq = ['open', 'high', 'low', 'close', 'volume', 'ema9',
'ema21', 'atr', 'bb_width',
'qqe_mod', 'ema_cross_ratio_9_21',
'ema_cross_ratio_21_50',
'atr_to_range_ratio', 'qqe_mod_derivative',
'body_percent',
'upper_shadow_percent', 'lower_shadow_percent',
'funding_rate', 'long_short_ratio',
'open_interest_delta']
last_sequence_df = df_1m[features_for_seq].tail(60).dropna()
if len(last_sequence_df) < 60:
print("Tidak cukup data untuk prediksi LSTM.")
return 0 # Tidak bisa prediksi
scaled_last_sequence = [Link](last_sequence_df)
prediction = self.lstm_model.predict(scaled_last_sequence.reshape(1, 60,
len(features_for_seq)))[0][0]
dummy_array = [Link]((1, len(features_for_seq)))
dummy_array[0, features_for_seq.index('close')] = prediction
actual_price_prediction = [Link].inverse_transform(dummy_array)[0,
features_for_seq.index('close')]
last_close = df_1m['close'].iloc[-1]
return 1 if actual_price_prediction > last_close else -1 # 1 untuk naik, -1
untuk turun
def predict_transformer(self, df_1m):
"""Memprediksi arah harga menggunakan Transformer."""
if self.transformer_model is None:
self.train_transformer(df_1m)
if self.transformer_model is None: return 0
features_for_seq = ['open', 'high', 'low', 'close', 'volume', 'ema9',
'ema21', 'atr', 'bb_width',
'qqe_mod', 'ema_cross_ratio_9_21',
'ema_cross_ratio_21_50',
'atr_to_range_ratio', 'qqe_mod_derivative',
'body_percent',
'upper_shadow_percent', 'lower_shadow_percent',
'funding_rate', 'long_short_ratio',
'open_interest_delta']
last_sequence_df = df_1m[features_for_seq].tail(60).dropna()
if len(last_sequence_df) < 60:
print("Tidak cukup data untuk prediksi Transformer.")
return 0
scaled_last_sequence = [Link](last_sequence_df)
prediction = self.transformer_model.predict(scaled_last_sequence.reshape(1,
60, len(features_for_seq)))[0][0]
dummy_array = [Link]((1, len(features_for_seq)))
dummy_array[0, features_for_seq.index('close')] = prediction
actual_price_prediction = [Link].inverse_transform(dummy_array)[0,
features_for_seq.index('close')]
last_close = df_1m['close'].iloc[-1]
return 1 if actual_price_prediction > last_close else -1
def _prepare_features_for_tree_models(self, analyzed_data, lstm_prediction,
transformer_prediction):
"""Mempersiapkan fitur untuk XGBoost/LightGBM dari data yang dianalisis."""
features_df_list = []
for interval, df in analyzed_data.items():
if [Link]: continue
current_bar = [Link][-1]
# Pilih hanya kolom numerik untuk prefixing
numeric_cols = current_bar.select_dtypes(include=[Link]).index
current_bar_numeric =
current_bar[numeric_cols].add_prefix(f'{interval}_')
features_df_list.append(current_bar_numeric.to_frame().T)
if not features_df_list:
return [Link]()
features_df = [Link](features_df_list, axis=1)
# Ensure all columns are numeric after concatenation
features_df = features_df.select_dtypes(include=[Link])
# Tambahkan prediksi LSTM & Transformer sebagai fitur tambahan
features_df['lstm_direction'] = lstm_prediction
features_df['transformer_direction'] = transformer_prediction
# Contoh sederhana target: 1 jika harga naik dalam X bar, 0 jika
turun/sideways
# Ini perlu disesuaikan dengan definisi target yang lebih canggih untuk
trading
# Misalnya, apakah posisi ini menguntungkan setelah X menit?
# Untuk training, Anda akan memerlukan target historis yang sebenarnya.
# Untuk demo ini, kita akan membuat target dummy untuk memastikan model
bisa dilatih.
if '1m_close' in features_df.columns:
# Target dummy: naik 5 menit ke depan (hanya untuk memastikan model
bisa dilatih)
features_df['target'] = (features_df['1m_close'].shift(-5) >
features_df['1m_close']).astype(int)
else:
features_df['target'] = 0 # Placeholder jika 1m_close tidak ada (tidak
ideal)
return features_df.dropna()
def train_tree_models(self, analyzed_data):
"""Melatih model XGBoost dan LightGBM."""
# Data dummy untuk melatih tree models jika belum ada model
dummy_features_df = self._prepare_features_for_tree_models(analyzed_data,
0, 0) # Berikan prediksi dummy untuk pelatihan
if dummy_features_df.empty:
print("Tidak cukup data untuk melatih model Tree.")
return
X = dummy_features_df.drop('target', axis=1)
y = dummy_features_df['target']
# K-fold Cross-validation untuk anti-overfit
kf = KFold(n_splits=5, shuffle=True, random_state=42)
# XGBoost
self.xgboost_model = [Link]()
best_xgb_model = None
min_xgb_val_loss = float('inf')
for fold, (train_index, val_index) in enumerate([Link](X)):
X_train, X_val = [Link][train_index], [Link][val_index]
y_train, y_val = [Link][train_index], [Link][val_index]
dtrain_xgb = [Link](X_train, label=y_train)
dval_xgb = [Link](X_val, label=y_val)
params_xgb = {'objective': 'binary:logistic', 'eval_metric': 'logloss',
'tree_method': 'hist'} # faster
evals_result = {}
current_xgb_model = [Link](params_xgb, dtrain_xgb,
num_boost_round=100,
evals=[(dval_xgb, 'validation')],
callbacks=[[Link].record_evaluation(evals_result)],
verbose_eval=False)
val_loss = evals_result['validation']['logloss'][-1]
if val_loss < min_xgb_val_loss:
min_xgb_val_loss = val_loss
best_xgb_model = current_xgb_model
if best_xgb_model:
self.xgboost_model = best_xgb_model
self.xgboost_model.save_model('models/xgboost_model.json')
print("XGBoost model trained and saved.")
else:
print("Failed to train XGBoost model.")
# LightGBM
self.lightgbm_model = [Link]()
best_lgb_model = None
min_lgb_val_loss = float('inf')
for fold, (train_index, val_index) in enumerate([Link](X)):
X_train, X_val = [Link][train_index], [Link][val_index]
y_train, y_val = [Link][train_index], [Link][val_index]
train_data_lgb = [Link](X_train, label=y_train)
test_data_lgb = [Link](X_val, label=y_val)
params_lgb = {'objective': 'binary', 'metric': 'binary_logloss'}
current_lgb_model = [Link](params_lgb, train_data_lgb,
num_boost_round=100,
valid_sets=[test_data_lgb],
callbacks=[lgb.early_stopping(10, verbose=False)]) # Early stopping
val_loss = current_lgb_model.best_score['valid_0']['binary_logloss']
if val_loss < min_lgb_val_loss:
min_lgb_val_loss = val_loss
best_lgb_model = current_lgb_model
if best_lgb_model:
self.lightgbm_model = best_lgb_model
self.lightgbm_model.save_model('models/lightgbm_model.txt')
print("LightGBM model trained and saved.")
else:
print("Failed to train LightGBM model.")
def make_decision(self, analyzed_data, lstm_prediction,
transformer_prediction):
"""Mengambil keputusan entry berdasarkan multi-feature pasar dan prediksi
AI."""
if self.xgboost_model is None or self.lightgbm_model is None:
self.train_tree_models(analyzed_data) # Melatih jika model belum ada
if self.xgboost_model is None or self.lightgbm_model is None:
return 'HOLD', {'xgb_conf': 0, 'lgb_conf': 0, 'combined_conf': 0}
features_df = self._prepare_features_for_tree_models(analyzed_data,
lstm_prediction, transformer_prediction)
if features_df.empty:
return 'HOLD', {'xgb_conf': 0, 'lgb_conf': 0, 'combined_conf': 0}
current_features = features_df.drop('target', axis=1).iloc[-1].to_frame().T
# Prediksi dengan XGBoost
dcurrent_xgb = [Link](current_features)
xgb_proba = self.xgboost_model.predict(dcurrent_xgb)[0]
# Prediksi dengan LightGBM
lgb_proba = self.lightgbm_model.predict(current_features)[0]
# Kombinasikan hasil prediksi (rata-rata)
combined_proba = (xgb_proba + lgb_proba) / 2
decision = 'HOLD'
if combined_proba > 0.75 and lstm_prediction == 1 and
transformer_prediction == 1: # Peningkatan ambang batas & konfirmasi Transformer
decision = 'BUY'
elif combined_proba < 0.25 and lstm_prediction == -1 and
transformer_prediction == -1:
decision = 'SELL'
confidence_scores = {
'xgb_conf': xgb_proba,
'lgb_conf': lgb_proba,
'combined_conf': combined_proba
}
return decision, confidence_scores
def update_reinforcement_model(self, trade_result, market_features_at_entry):
"""Memperbarui model Reinforcement Learning berdasarkan hasil trading."""
# Ini adalah inti dari "live learning adaptif" dan Meta-Learning Layer.
# 'trade_result' akan berisi informasi seperti: entry_price, exit_price,
profit_loss, duration
# 'market_features_at_entry' adalah state pasar saat entry.
# RL akan menggunakan ini untuk menyesuaikan strategi atau bobot model.
# Implementasi RL bisa berupa A2C, PPO, DQN, dll., yang memerlukan
lingkungan simulasi.
# Disini kita panggil agen RL untuk menerima feedback.
print(f"Reinforcement Learning: Menerima hasil trading: {trade_result}.
Mengoptimalkan model...")
# Contoh reward functions:
profit_loss = trade_result.get('profit_loss', 0)
duration = trade_result.get('duration', 1)
r_r = trade_result.get('r_r', 0) # Reward from Risk:Reward
max_drawdown = trade_result.get('max_drawdown', 0) # Reward from Max
Drawdown
# Reward dari P/L
reward = profit_loss
# Tambahkan reward berdasarkan R:R (jika > 1, reward positif)
if r_r > 1:
reward += r_r * 0.1
# Beri penalti untuk drawdown tinggi
if max_drawdown > 0.02: # Misalnya, jika drawdown > 2%
reward -= max_drawdown * 10
# Sharpe ratio akan dihitung secara agregat di Meta-Learning Layer atau
Dashboard
# Agen RL menerima state (fitur pasar) dan reward
self.reinforcement_agent.learn(market_features_at_entry, reward)
# Placeholder: Di sini juga bisa memicu retraining model AI/ML utama
# berdasarkan akumulasi data hasil trading.
if self.reinforcement_agent.should_retrain():
print("Memicu retraining model AI/ML utama berdasarkan pembelajaran
RL...")
# Ini akan membutuhkan data historis yang diperbarui dengan hasil
trading.
# self.train_lstm(updated_df_1m)
# self.train_transformer(updated_df_1m)
# self.train_tree_models(updated_analyzed_data)
pass
# --- Placeholder untuk Reinforcement Learning Agent ---
class ReinforcementAgent:
def __init__(self):
self.episode_count = 0
self.total_reward = 0
self.history_for_retraining = [] # Simpan data untuk retraining
def learn(self, state, reward):
"""Agen belajar dari state dan reward."""
self.episode_count += 1
self.total_reward += reward
# Dalam implementasi nyata, ini akan memperbarui kebijakan agen (misalnya,
Jaringan Neural)
# berdasarkan algoritma RL (PPO, A2C, DQN).
# State di sini akan menjadi fitur pasar yang relevan.
print(f"RL Agent: Episode {self.episode_count}, Reward Terbaru:
{reward:.2f}, Total Reward: {self.total_reward:.2f}")
def should_retrain(self):
"""Memutuskan apakah model utama perlu dilatih ulang."""
# Contoh: Latih ulang setiap 100 episode atau jika total reward menurun
tajam.
return self.episode_count % 100 == 0 and self.episode_count > 0
# --- Kelas Eksekusi & Manajemen Risiko ---
class TradeExecutor:
def __init__(self, exchange_client, initial_capital):
[Link] = exchange_client
self.current_capital = initial_capital
[Link] = {} # {symbol: {side, entry_price, size, sl, tp, open_time,
initial_sl_price, initial_tp_price, max_profit_seen, max_drawdown}}
self.daily_profit_loss = 0
self.trades_today = 0
self.daily_risk_lock_triggered = False
self.current_max_risk_per_trade = MAX_RISK_PER_TRADE_PERCENT
def get_balance(self, asset='USDT'):
"""Mendapatkan saldo akun."""
try:
balance = [Link].fetch_balance()
return float(balance['free'][asset])
except Exception as e:
print(f"Error fetching balance: {e}")
return self.current_capital # Fallback
def calculate_position_size(self, current_price, atr_value,
trade_mode='normal', confidence_score=0.5, volatility_factor=1.0):
"""Menghitung ukuran posisi berdasarkan risiko tetap per posisi, mode
trade, dan confidence AI."""
if self.daily_risk_lock_triggered:
print("Risk lock harian telah terpicu. Tidak membuka posisi baru.")
return 0
account_balance = self.get_balance()
if account_balance is None:
account_balance = self.current_capital # Fallback
# Dynamic Risk Profiling per Coin: Sesuaikan risiko berdasarkan volatilitas
adjusted_risk_per_trade = self.current_max_risk_per_trade /
volatility_factor
# Mode Evaluasi Skor Prediksi AI: Semakin besar confidence, semakin besar
lot
# Scaling confidence (misal: 0.5-1.0 menjadi 1.0-2.0 multiplier)
confidence_multiplier = 1 + (confidence_score - 0.5) * 2 if
confidence_score > 0.5 else 1
risk_amount_usd = account_balance * adjusted_risk_per_trade *
confidence_multiplier
# Perkiraan Stop Loss dalam poin (berdasarkan ATR)
estimated_sl_points = atr_value * 1.5 # Misal: 1.5x ATR sebagai dasar SL
if estimated_sl_points == 0:
print("ATR sangat kecil, tidak bisa menghitung SL yang berarti.")
return 0
# Ukuran posisi = Risiko USD / (SL dalam poin * Harga)
risk_percentage_for_sl = estimated_sl_points / current_price
if risk_percentage_for_sl == 0: return 0
size_usd = risk_amount_usd / risk_percentage_for_sl
position_size = size_usd / current_price
# Mode Serangan Volatilitas Ekstrem
if trade_mode == 'extreme_attack':
position_size *= 2 # Lot lebih besar 2x dari biasa
# Pertimbangkan minimal tick size atau lot size dari bursa
# Anda perlu mengambil informasi market dari exchange
([Link][symbol])
# untuk mendapatkan min_amount, precision, dll.
print(f"Menghitung ukuran posisi: Saldo: {account_balance:.2f} USDT, Risiko
per trade: {risk_amount_usd:.2f} USD")
print(f"Mode: {trade_mode}, Confidence: {confidence_score:.2f}, Ukuran
posisi direkomendasikan: {position_size:.4f}")
return position_size
def open_position(self, symbol, side, size, entry_price, atr_value,
current_data, trade_mode='normal'):
"""Membuka posisi trading dengan SL/TP dinamis."""
if self.trades_today >= MAX_DAILY_TRADES:
print("Batasan overtrade harian tercapai.")
return None
if self.daily_risk_lock_triggered:
print("Risk lock harian telah terpicu. Tidak membuka posisi baru.")
return None
sl_offset = atr_value * 1.5 # SL sangat ketat berdasarkan ATR
tp_offset = atr_value * 3 # TP agresif (R:R fleksibel)
# Mode Serangan Volatilitas Ekstrem: SL lebih longgar, TP lebih agresif
if trade_mode == 'extreme_attack':
sl_offset = atr_value * 2.5 # SL lebih longgar
tp_offset = atr_value * 7 # TP lebih agresif (ATR x 7)
stop_loss_price = 0
take_profit_price = 0
if side == 'BUY':
stop_loss_price = entry_price - sl_offset
take_profit_price = entry_price + tp_offset
# Candle Lock-in SL: SL otomatis ditarik ke low candle momentum
sebelumnya.
if current_data['pin_bar'] == 'bullish_pin' or
current_data['engulfing'] == 'bullish':
stop_loss_price = min(stop_loss_price, current_data['low'] -
atr_value * 0.2)
elif side == 'SELL':
stop_loss_price = entry_price + sl_offset
take_profit_price = entry_price - tp_offset
if current_data['pin_bar'] == 'bearish_pin' or
current_data['engulfing'] == 'bearish':
stop_loss_price = max(stop_loss_price, current_data['high'] +
atr_value * 0.2)
# Mode Scalping Brutal: Auto TP 0.3-0.5%
if trade_mode == 'brutal_scalping':
if side == 'BUY':
take_profit_price = entry_price * (1 + 0.003) # 0.3% TP
elif side == 'SELL':
take_profit_price = entry_price * (1 - 0.003) # 0.3% TP
# Anda bisa membuat ini dinamis antara 0.3-0.5% berdasarkan volatilitas
atau confidence
# Pastikan SL/TP valid
if (side == 'BUY' and (stop_loss_price >= entry_price or take_profit_price
<= entry_price)) or \
(side == 'SELL' and (stop_loss_price <= entry_price or take_profit_price
>= entry_price)):
print("Stop Loss atau Take Profit tidak valid.")
return None
try:
order = [Link].create_order(symbol, 'market', [Link](),
size)
print(f"Posisi {side} {symbol} dengan size {size:.4f} dibuka pada harga
{entry_price:.2f}.")
[Link][symbol] = {
'side': side,
'entry_price': entry_price,
'size': size,
'sl': stop_loss_price,
'tp': take_profit_price,
'open_time': [Link](),
'initial_sl_price': stop_loss_price, # Simpan SL awal
'initial_tp_price': take_profit_price, # Simpan TP awal
'max_profit_seen': 0,
'max_drawdown': 0, # Inisialisasi max drawdown untuk posisi ini
'trade_mode': trade_mode # Simpan mode trade
}
self.trades_today += 1
return order
except Exception as e:
print(f"Gagal membuka posisi: {e}")
return None
def manage_positions(self, symbol, current_price, current_atr):
"""Mengelola posisi terbuka (trailing stop, monitoring SL/TP)."""
if symbol in [Link]:
pos = [Link][symbol]
# Hitung profit/loss aktual
current_profit_loss_abs = (current_price - pos['entry_price']) if
pos['side'] == 'BUY' else (pos['entry_price'] - current_price)
current_profit_loss_percent = current_profit_loss_abs /
pos['entry_price']
# Update max_profit_seen dan max_drawdown
if current_profit_loss_abs > pos['max_profit_seen']:
pos['max_profit_seen'] = current_profit_loss_abs
# Hitung drawdown relatif terhadap max_profit_seen
if pos['max_profit_seen'] > 0:
drawdown = (pos['max_profit_seen'] - current_profit_loss_abs) /
pos['max_profit_seen']
if drawdown > pos['max_drawdown']:
pos['max_drawdown'] = drawdown
# --- High-Precision Trailing TP Logic ---
# Dynamic ATR Trailing (geser SL saat profit > 1.8x ATR)
if pos['side'] == 'BUY':
if current_profit_loss_abs > current_atr * 1.8: # Profit > 1.8x ATR
new_sl = current_price - (current_atr * 1.0) # Geser SL ke 1.0x
ATR di bawah harga saat ini
if new_sl > pos['sl']: # Pastikan SL hanya bergerak ke arah
yang menguntungkan
pos['sl'] = new_sl
print(f"SL BUY {symbol} digeser ke {new_sl:.2f} (Dynamic
ATR Trailing).")
elif pos['side'] == 'SELL':
if current_profit_loss_abs > current_atr * 1.8:
new_sl = current_price + (current_atr * 1.0)
if new_sl < pos['sl']:
pos['sl'] = new_sl
print(f"SL SELL {symbol} digeser ke {new_sl:.2f} (Dynamic
ATR Trailing).")
# --- Cek SL/TP ---
should_close = False
if pos['side'] == 'BUY' and (current_price <= pos['sl'] or
current_price >= pos['tp']):
should_close = True
elif pos['side'] == 'SELL' and (current_price >= pos['sl'] or
current_price <= pos['tp']):
should_close = True
if should_close:
# Dapatkan market_features_at_entry jika perlu untuk RL.
# Ini harus disimpan saat `open_position`.
trade_result = self.close_position(symbol, pos['size'],
pos['side'], current_price)
if trade_result:
# Hitung R:R sebenarnya
entry = pos['entry_price']
sl = pos['initial_sl_price']
tp = pos['initial_tp_price']
if pos['side'] == 'BUY':
risk = entry - sl
reward = tp - entry
else: # SELL
risk = sl - entry
reward = entry - tp
trade_result['r_r'] = (reward / risk) if risk > 0 else 0
trade_result['max_drawdown'] = pos['max_drawdown']
# Panggil RL untuk belajar dari hasil trade
# Anda perlu menyimpan state (fitur pasar) saat entry untuk
ini.
# market_features_at_entry_example = {} # Perlu data aktual
# self.ai_ml_predictor.update_reinforcement_model(trade_result,
market_features_at_entry_example)
def close_position(self, symbol, size, side, current_price):
"""Menutup posisi trading."""
try:
close_side = 'sell' if side == 'BUY' else 'buy'
order = [Link].create_order(symbol, 'market', close_side, size)
profit_loss_usd = 0
if side == 'BUY':
profit_loss_usd = (current_price - [Link][symbol]
['entry_price']) * size
else: # SELL
profit_loss_usd = ([Link][symbol]['entry_price'] -
current_price) * size
self.daily_profit_loss += profit_loss_usd
self.current_capital += profit_loss_usd # Perbarui modal
# Auto Compound Micro-Win
if profit_loss_usd > 0:
compound_amount = profit_loss_usd * MICRO_COMPOUND_PERCENT
# Tingkatkan risiko per trade secara dinamis, tapi ada batasnya
# current_max_risk_per_trade akan direset setiap hari
# Ini contoh sederhana, harus lebih sophisticated agar tidak
terlalu agresif
self.current_max_risk_per_trade = min(MAX_RISK_PER_TRADE_PERCENT +
(compound_amount / INITIAL_CAPITAL_USD) * MAX_COMPOUND_RISK_INCREASE_PERCENT,
MAX_RISK_PER_TRADE_PERCENT *
1.5) # Batas max 1.5x risiko awal
print(f"Posisi {symbol} ditutup. P/L: {profit_loss_usd:.2f} USD. Modal
saat ini: {self.current_capital:.2f} USD.")
trade_result = {
'symbol': symbol,
'side': side,
'entry_price': [Link][symbol]['entry_price'],
'exit_price': current_price,
'profit_loss_usd': profit_loss_usd,
'duration': [Link]() - [Link][symbol]['open_time'],
'trade_mode': [Link][symbol]['trade_mode'],
'max_profit_seen': [Link][symbol]['max_profit_seen'],
'max_drawdown_ratio': [Link][symbol]['max_drawdown']
}
del [Link][symbol]
# Cek Risk Lock Harian
if self.daily_profit_loss < - (self.current_capital *
RISK_LOCK_DAILY_MAX_LOSS_PERCENT):
self.daily_risk_lock_triggered = True
print(f"!!! RISK LOCK TERPICU: Kerugian harian melebihi
{RISK_LOCK_DAILY_MAX_LOSS_PERCENT*100}% !!!")
print("Semua trading dihentikan hingga hari berikutnya.")
return trade_result
except Exception as e:
print(f"Gagal menutup posisi: {e}")
return None
# --- Kelas Coin Scanner ---
class CoinScanner:
def __init__(self, exchange_client):
[Link] = exchange_client
self.coin_performance = {} # Untuk Auto Symbol Rotation
def scan_active_coins(self, min_volume_usd=1_000_000,
bb_squeeze_threshold_percent=0.5):
"""Memindai koin aktif dengan volume tinggi dan potensi BB squeeze."""
active_symbols = []
try:
markets = [Link].load_markets()
symbols = [s for s in markets if markets[s]['spot'] is False and
[Link]('/USDT') and markets[s]['active']] # Hanya futures USDT pair dan aktif
for symbol in symbols:
# Auto Symbol Rotation via Performance: Keluarkan coin jika 3x loss
berturut-turut
if self.coin_performance.get(symbol, {'consecutive_losses': 0})
['consecutive_losses'] >= 3:
print(f"Melewatkan {symbol} karena 3x loss berturut-turut.")
continue
try:
# Ambil data 1h untuk analisis awal
ohlcv_1h = [Link].fetch_ohlcv(symbol, '1h', limit=100)
if not ohlcv_1h:
continue
df_1h = [Link](ohlcv_1h, columns=['timestamp', 'open',
'high', 'low', 'close', 'volume'])
df_1h['timestamp'] = pd.to_datetime(df_1h['timestamp'],
unit='ms')
df_1h.set_index('timestamp', inplace=True)
current_volume_usd = df_1h['volume'].iloc[-1] *
df_1h['close'].iloc[-1]
if current_volume_usd < min_volume_usd:
continue # Abaikan koin dengan volume rendah
# Cek Bollinger Band Width untuk potensi squeeze
bb_width = calculate_bollinger_bandwidth(df_1h['close'])
# Batasan: BB squeeze < 0.5% (lebih ketat untuk brutal
scalping)
if bb_width.iloc[-1] < bb_squeeze_threshold_percent: # BB
squeeze aktif (bandwidth rendah)
# Tambahkan cek spread/likuiditas (placeholder)
# spread = [Link].fetch_ticker(symbol)['ask'] -
[Link].fetch_ticker(symbol)['bid']
# if spread / [Link].fetch_ticker(symbol)['last'] >
0.002: # Spread > 0.2%
# print(f"Melewatkan {symbol}: likuiditas tipis (Spread
> 0.2%).")
# continue
active_symbols.append(symbol)
print(f"Koin aktif terdeteksi: {symbol} (Volume:
{current_volume_usd:.0f} USD, BB Width: {bb_width.iloc[-1]:.2f}%)")
except Exception as e:
print(f"Error saat memindai {symbol}: {e}")
continue
except Exception as e:
print(f"Error saat memuat pasar: {e}")
return active_symbols
def update_coin_performance(self, symbol, profit_loss_usd):
"""Memperbarui performa koin untuk rotasi otomatis."""
if symbol not in self.coin_performance:
self.coin_performance[symbol] = {'consecutive_losses': 0, 'total_pnl':
0}
self.coin_performance[symbol]['total_pnl'] += profit_loss_usd
if profit_loss_usd < 0:
self.coin_performance[symbol]['consecutive_losses'] += 1
else:
self.coin_performance[symbol]['consecutive_losses'] = 0
# --- Sistem Logging Real-time ---
class SystemLogger:
def __init__(self, db_client=None):
self.log_file = f"trading_log_{[Link]('%Y%m%d_%H%M%S')}.log"
[Link] = db_client # Firestore client
self.user_id = str(uuid.uuid4()) # ID pengguna unik untuk logging
# Tentukan lokasi penyimpanan data log di Firestore
self.firestore_collection_path =
f"artifacts/{APP_ID}/public/data/trade_logs"
self.firestore_public_path = f"artifacts/{APP_ID}/public/data"
print(f"Logging ke: {self.log_file}")
if [Link]:
print(f"Logging ke Firestore Collection:
{self.firestore_collection_path}")
def log(self, message, level='INFO'):
"""Menulis pesan ke file log dan (opsional) Firestore."""
timestamp = [Link]('%Y-%m-%d %H:%M:%S')
log_entry = f"[{timestamp}] [{level}] {message}"
print(log_entry) # Cetak ke konsol juga
with open(self.log_file, 'a') as f:
[Link](log_entry + '\n')
if [Link]:
try:
doc_ref =
[Link](self.firestore_collection_path).document()
doc_ref.set({
'timestamp': firestore.SERVER_TIMESTAMP,
'level': level,
'message': message,
'user_id': self.user_id # ID pengguna untuk identifikasi
})
except Exception as e:
print(f"Error writing log to Firestore: {e}")
def log_trade_result(self, trade_data):
"""Mencatat hasil trading secara terstruktur."""
[Link](f"TRADE RESULT: {[Link](trade_data)}", level='TRADE')
if [Link]:
try:
doc_ref =
[Link](self.firestore_collection_path).document()
trade_data['timestamp'] = firestore.SERVER_TIMESTAMP
trade_data['user_id'] = self.user_id
doc_ref.set(trade_data)
except Exception as e:
print(f"Error logging trade result to Firestore: {e}")
def update_dashboard_data(self, metrics):
"""Memperbarui metrik dashboard di Firestore (atau database lain)."""
if [Link]:
try:
dashboard_ref =
[Link](self.firestore_public_path).document('dashboard_metrics')
# Menggunakan set(..., merge=True) untuk memperbarui atau membuat
dokumen
dashboard_ref.set(metrics, merge=True)
[Link](f"Dashboard metrics updated: {metrics}")
except Exception as e:
[Link](f"Error updating dashboard metrics: {e}", level='ERROR')
# --- Kelas Utama Sistem Trading Algoritmik ---
class AlgorithmicTradingSystem:
def __init__(self):
print("Menginisialisasi Sistem Trading Algoritmik Elite...")
[Link] = [Link]({
'apiKey': API_KEY,
'secret': SECRET_KEY,
'options': {
'defaultType': 'future',
},
'enableRateLimit': True, # Pastikan tidak melebihi rate limit API
})
# Verifikasi koneksi
try:
[Link].load_markets()
print(f"Terhubung ke {EXCHANGE_ID} berhasil.")
except Exception as e:
print(f"Gagal terhubung ke {EXCHANGE_ID}: {e}")
# exit() # Tidak exit di sini agar bisa di-deploy meskipun ada masalah
koneksi awal
self.market_analyzer = MarketAnalyzer()
self.ai_ml_predictor = AIMLPredictor()
self.trade_executor = TradeExecutor([Link], INITIAL_CAPITAL_USD)
self.coin_scanner = CoinScanner([Link])
[Link] = SystemLogger(db_client=db)
[Link] = False
self.active_trading_symbols = [] # Simbol yang sedang dipantau dan
ditradingkan
self.market_features_at_entry = {} # Simpan fitur saat entry untuk RL
def _initial_setup(self):
"""Setup awal dan pemuatan model."""
import os
if not [Link]('models'):
[Link]('models')
[Link]("Melakukan setup awal dan memuat/melatih model AI/ML...")
# Data dummy untuk pelatihan awal jika tidak ada model yang tersimpan
# Pastikan dummy_df memiliki semua kolom yang dibutuhkan oleh
MarketAnalyzer dan AIMLPredictor
dummy_data_length = 200 # Perlu lebih banyak data untuk pelatihan
dummy_df_base = [Link]([Link](dummy_data_length, 6),
columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
dummy_df_base['timestamp'] = pd.to_datetime(dummy_df_base['timestamp'],
unit='ms')
dummy_df_base.set_index('timestamp', inplace=True)
# Tambahkan kolom yang akan diisi oleh apply_indicators dan
calculate_auto_features
for col in ['engulfing', 'inside_bar', 'pin_bar', 'fractal_high',
'fractal_low', 'is_breakout_up', 'is_breakout_down',
'is_fakeout_up', 'is_fakeout_down', 'supertrend', 'cvd',
'funding_rate', 'long_short_ratio',
'open_interest_delta', 'ema_cross_ratio_9_21',
'ema_cross_ratio_21_50', 'atr_to_range_ratio',
'qqe_mod_derivative', 'body_percent', 'upper_shadow_percent',
'lower_shadow_percent',
'volume_spike', 'volume_spike_extreme']:
if col not in dummy_df_base.columns:
dummy_df_base[col] = [Link] # Inisialisasi dengan NaN, akan diisi
sebagian oleh apply_indicators
# Hitung beberapa indikator dasar pada dummy data agar tidak kosong saat
training
dummy_df_base['atr'] = calculate_atr(dummy_df_base['high'],
dummy_df_base['low'], dummy_df_base['close'])
dummy_df_base['bb_upper'], dummy_df_base['bb_lower'], _, _ =
calculate_bollinger_bands(dummy_df_base['close'])
dummy_df_base['bb_width'] =
calculate_bollinger_bandwidth(dummy_df_base['close'])
dummy_df_base['keltner_upper'], dummy_df_base['keltner_lower'], _ =
calculate_keltner_channels(dummy_df_base['high'], dummy_df_base['low'],
dummy_df_base['close'])
dummy_df_base['ema9'] = calculate_ema(dummy_df_base['close'], 9)
dummy_df_base['ema21'] = calculate_ema(dummy_df_base['close'], 21)
dummy_df_base['ema50'] = calculate_ema(dummy_df_base['close'], 50)
dummy_df_base['ema200'] = calculate_ema(dummy_df_base['close'], 200)
dummy_df_base['qqe_mod'] = calculate_qqe_mod(dummy_df_base['close'])
dummy_df_base = calculate_auto_features(dummy_df_base) # Hitung fitur
AutoFE
# Data yang dianalisis untuk pelatihan tree models (sederhana dari
dummy_df_base)
dummy_analyzed_data = {
'1m': dummy_df_base.copy(),
'5m': dummy_df_base.resample('5T').agg({col: 'last' if col not in
['high', 'low', 'volume'] else 'max' if col == 'high' else 'min' if col == 'low'
else 'sum' for col in dummy_df_base.columns if col != 'timestamp'}).dropna(),
'1h': dummy_df_base.resample('H').agg({col: 'last' if col not in
['high', 'low', 'volume'] else 'max' if col == 'high' else 'min' if col == 'low'
else 'sum' for col in dummy_df_base.columns if col != 'timestamp'}).dropna()
}
# Hitung ulang indikator pada data resampled
for interval, df in dummy_analyzed_data.items():
if not [Link] and 'high' in [Link] and 'low' in [Link] and
'close' in [Link]:
df['atr'] = calculate_atr(df['high'], df['low'], df['close'])
df['bb_width'] = calculate_bollinger_bandwidth(df['close'])
df['qqe_mod'] = calculate_qqe_mod(df['close'])
df['ema9'] = calculate_ema(df['close'], 9)
df['ema21'] = calculate_ema(df['close'], 21)
df['ema50'] = calculate_ema(df['close'], 50)
df['ema200'] = calculate_ema(df['close'], 200)
df = calculate_auto_features(df) # Recalculate AutoFE features
dummy_analyzed_data[interval] = df
# Panggil train_lstm dan train_tree_models dengan data dummy jika model
tidak ada
if self.ai_ml_predictor.lstm_model is None:
self.ai_ml_predictor.train_lstm(dummy_df_base)
if self.ai_ml_predictor.transformer_model is None:
self.ai_ml_predictor.train_transformer(dummy_df_base) # Latih
Transformer
if self.ai_ml_predictor.xgboost_model is None or
self.ai_ml_predictor.lightgbm_model is None:
self.ai_ml_predictor.train_tree_models(dummy_analyzed_data)
[Link]("Setup awal selesai. Sistem siap beroperasi.")
def start(self):
"""Memulai sistem trading otomatis."""
[Link] = True
[Link]("Sistem Algorithmic Trading Elite dimulai. Beroperasi
24/7.")
self._initial_setup()
while [Link]:
try:
current_hour = [Link]().tm_hour
current_minute = [Link]().tm_min
# 1. Coin Scanning (setiap jam)
if current_minute == 0: # Hanya jalankan di awal jam
self.active_trading_symbols =
self.coin_scanner.scan_active_coins(bb_squeeze_threshold_percent=0.5) # Batas BB
squeeze lebih ketat
[Link](f"Simbol aktif yang dipindai:
{self.active_trading_symbols}")
# Reset daily stats (opsional, tergantung definisi "harian")
if current_hour == 0 and current_minute == 0: # Setiap
pergantian hari (00:00)
self.trade_executor.daily_profit_loss = 0
self.trade_executor.trades_today = 0
self.trade_executor.daily_risk_lock_triggered = False
self.trade_executor.current_max_risk_per_trade =
MAX_RISK_PER_TRADE_PERCENT # Reset compounding
[Link]("Statistik harian direset.")
if not self.active_trading_symbols:
[Link]("Tidak ada simbol aktif yang ditemukan.
Menunggu pemindaian berikutnya...")
[Link](60) # Tunggu 1 menit sebelum cek lagi
continue
# Integrasi Layer Proteksi Cerdas (cek kondisi pasar umum)
# Placeholder: Cek BTC dominance sideways (low ATR)
# Anda perlu mengambil data BTC dan menghitung ATR-nya.
# if self._is_btc_sideways():
# [Link]("BTC sideways (ATR rendah). Tidak trading
untuk saat ini.", level='WARNING')
# [Link](60)
# continue
# 2. Loop Trading Utama untuk setiap simbol aktif
for symbol in list(self.active_trading_symbols): # Gunakan list
copy karena bisa dimodifikasi saat iterasi
# Ambil dan analisis data multi-timeframe
analyzed_data = self.market_analyzer.fetch_data([Link],
symbol)
self.market_analyzer.apply_indicators()
if '1m' not in analyzed_data or analyzed_data['1m'].empty:
[Link](f"Tidak ada data 1m untuk {symbol}.
Melewatkan.")
continue
current_price = analyzed_data['1m']['close'].iloc[-1]
current_atr = analyzed_data['1m']['atr'].iloc[-1]
current_1m_data = analyzed_data['1m'].iloc[-1] # Data terakhir
1m untuk detail
# Simpan fitur saat ini untuk RL (digunakan saat entry)
# Ini adalah representasi state
self.market_features_at_entry[symbol] = analyzed_data # Simpan
seluruh data yang dianalisis
# Kelola posisi yang ada terlebih dahulu
self.trade_executor.manage_positions(symbol, current_price,
current_atr)
if symbol in self.trade_executor.positions:
[Link](f"Posisi sudah terbuka untuk {symbol}.
Menunggu penutupan atau trailing stop.")
continue
# Anti-Overtrade Per Hari
if self.trade_executor.trades_today >= MAX_DAILY_TRADES:
[Link](f"Batasan overtrade harian
({MAX_DAILY_TRADES}) tercapai. Tidak membuka posisi baru untuk {symbol}.")
continue
if self.trade_executor.daily_risk_lock_triggered:
[Link](f"Risk lock harian terpicu. Tidak membuka
posisi baru untuk {symbol}.")
continue
# Prediksi AI/ML & Pengambilan Keputusan
lstm_direction =
self.ai_ml_predictor.predict_lstm(analyzed_data['1m'])
transformer_direction =
self.ai_ml_predictor.predict_transformer(analyzed_data['1m']) # Prediksi
Transformer
decision, confidence_scores =
self.ai_ml_predictor.make_decision(analyzed_data, lstm_direction,
transformer_direction)
[Link](f"Symbol: {symbol}, Current Price:
{current_price:.2f}, LSTM Pred: {lstm_direction}, Transformer Pred:
{transformer_direction}, Decision: {decision}, Confidence:
{confidence_scores['combined_conf']:.2f}")
trade_mode = 'normal'
# Deteksi "Mode Serangan Volatilitas Ekstrem" atau "Mode
Scalping Brutal"
if current_1m_data['volume_spike_extreme'] and
current_1m_data['bb_width'] < 0.5: # BB squeeze sangat ketat
trade_mode = 'extreme_attack'
[Link](f"Mode Serangan Volatilitas Ekstrem
diaktifkan untuk {symbol}!")
elif current_1m_data['bb_width'] < 0.5 and
current_1m_data['volume_spike'] and \
current_1m_data['body_percent'] > 0.7: # Body dominan >
70%
trade_mode = 'brutal_scalping'
[Link](f"Mode Scalping Brutal diaktifkan untuk
{symbol}!")
if decision in ['BUY', 'SELL']:
# Validasi multi-konfirmasi (ini adalah pseudo-code yang
harus dikembangkan)
is_valid_entry =
self._validate_multi_confirmation(analyzed_data, decision, current_1m_data)
if is_valid_entry:
# Dynamic Risk Profiling per Coin: Lewatkan faktor
volatilitas (misal: ATR sebagai proksi)
volatility_factor = max(1.0, current_atr /
analyzed_data['1h']['atr'].mean()) # Volatilitas relatif terhadap H1
position_size =
self.trade_executor.calculate_position_size(current_price, current_atr,
trade_mode=trade_mode,
confidence_score=confidence_scores['combined_conf'],
volatility_factor=volatility_factor)
if position_size > 0:
self.trade_executor.open_position(symbol, decision,
position_size, current_price, current_atr, current_1m_data, trade_mode=trade_mode)
[Link](f"ENTRY SIGNAL: {decision} {symbol}
di {current_price:.2f} dengan size {position_size:.4f} (Mode: {trade_mode}, Conf:
{confidence_scores['combined_conf']:.2f})")
# Simpan market features saat entry untuk RL
self.market_features_at_entry[symbol] =
analyzed_data
else:
[Link](f"Tidak bisa membuka posisi untuk
{symbol}: ukuran posisi nol atau risk lock.")
else:
[Link](f"Sinyal {decision} untuk {symbol}
ditolak: gagal validasi multi-konfirmasi.")
else:
[Link](f"Decision for {symbol}: {decision}. No
trade taken.")
# Auto Symbol Rotation: Update performa koin setelah potensi
trade
self.coin_scanner.update_coin_performance(symbol,
self.trade_executor.daily_profit_loss - self.trade_executor.last_day_pnl_recorded)
# Contoh, butuh last_day_pnl_recorded
# Perbarui dashboard metrik secara berkala
[Link].update_dashboard_data({
'current_capital': self.trade_executor.current_capital,
'daily_profit_loss': self.trade_executor.daily_profit_loss,
'trades_today': self.trade_executor.trades_today,
'risk_lock_active':
self.trade_executor.daily_risk_lock_triggered,
'active_symbols': len(self.active_trading_symbols),
'total_positions_open': len(self.trade_executor.positions),
'timestamp': [Link](),
# Untuk dashboard UI/UX: Anda bisa tambahkan data historis P/L,
performa terbaik, dll.
# Contoh: 'daily_pnl_history': [list of daily PnLs]
# 'best_performing_coin': symbol_name
})
# Jeda singkat sebelum iterasi berikutnya (sesuaikan untuk scalping
M1)
# Untuk M1 scalping, ini harus mendekati 1 detik.
[Link](5) # Memberi sedikit ruang untuk API rate limit dan
komputasi
except [Link] as e:
[Link](f"Kesalahan jaringan CCXT: {e}. Mencoba lagi...",
level='ERROR')
[Link](30)
except [Link] as e:
[Link](f"Kesalahan bursa CCXT: {e}. Periksa
kredensial/status API.", level='CRITICAL')
[Link](60)
except Exception as e:
[Link](f"Kesalahan tak terduga: {e}", level='CRITICAL')
[Link](60)
def stop(self):
"""Menghentikan sistem trading."""
[Link] = False
[Link]("Sistem Algorithmic Trading Elite dihentikan.")
for symbol in list(self.trade_executor.[Link]()):
pos = self.trade_executor.positions[symbol]
# Fetch harga terakhir untuk penutupan
try:
last_price = [Link].fetch_ticker(symbol)['last']
trade_result = self.trade_executor.close_position(symbol,
pos['size'], pos['side'], last_price)
if trade_result and symbol in self.market_features_at_entry:
self.ai_ml_predictor.update_reinforcement_model(trade_result,
self.market_features_at_entry[symbol])
self.coin_scanner.update_coin_performance(symbol,
trade_result['profit_loss_usd'])
except Exception as e:
[Link](f"Gagal menutup posisi {symbol} saat shutdown:
{e}", level='ERROR')
[Link]("Semua posisi ditutup.")
# Placeholder untuk fungsi deteksi BTC sideways
def _is_btc_sideways(self):
"""
Cek apakah BTC sedang sideways (misal: ATR rendah relatif terhadap rata-
rata atau BB width rendah).
Membutuhkan pengambilan data BTC secara terpisah.
"""
# Pseudo-code:
# btc_ohlcv = [Link].fetch_ohlcv('BTC/USDT', '1h', limit=50)
# btc_df = [Link](btc_ohlcv, columns=['timestamp', 'open', 'high',
'low', 'close', 'volume'])
# btc_atr = calculate_atr(btc_df['high'], btc_df['low'],
btc_df['close']).iloc[-1]
# btc_bb_width = calculate_bollinger_bandwidth(btc_df['close']).iloc[-1]
# if btc_atr < (avg_btc_atr * 0.5) or btc_bb_width < 0.008: # Misal: 0.8%
BB width
# return True
return False # Contoh
def _validate_multi_confirmation(self, analyzed_data, decision,
current_1m_data):
"""
Logika validasi multi-konfirmasi (minimal 3 layer) yang lebih ketat.
"""
[Link](f"Memulai validasi multi-konfirmasi untuk {decision}...")
df_1m = analyzed_data.get('1m')
df_5m = analyzed_data.get('5m')
df_1h = analyzed_data.get('1h')
if df_1m.empty or df_5m.empty or df_1h.empty:
[Link]("Data multi-timeframe tidak lengkap untuk validasi.",
level='WARNING')
return False
# --- Layer 1: Price Action M1 & Volatilitas & AutoFE ---
pa_confirmation = False
if decision == 'BUY':
if (current_1m_data['engulfing'] == 'bullish' or
current_1m_data['pin_bar'] == 'bullish_pin') and \
current_1m_data['qqe_mod'] > 50 and
current_1m_data['qqe_mod_derivative'] > 0: # QQE Mod & momentum
pa_confirmation = True
if current_1m_data['is_breakout_up'] and
current_1m_data['volume_spike'] and current_1m_data['body_percent'] > 0.6: #
Breakout kuat
pa_confirmation = True
elif decision == 'SELL':
if (current_1m_data['engulfing'] == 'bearish' or
current_1m_data['pin_bar'] == 'bearish_pin') and \
current_1m_data['qqe_mod'] < 50 and
current_1m_data['qqe_mod_derivative'] < 0:
pa_confirmation = True
if current_1m_data['is_breakout_down'] and
current_1m_data['volume_spike'] and current_1m_data['body_percent'] > 0.6:
pa_confirmation = True
if not pa_confirmation:
[Link]("Layer 1 (Price Action M1 & Volatilitas) FAILED.",
level='DEBUG')
return False
[Link]("Layer 1 (Price Action M1 & Volatilitas) PASSED.",
level='DEBUG')
# --- Layer 2: Kekuatan di M5 (EMA & QQE Mod & S/R dinamis) ---
m5_confirmation = False
last_m5 = df_5m.iloc[-1]
if decision == 'BUY':
# EMA M5 Bullish crossover (EMA9 > EMA21) AND EMA21 > EMA50 AND QQE Mod
di atas 50
if last_m5['ema9'] > last_m5['ema21'] and last_m5['ema21'] >
last_m5['ema50'] and last_m5['qqe_mod'] > 50:
m5_confirmation = True
# Harga mendekati Support kuat M5
# if last_m5['close'] > (last_m5['fractal_low'] - last_m5['atr'] * 0.5)
and last_m5['close'] < last_m5['fractal_low']:
# m5_confirmation = True # Harga memantul dari support
elif decision == 'SELL':
if last_m5['ema9'] < last_m5['ema21'] and last_m5['ema21'] <
last_m5['ema50'] and last_m5['qqe_mod'] < 50:
m5_confirmation = True
# if last_m5['close'] < (last_m5['fractal_high'] + last_m5['atr'] *
0.5) and last_m5['close'] > last_m5['fractal_high']:
# m5_confirmation = True # Harga memantul dari resistance
if not m5_confirmation:
[Link]("Layer 2 (Kekuatan M5) FAILED.", level='DEBUG')
return False
[Link]("Layer 2 (Kekuatan M5) PASSED.", level='DEBUG')
# --- Layer 3: Arah Dominan H1 & Filter Volume/Order Flow & Anti-Choppy ---
h1_confirmation = False
last_h1 = df_1h.iloc[-1]
# Anti-Choppy: Cek "fakeout" 3x dalam 15 menit terakhir di M1 (contoh).
# Ini akan menjadi fungsi terpisah yang menganalisis historis
`is_fakeout_up`/`is_fakeout_down`
if self._detect_recent_fakeouts(df_1m.tail(15)): # Cek 15 bar terakhir di
M1
[Link]("Terlalu banyak fakeout baru-baru ini. Menghindari
pasar choppy.", level='WARNING')
return False
if decision == 'BUY':
# Harga H1 di atas EMA200 H1 (tren naik) DAN volume spike di M1 DAN CVD
menunjukkan akumulasi (placeholder)
if last_h1['close'] > last_h1['ema200'] and
current_1m_data['volume_spike']: # Gunakan volume spike dari M1
h1_confirmation = True
# Logika CVD: akumulasi (placeholder)
# if current_1m_data['cvd'] > 0 and
self._detect_bullish_volume_divergence(analyzed_data): h1_confirmation = True
elif decision == 'SELL':
if last_h1['close'] < last_h1['ema200'] and
current_1m_data['volume_spike']:
h1_confirmation = True
# Logika CVD: distribusi (placeholder)
# if current_1m_data['cvd'] < 0 and
self._detect_bearish_volume_divergence(analyzed_data): h1_confirmation = True
if not h1_confirmation:
[Link]("Layer 3 (Arah H1 & Volume Filter) FAILED.",
level='DEBUG')
return False
[Link]("Layer 3 (Arah H1 & Volume Filter) PASSED.", level='DEBUG')
[Link]("Semua 3 lapisan validasi konfirmasi berhasil!",
level='INFO')
return True
def _detect_recent_fakeouts(self, df):
"""
Deteksi apakah ada terlalu banyak fakeout dalam periode tertentu.
Misal, jika ada lebih dari 2 fakeout dalam 15 bar terakhir.
"""
if 'is_fakeout_up' not in [Link] or 'is_fakeout_down' not in
[Link]:
return False
fakeout_count = (df['is_fakeout_up'] | df['is_fakeout_down']).sum()
return fakeout_count >= 2 # Jika 2 atau lebih fakeout dalam 15 bar
def _detect_bullish_volume_divergence(self, analyzed_data):
"""Placeholder untuk deteksi divergensi volume bullish."""
# Logika: Harga membuat lower low, Volume membuat higher low
# Membutuhkan analisis pergerakan harga dan volume secara terkoordinasi
return True
def _detect_bearish_volume_divergence(self, analyzed_data):
"""Placeholder untuk deteksi divergensi volume bearish."""
# Logika: Harga membuat higher high, Volume membuat lower high
return True
# --- Main Execution Block ---
if __name__ == "__main__":
# Ini akan menjadi titik masuk utama saat sistem dijalankan.
# Pastikan untuk menginstal dependensi: pip install ccxt pandas numpy
tensorflow scikit-learn xgboost lightgbm firebase-admin
# Inisialisasi sistem
system = AlgorithmicTradingSystem()
try:
[Link]()
except KeyboardInterrupt:
[Link]()
print("Sistem dihentikan secara manual.")
except Exception as e:
[Link](f"Sistem mengalami kesalahan fatal: {e}",
level='CRITICAL')
[Link]()