數據樣本
Date	RSV	K	D	J
2020/1/14	100	56.78	49.08	72.18
2020/1/15	71.42857143	61.66285714	53.27428571	78.44
2020/1/16	45.23809524	56.18793651	54.24550265	60.07280423
2020/1/17	38.0952381	50.15703704	52.88268078	44.70574956
2020/1/20	38.0952381	46.13643739	50.63393298	37.14144621
2020/1/30	0	30.75762493	44.00849696	4.255880854
2020/1/31	11.86440678	24.45988554	37.49229316	-1.60492968
2020/2/3	8.823529412	19.24776683	31.41078438	-5.078268264
2020/2/4	38.23529412	25.57694259	29.46617045	17.79848688
2020/2/5	46.96969697	32.70786072	30.54673388	37.03011441




# ============================= Complete LSTM Stock Price Prediction Script =============================

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
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\stock_price_with_kdj.csv"
data = pd.read_csv(file_path, parse_dates=['Date'])

# 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
# Set 'Date' as the DataFrame index if it exists
if 'Date' in data.columns:
    data.set_index('Date', inplace=True)

numeric_features = ['RSV', 'K', 'D','J']  # Include 'Closing Price'
target = 'J'
data = data[numeric_features]

# 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))
scaled_features = scaler_features.fit_transform(data[numeric_features[:-1]])  # Exclude 'Closing Price'

scaler_target = MinMaxScaler(feature_range=(0, 1))
scaled_target = scaler_target.fit_transform(data[[target]])  # Only 'Closing Price'

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

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

sequence_length = 70  # You can adjust this value
X, y = create_sequences(scaled_data, sequence_length, target_column=numeric_features.index('J'))

# 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:]

# 6. Build the LSTM Model
model = Sequential()
model.add(LSTM(units=50, return_sequences=True, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(LSTM(units=50))
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 Prices
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 Closing Prices
plt.figure(figsize=(14, 7))
plt.plot(y_test_inverse, color='blue', label='Actual Closing Price')
plt.plot(predictions_inverse, color='red', label='Predicted Closing Price')
plt.title('Actual vs Predicted Closing Prices')
plt.xlabel('Time')
plt.ylabel('Closing Price')
plt.legend()
plt.show()

# =================== 12. Predict Future 2 Weeks of Stock Prices ===================

# 12.1 Prepare the Latest Data Sequence
last_sequence = scaled_data[-sequence_length:, :-1]  # Exclude the target from the last sequence

# 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, current_sequence.shape[1]))
    
    # Predict the next day's closing price
    predicted_scaled = model.predict(input_seq)
    
    # Append the predicted closing price to future_predictions_scaled
    future_predictions_scaled.append(predicted_scaled[0][0])
    
    # Create a new row for the next input sequence
    # Reuse the last known values for 'Buy', 'Sell', 'Net Buy/Sell (Lots)'
    new_row = current_sequence[-1].copy()
    new_row[-1] = predicted_scaled  # Update 'Closing Price'
    
    # Append the updated 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
if isinstance(data.index, pd.DatetimeIndex):
    last_date = data.index[-1]
else:
    last_date = pd.to_datetime(data['Date'].iloc[-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 DataFrames for Historical and Future Data
# Historical Closing Prices
history_df = pd.DataFrame({
    'Date': data.index[-len(y_test_inverse):],
    'Actual Closing Price': y_test_inverse.flatten()
})

# Future Predicted Closing Prices
future_df = pd.DataFrame({
    'Date': future_dates,
    'Predicted Closing Price': future_predictions_inverse.flatten()
})

# 12.5 Plot Predicted Closing Prices Only
import matplotlib.dates as mdates

plt.figure(figsize=(14, 7))
plt.plot(future_df['Date'], future_df['Predicted Closing Price'], color='red', marker='o', label='Predicted Closing Price')
plt.title('Predicted Closing Prices for the Next 2 Weeks')
plt.xlabel('Date')
plt.ylabel('Closing Price')
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 Closing Prices for the Next 2 Weeks:")
print(future_df)

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


###幫我重寫，因為MAPE50%