0% found this document useful (0 votes)
112 views29 pages

Crypto Trading Bot with Technical Analysis

This document outlines a trading system implementation using Python, incorporating libraries such as ccxt, pandas, and TensorFlow for data analysis and machine learning. It includes configurations for API credentials, trading constants, and various technical indicators for market analysis. Additionally, it features classes for market data fetching, indicator application, and AI/ML prediction, along with utility functions for calculating trading indicators and features.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
112 views29 pages

Crypto Trading Bot with Technical Analysis

This document outlines a trading system implementation using Python, incorporating libraries such as ccxt, pandas, and TensorFlow for data analysis and machine learning. It includes configurations for API credentials, trading constants, and various technical indicators for market analysis. Additionally, it features classes for market data fetching, indicator application, and AI/ML prediction, along with utility functions for calculating trading indicators and features.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

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]()

You might also like