Initial Commit
This commit is contained in:
parent
da50b667da
commit
16c603bacf
48 changed files with 1752 additions and 0 deletions
0
agents/lstm_agent.py
Normal file
0
agents/lstm_agent.py
Normal file
BIN
data/raw/DAT_XLSX_EURUSD_M1_2021.xlsx
Normal file
BIN
data/raw/DAT_XLSX_EURUSD_M1_2021.xlsx
Normal file
Binary file not shown.
BIN
data/raw/DAT_XLSX_EURUSD_M1_2022.xlsx
Normal file
BIN
data/raw/DAT_XLSX_EURUSD_M1_2022.xlsx
Normal file
Binary file not shown.
BIN
data/raw/DAT_XLSX_EURUSD_M1_2023.xlsx
Normal file
BIN
data/raw/DAT_XLSX_EURUSD_M1_2023.xlsx
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12000.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12000.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12001.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12001.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12002.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12002.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12003.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12003.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12004.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12004.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12005.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12005.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12006.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12006.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12007.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12007.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12008.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12008.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12009.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12009.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12010.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12010.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12011.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12011.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12012.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12012.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12013.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12013.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12014.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12014.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12015.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12015.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12016.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12016.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12017.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12017.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12018.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12018.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12019.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12019.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12020.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12020.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12021.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12021.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12022.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12022.zip
Normal file
Binary file not shown.
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12023.zip
Normal file
BIN
data/zips/EURUSD/HISTDATA_COM_XLSX_EURUSD_M12023.zip
Normal file
Binary file not shown.
0
docker/DockerFile
Normal file
0
docker/DockerFile
Normal file
0
env/trading_env.py
vendored
Normal file
0
env/trading_env.py
vendored
Normal file
104
generate_perfect_trades.py
Normal file
104
generate_perfect_trades.py
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Load 2022 EURUSD 1m data
|
||||||
|
df = pd.read_excel("data/DAT_XLSX_EURUSD_M1_2021.xlsx", header=None, names=[
|
||||||
|
'timestamp', 'open', 'high', 'low', 'close', 'volume'
|
||||||
|
])
|
||||||
|
df['timestamp'] = pd.to_datetime(df['timestamp'])
|
||||||
|
df.set_index('timestamp', inplace=True)
|
||||||
|
|
||||||
|
# Resample to 15-minute intervals
|
||||||
|
df = df.resample('15min').agg({
|
||||||
|
'open': 'first',
|
||||||
|
'high': 'max',
|
||||||
|
'low': 'min',
|
||||||
|
'close': 'last',
|
||||||
|
'volume': 'sum'
|
||||||
|
}).dropna()
|
||||||
|
|
||||||
|
# Create perfect actions
|
||||||
|
lookahead = 4 # ~1 hour
|
||||||
|
profit_threshold = 0.001
|
||||||
|
loss_threshold = 0.001
|
||||||
|
|
||||||
|
labels = []
|
||||||
|
for i in range(len(df) - lookahead):
|
||||||
|
current_close = df.iloc[i]['close']
|
||||||
|
future_window = df.iloc[i + 1:i + 1 + lookahead]
|
||||||
|
max_future_price = future_window['high'].max()
|
||||||
|
min_future_price = future_window['low'].min()
|
||||||
|
|
||||||
|
if max_future_price >= current_close * (1 + profit_threshold):
|
||||||
|
labels.append(1)
|
||||||
|
elif min_future_price <= current_close * (1 - loss_threshold):
|
||||||
|
labels.append(-1)
|
||||||
|
else:
|
||||||
|
labels.append(0)
|
||||||
|
|
||||||
|
labels += [0] * lookahead
|
||||||
|
df['perfect_action'] = labels
|
||||||
|
|
||||||
|
# Simulate perfect trader
|
||||||
|
balance = 10000.0
|
||||||
|
position = 0.0
|
||||||
|
entry_price = 0.0
|
||||||
|
win_count = 0
|
||||||
|
loss_count = 0
|
||||||
|
trades_count = 0
|
||||||
|
trades = []
|
||||||
|
|
||||||
|
for i in range(len(df)):
|
||||||
|
price = df.iloc[i]['close']
|
||||||
|
action = df.iloc[i]['perfect_action']
|
||||||
|
|
||||||
|
if action == 1 and position == 0:
|
||||||
|
position = balance / price
|
||||||
|
entry_price = price
|
||||||
|
trades.append({
|
||||||
|
"timestamp": df.index[i],
|
||||||
|
"action": "buy",
|
||||||
|
"price": price,
|
||||||
|
"balance": balance
|
||||||
|
})
|
||||||
|
balance = 0
|
||||||
|
trades_count += 1
|
||||||
|
|
||||||
|
elif action == -1 and position > 0:
|
||||||
|
exit_value = position * price
|
||||||
|
pnl = exit_value - (position * entry_price)
|
||||||
|
if pnl > 0:
|
||||||
|
win_count += 1
|
||||||
|
else:
|
||||||
|
loss_count += 1
|
||||||
|
balance = exit_value
|
||||||
|
trades.append({
|
||||||
|
"timestamp": df.index[i],
|
||||||
|
"action": "sell",
|
||||||
|
"price": price,
|
||||||
|
"balance": balance
|
||||||
|
})
|
||||||
|
position = 0
|
||||||
|
entry_price = 0
|
||||||
|
|
||||||
|
# Finalize any open position
|
||||||
|
if position > 0:
|
||||||
|
balance = position * df.iloc[-1]['close']
|
||||||
|
|
||||||
|
# Results
|
||||||
|
print("\n📊 Perfect Trader Simulation:")
|
||||||
|
print(f"🟢 Starting Balance: $10,000.00")
|
||||||
|
print(f"🔚 Ending Balance: ${balance:,.2f}")
|
||||||
|
print(f"💼 Total Trades: {trades_count}")
|
||||||
|
print(f"✅ Wins: {win_count} | ❌ Losses: {loss_count}")
|
||||||
|
if trades_count > 0:
|
||||||
|
print(f"📈 Win Rate: {win_count / trades_count * 100:.2f}%")
|
||||||
|
|
||||||
|
# Save trade log
|
||||||
|
output_path = "ml/perfect_trades.csv"
|
||||||
|
os.makedirs("ml", exist_ok=True)
|
||||||
|
|
||||||
|
trades_df = pd.DataFrame(trades)
|
||||||
|
trades_df.to_csv(output_path, index=False)
|
||||||
|
print(f"📁 Perfect trades saved to: {output_path}")
|
||||||
8
main.py
Normal file
8
main.py
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
from backtest import run_backtest
|
||||||
|
from ai_backtest import run_ai_backtest
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# run_backtest() # ← SMA strategy
|
||||||
|
run_ai_backtest() # ← AI model strategy
|
||||||
|
|
||||||
0
ml/__init__.py
Normal file
0
ml/__init__.py
Normal file
BIN
ml/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
ml/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
ml/__pycache__/preprocessing.cpython-311.pyc
Normal file
BIN
ml/__pycache__/preprocessing.cpython-311.pyc
Normal file
Binary file not shown.
BIN
ml/__pycache__/train_mlp.cpython-311.pyc
Normal file
BIN
ml/__pycache__/train_mlp.cpython-311.pyc
Normal file
Binary file not shown.
19
ml/feature_names.txt
Normal file
19
ml/feature_names.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
open
|
||||||
|
high
|
||||||
|
low
|
||||||
|
close
|
||||||
|
volume
|
||||||
|
sma_10
|
||||||
|
sma_30
|
||||||
|
rsi_14
|
||||||
|
momentum
|
||||||
|
price_delta
|
||||||
|
vol_rolling
|
||||||
|
bollinger_b
|
||||||
|
macd
|
||||||
|
day_of_week
|
||||||
|
hour
|
||||||
|
minute
|
||||||
|
sim_balance
|
||||||
|
buy_amount
|
||||||
|
pnl
|
||||||
0
ml/models/__init__.py
Normal file
0
ml/models/__init__.py
Normal file
BIN
ml/models/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
ml/models/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
ml/models/__pycache__/forex_mlp.cpython-311.pyc
Normal file
BIN
ml/models/__pycache__/forex_mlp.cpython-311.pyc
Normal file
Binary file not shown.
BIN
ml/models/forex_mlp.pt
Normal file
BIN
ml/models/forex_mlp.pt
Normal file
Binary file not shown.
16
ml/models/forex_mlp.py
Normal file
16
ml/models/forex_mlp.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
import torch.nn.functional as F
|
||||||
|
|
||||||
|
class ForexMLP(nn.Module):
|
||||||
|
def __init__(self, input_size):
|
||||||
|
super(ForexMLP, self).__init__()
|
||||||
|
self.fc1 = nn.Linear(input_size, 64)
|
||||||
|
self.fc2 = nn.Linear(64, 32)
|
||||||
|
self.out = nn.Linear(32, 2) # [buy_score, sell_score]
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
x = F.relu(self.fc1(x))
|
||||||
|
x = F.relu(self.fc2(x))
|
||||||
|
x = torch.sigmoid(self.out(x)) # We want probabilities between 0-1
|
||||||
|
return x
|
||||||
1371
ml/perfect_trades.csv
Normal file
1371
ml/perfect_trades.csv
Normal file
File diff suppressed because it is too large
Load diff
65
ml/preprocessing.py
Normal file
65
ml/preprocessing.py
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
# ml/preprocessing.py
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def load_and_preprocess_data(path):
|
||||||
|
df = pd.read_excel(path, header=None, names=[
|
||||||
|
'timestamp', 'open', 'high', 'low', 'close', 'volume'
|
||||||
|
])
|
||||||
|
df['timestamp'] = pd.to_datetime(df['timestamp'])
|
||||||
|
df.set_index('timestamp', inplace=True)
|
||||||
|
|
||||||
|
# Resample to 15-minute intervals
|
||||||
|
df = df.resample('15min').agg({
|
||||||
|
'open': 'first',
|
||||||
|
'high': 'max',
|
||||||
|
'low': 'min',
|
||||||
|
'close': 'last',
|
||||||
|
'volume': 'sum'
|
||||||
|
}).dropna()
|
||||||
|
|
||||||
|
# Add features
|
||||||
|
df['sma_10'] = df['close'].rolling(10).mean()
|
||||||
|
df['sma_30'] = df['close'].rolling(30).mean()
|
||||||
|
df['rsi_14'] = 100 - (100 / (1 + df['close'].pct_change().add(1).rolling(14).mean()))
|
||||||
|
df['momentum'] = df['close'] - df['close'].shift(4)
|
||||||
|
df['price_delta'] = df['close'] - df['open']
|
||||||
|
df['vol_rolling'] = df['volume'].rolling(10).mean()
|
||||||
|
|
||||||
|
# Bollinger %B
|
||||||
|
rolling_mean = df['close'].rolling(20).mean()
|
||||||
|
rolling_std = df['close'].rolling(20).std()
|
||||||
|
df['bollinger_b'] = (df['close'] - rolling_mean) / (2 * rolling_std)
|
||||||
|
|
||||||
|
# MACD
|
||||||
|
ema12 = df['close'].ewm(span=12, adjust=False).mean()
|
||||||
|
ema26 = df['close'].ewm(span=26, adjust=False).mean()
|
||||||
|
df['macd'] = ema12 - ema26
|
||||||
|
|
||||||
|
# Timestamp-based features
|
||||||
|
df['hour'] = df.index.hour
|
||||||
|
df['weekday'] = df.index.weekday
|
||||||
|
|
||||||
|
# Simulated portfolio balance and buy-in value (placeholders for now)
|
||||||
|
df['balance'] = 10000.0 # Placeholder: could be dynamic in real-time
|
||||||
|
df['buy_in'] = df['close'].shift(1) # Simulated buy price
|
||||||
|
df['pnl_per_trade'] = df['close'] - df['buy_in'] # Fake PnL calc
|
||||||
|
|
||||||
|
# Target: Will price rise X% in next N intervals?
|
||||||
|
future_window = 4
|
||||||
|
threshold = 0.001
|
||||||
|
df['future_max'] = df['close'].shift(-future_window).rolling(future_window).max()
|
||||||
|
df['target'] = np.where(df['future_max'] > df['close'] * (1 + threshold), 1, 0)
|
||||||
|
|
||||||
|
df.dropna(inplace=True)
|
||||||
|
|
||||||
|
# Define feature set
|
||||||
|
features = [
|
||||||
|
'open', 'high', 'low', 'close', 'volume',
|
||||||
|
'sma_10', 'sma_30', 'rsi_14', 'momentum',
|
||||||
|
'price_delta', 'vol_rolling', 'bollinger_b', 'macd',
|
||||||
|
'hour', 'weekday', 'balance', 'buy_in', 'pnl_per_trade'
|
||||||
|
]
|
||||||
|
|
||||||
|
return df[features], df['target']
|
||||||
BIN
ml/scaler.pkl
Normal file
BIN
ml/scaler.pkl
Normal file
Binary file not shown.
106
ml/train_mlp.py
Normal file
106
ml/train_mlp.py
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
# ml/train_mlp.py
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
import torch
|
||||||
|
import torch.nn as nn
|
||||||
|
from torch.utils.data import DataLoader, TensorDataset
|
||||||
|
from sklearn.preprocessing import StandardScaler
|
||||||
|
from sklearn.model_selection import train_test_split
|
||||||
|
from sklearn.metrics import classification_report
|
||||||
|
from ml.models.forex_mlp import ForexMLP
|
||||||
|
from ml.preprocessing import load_and_preprocess_data
|
||||||
|
import numpy as np
|
||||||
|
import joblib
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
|
||||||
|
# Load + preprocess
|
||||||
|
features_df, labels = load_and_preprocess_data("data/DAT_XLSX_EURUSD_M1_2021.xlsx")
|
||||||
|
|
||||||
|
# Encode targets into two-element vectors: [Buy, Sell]
|
||||||
|
def encode_targets(labels):
|
||||||
|
encoded = []
|
||||||
|
for val in labels:
|
||||||
|
if val == 1:
|
||||||
|
encoded.append([1, 0])
|
||||||
|
elif val == -1:
|
||||||
|
encoded.append([0, 1])
|
||||||
|
else:
|
||||||
|
encoded.append([0, 0])
|
||||||
|
return torch.tensor(encoded, dtype=torch.float32)
|
||||||
|
|
||||||
|
# Train-test split
|
||||||
|
X_train, X_test, y_train, y_test = train_test_split(features_df, labels, test_size=0.3, shuffle=False)
|
||||||
|
|
||||||
|
# Scale
|
||||||
|
scaler = StandardScaler()
|
||||||
|
X_train_scaled = scaler.fit_transform(X_train)
|
||||||
|
X_test_scaled = scaler.transform(X_test)
|
||||||
|
|
||||||
|
# Save scaler
|
||||||
|
# 🟢 Convert to DataFrame for easier manipulation
|
||||||
|
train_df = X_train.copy()
|
||||||
|
train_df['target'] = y_train.values
|
||||||
|
|
||||||
|
# 🧪 Split into minority and majority
|
||||||
|
minority = train_df[train_df['target'] == 1]
|
||||||
|
majority = train_df[train_df['target'] == 0]
|
||||||
|
|
||||||
|
# 🔁 Oversample minority class to match majority count
|
||||||
|
minority_oversampled = minority.sample(len(majority), replace=True, random_state=42)
|
||||||
|
|
||||||
|
# 🔄 Combine + shuffle
|
||||||
|
balanced_df = pd.concat([majority, minority_oversampled]).sample(frac=1, random_state=42)
|
||||||
|
|
||||||
|
# ✅ Re-split into features and labels
|
||||||
|
X_balanced = balanced_df.drop(columns=['target'])
|
||||||
|
y_balanced = balanced_df['target']
|
||||||
|
|
||||||
|
# 🔢 Scale
|
||||||
|
scaler = StandardScaler()
|
||||||
|
X_scaled = scaler.fit_transform(X_balanced)
|
||||||
|
X_test_scaled = scaler.transform(X_test) # Use the original test set!
|
||||||
|
|
||||||
|
# 💾 Save scaler
|
||||||
|
os.makedirs("ml", exist_ok=True)
|
||||||
|
joblib.dump(scaler, "ml/scaler.pkl")
|
||||||
|
|
||||||
|
# 📦 Wrap in TensorDataset with encoded 2D targets
|
||||||
|
y_encoded = encode_targets(y_balanced.values)
|
||||||
|
train_dataset = TensorDataset(torch.tensor(X_scaled, dtype=torch.float32), y_encoded)
|
||||||
|
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
|
||||||
|
|
||||||
|
|
||||||
|
# Init model
|
||||||
|
model = ForexMLP(input_size=X_train.shape[1])
|
||||||
|
criterion = nn.BCELoss()
|
||||||
|
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
|
||||||
|
|
||||||
|
# Training loop
|
||||||
|
epochs = 100
|
||||||
|
for epoch in range(epochs):
|
||||||
|
total_loss = 0.0
|
||||||
|
for inputs, targets in train_loader:
|
||||||
|
optimizer.zero_grad()
|
||||||
|
outputs = model(inputs)
|
||||||
|
loss = criterion(outputs, targets)
|
||||||
|
loss.backward()
|
||||||
|
optimizer.step()
|
||||||
|
total_loss += loss.item()
|
||||||
|
print(f"📉 Epoch {epoch + 1} | Loss: {total_loss / len(train_loader):.4f}")
|
||||||
|
|
||||||
|
# Save model
|
||||||
|
torch.save(model.state_dict(), "ml/models/forex_mlp.pt")
|
||||||
|
print("✅ Trained model saved to ml/models/forex_mlp.pt")
|
||||||
|
|
||||||
|
# Evaluation
|
||||||
|
model.eval()
|
||||||
|
with torch.no_grad():
|
||||||
|
preds = model(torch.tensor(X_test_scaled, dtype=torch.float32))
|
||||||
|
preds_bin = (preds > 0.5).int()
|
||||||
|
y_test_encoded = encode_targets(y_test.values).int()
|
||||||
|
print("📊 Evaluation Report:")
|
||||||
|
print(classification_report(y_test_encoded, preds_bin))
|
||||||
22
requirements.txt
Normal file
22
requirements.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
backtrader
|
||||||
|
pandas
|
||||||
|
# numpy==2.2.4
|
||||||
|
numpy<2
|
||||||
|
matplotlib
|
||||||
|
requests
|
||||||
|
openpyxl
|
||||||
|
ta
|
||||||
|
scikit-learn
|
||||||
|
xgboost
|
||||||
|
joblib
|
||||||
|
matplotlib
|
||||||
|
seaborn
|
||||||
|
torch==2.1.0
|
||||||
|
torchvision==0.16.0
|
||||||
|
torchaudio==2.1.0
|
||||||
|
scikit-learn
|
||||||
|
pandas
|
||||||
|
matplotlib
|
||||||
|
seaborn
|
||||||
|
tqdm
|
||||||
|
tensorboard
|
||||||
41
strategies/pytorch_strategy.py
Normal file
41
strategies/pytorch_strategy.py
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import backtrader as bt
|
||||||
|
import torch
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
from ml.models.forex_mlp import ForexMLP
|
||||||
|
|
||||||
|
class PyTorchAIModel(bt.Strategy):
|
||||||
|
def __init__(self):
|
||||||
|
self.model = ForexMLP()
|
||||||
|
self.model.load_state_dict(torch.load("ml/models/forex_mlp.pt", map_location=torch.device("cpu")))
|
||||||
|
self.model.eval()
|
||||||
|
|
||||||
|
self.buy_threshold = 0.7
|
||||||
|
self.sell_threshold = 0.6
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
# Skip early bars (for indicators if you add them)
|
||||||
|
if len(self.datas[0]) < 30:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create feature vector for the current candle
|
||||||
|
features = np.array([[
|
||||||
|
self.data.open[0],
|
||||||
|
self.data.high[0],
|
||||||
|
self.data.low[0],
|
||||||
|
self.data.close[0],
|
||||||
|
self.data.volume[0]
|
||||||
|
]], dtype=np.float32)
|
||||||
|
|
||||||
|
inputs = torch.tensor(features)
|
||||||
|
with torch.no_grad():
|
||||||
|
output = self.model(inputs)
|
||||||
|
buy_score, sell_score = output[0].numpy()
|
||||||
|
|
||||||
|
print(f"[AI] Buy: {buy_score:.3f}, Sell: {sell_score:.3f}")
|
||||||
|
|
||||||
|
# Trade logic
|
||||||
|
if buy_score > self.buy_threshold and not self.position:
|
||||||
|
self.buy()
|
||||||
|
elif sell_score > self.sell_threshold and self.position:
|
||||||
|
self.close()
|
||||||
Loading…
Reference in a new issue