# ============================= Complete LSTM Foreign Buy/Sell Prediction Script =============================

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_absolute_percentage_error
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
from tensorflow.keras.callbacks import EarlyStopping
import tensorflow as tf
import warnings
from datetime import timedelta

# Suppress warnings for cleaner output
warnings.filterwarnings("ignore")
tf.get_logger().setLevel('ERROR')

# 1. Load and Preprocess Data
file_path = r"C:\Users\jack8\OneDrive\stock cow ai\data\Foreign.csv"
data = pd.read_csv(file_path)

# Display first few rows and check for missing values
print("Sample Data:")
print(data.head())
print("\nMissing Values:")
print(data.isnull().sum())

# 2. Select Features and Target Variable
# Assuming columns are ['Date', 'Buy', 'Sell', 'Net Buy/Sell (Lots)']
# Convert 'Date' to datetime and set as index
data['Date'] = pd.to_datetime(data['Date'], format='%Y/%m/%d')
data.set_index('Date', inplace=True)

# Selecting features - You can choose to predict 'Buy', 'Sell', or 'Net Buy/Sell (Lots)'
# Here, we'll predict 'Net Buy/Sell (Lots)' based on previous 'Buy' and 'Sell'
features = ['Buy', 'Sell','Net Buy/Sell (Lots)']
target = 'Closing Price'
data = data[features + [target]]

# Ensure data is sorted by date
data.sort_index(inplace=True)

# Handle missing values (e.g., forward fill)
data.fillna(method='ffill', inplace=True)

# 3. Feature Scaling
scaler_features = MinMaxScaler(feature_range=(0, 1))
scaler_target = MinMaxScaler(feature_range=(0, 1))

# Fit scaler_features on 'Buy' and 'Sell'
scaled_features = scaler_features.fit_transform(data[features])

# Fit scaler_target on 'Net Buy/Sell (Lots)'
scaled_target = scaler_target.fit_transform(data[[target]])

# Combine scaled features and target
scaled_data = np.hstack((scaled_features, scaled_target))

# 4. Create Sequences for LSTM
def create_sequences(data, sequence_length):
    X = []
    y = []
    for i in range(sequence_length, len(data)):
        X.append(data[i-sequence_length:i, :-1])  # All features except target
        y.append(data[i, -1])  # Target variable
    return np.array(X), np.array(y)

sequence_length = 30  # You can adjust this value based on your data size
X, y = create_sequences(scaled_data, sequence_length)

print(f"\nTotal sequences created: {len(X)}")

# 5. Manual Train-Test Split to Ensure Alignment
train_size = int(len(X) * 0.8)
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]

print(f"Training sequences: {len(X_train)}")
print(f"Testing sequences: {len(X_test)}")

# 6. Build the LSTM Model
model = Sequential()
model.add(LSTM(units=64, return_sequences=True, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(LSTM(units=64))
model.add(Dense(1))

# Compile the model
model.compile(optimizer='adam', loss='mean_squared_error', metrics=['mean_absolute_percentage_error'])

# 7. Implement Early Stopping
early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)

# 8. Train the Model
history = model.fit(
    X_train, y_train,
    epochs=100,
    batch_size=16,
    validation_split=0.1,
    callbacks=[early_stop],
    verbose=1
)

# 9. Make Predictions on Test Set
predictions = model.predict(X_test)

# Inverse Transform to Get Actual Values
y_test_inverse = scaler_target.inverse_transform(y_test.reshape(-1, 1))
predictions_inverse = scaler_target.inverse_transform(predictions)

# 10. Calculate MAPE
mape = mean_absolute_percentage_error(y_test_inverse, predictions_inverse)
print(f"\nMean Absolute Percentage Error (MAPE): {mape * 100:.2f}%")

# 11. Plot Actual vs Predicted Net Buy/Sell (Lots)
plt.figure(figsize=(14, 7))
plt.plot(y_test_inverse, color='blue', label='Actual Net Buy/Sell (Lots)')
plt.plot(predictions_inverse, color='red', label='Predicted Net Buy/Sell (Lots)')
plt.title('Actual vs Predicted Net Buy/Sell (Lots)')
plt.xlabel('Time')
plt.ylabel('Net Buy/Sell (Lots)')
plt.legend()
plt.show()

# =================== 12. Predict Future 2 Weeks of Buy/Sell (10 Trading Days) ===================

# 12.1 Prepare the Latest Data Sequence
last_sequence = scaled_data[-sequence_length:, :-1]  # Last 'sequence_length' days of features

# Initialize a list to store future predictions
future_predictions_scaled = []

# Define the number of future days to predict (2 weeks ≈ 10 trading days)
future_days = 10  # Adjusted to represent two weeks of trading days

# Copy the last_sequence to avoid modifying the original scaled_data
current_sequence = last_sequence.copy()

for day in range(future_days):
    # Reshape to match the input shape of the model: (1, sequence_length, num_features)
    input_seq = current_sequence.reshape((1, sequence_length, X_train.shape[2]))
    
    # Predict the next day's Net Buy/Sell (Lots)
    predicted_scaled = model.predict(input_seq)
    
    # Append the predicted value to future_predictions_scaled
    future_predictions_scaled.append(predicted_scaled[0][0])
    
    # Create a new row for the next input sequence
    # Here, we assume that the 'Buy' and 'Sell' for the next day remain the same as the last day
    # except for the 'Net Buy/Sell' which is updated based on prediction
    # You may enhance this by incorporating more sophisticated logic
    new_row = current_sequence[-1].copy()
    new_row[-1] = predicted_scaled  # Update 'Net Buy/Sell (Lots)'
    
    # Append the new row to current_sequence and remove the first row
    current_sequence = np.vstack([current_sequence[1:], new_row])

# 12.2 Inverse Transform the Predicted Values
future_predictions_scaled = np.array(future_predictions_scaled).reshape(-1, 1)
future_predictions_inverse = scaler_target.inverse_transform(future_predictions_scaled)

# 12.3 Prepare Dates for Future Predictions
# Get the last date from the original dataset
last_date = data.index[-1]

# Generate future trading dates (excluding weekends)
future_dates = []
current_date = last_date
while len(future_dates) < future_days:
    current_date += timedelta(days=1)
    if current_date.weekday() < 5:  # Monday=0, Sunday=6
        future_dates.append(current_date)

future_dates = pd.to_datetime(future_dates)

# 12.4 Create DataFrame for Future Predictions
future_df = pd.DataFrame({
    'Date': future_dates,
    'Predicted Net Buy/Sell (Lots)': future_predictions_inverse.flatten()
})

# 12.5 Plot Predicted Net Buy/Sell (Lots) Only
plt.figure(figsize=(14, 7))
plt.plot(future_df['Date'], future_df['Predicted Net Buy/Sell (Lots)'], color='red', marker='o', label='Predicted Net Buy/Sell (Lots)')
plt.title('Predicted Net Buy/Sell (Lots) for the Next 2 Weeks')
plt.xlabel('Date')
plt.ylabel('Predicted Net Buy/Sell (Lots)')
plt.legend()

# Format the x-axis to show dates in YYYY-MM-DD format
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=1))  # Set major ticks to daily intervals

# Rotate date labels for better readability
plt.xticks(rotation=45)

# Ensure layout fits well
plt.tight_layout()
plt.show()

# 12.6 Print Future Predictions
print("\nPredicted Net Buy/Sell (Lots) for the Next 2 Weeks:")
print(future_df)



# ============================= Script End =============================



###我增加了Closing Price這個欄位在CSV，把Closing Price'當為預測值