File size: 10,003 Bytes
6404666
 
 
 
 
 
4361d58
 
 
 
 
 
6404666
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4361d58
 
 
6404666
4361d58
6404666
4361d58
 
6404666
4361d58
 
 
6404666
4361d58
 
 
6404666
4361d58
 
 
 
 
6404666
4361d58
 
6404666
4361d58
 
6404666
4361d58
 
 
 
 
 
6404666
4361d58
6404666
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4361d58
 
6404666
 
 
 
 
 
 
 
 
 
 
4361d58
6404666
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4361d58
 
 
 
 
 
 
 
 
 
 
 
6404666
4361d58
 
 
 
 
6404666
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4361d58
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
from flask import Flask, render_template, request, jsonify
import pandas as pd
import numpy as np
import pickle
import os
from datetime import datetime, timedelta
import matplotlib
matplotlib.use('Agg')  # Use non-interactive backend
import matplotlib.pyplot as plt
import seaborn as sns
import base64
import io

app = Flask(__name__)

# Load model and scaler
def load_model_and_scaler():
    try:
        with open('model.pkl', 'rb') as model_file:
            model = pickle.load(model_file)
        
        with open('scaler.pkl', 'rb') as scaler_file:
            scaler = pickle.load(scaler_file)
        
        return model, scaler
    except FileNotFoundError:
        return None, None

def load_data():
    """Load and preprocess the gold data"""
    try:
        data = pd.read_csv('gold.csv')
        data['date'] = pd.to_datetime(data['date'], format='%d/%m/%Y').dt.strftime('%Y-%m-%d')
        temp_df = data[['date', 'close', 'open']]
        
        # Preprocessing
        df = temp_df.copy()
        df['date'] = pd.to_datetime(df['date'], dayfirst=True, format='%Y-%m-%d').dt.date
        df.set_index('date', inplace=True)
        df.index = pd.to_datetime(df.index)
        df = df.sort_index()
        
        return df
    except FileNotFoundError:
        return None

def normalize_data(df, scaler):
    """Normalize the data using the provided scaler"""
    np_data_unscaled = np.array(df)
    np_data_scaled = scaler.transform(np_data_unscaled)
    normalized_df = pd.DataFrame(np_data_scaled, columns=df.columns, index=df.index)
    return normalized_df

def sliding_window(data, lag):
    """Create sliding window features"""
    series_close = data['close']
    series_open = data['open']
    result = pd.DataFrame()

    # Add lag columns for 'close'
    for l in lag:
        result[f'close-{l}'] = series_close.shift(l)

    # Add lag columns for 'open'
    for l in lag:
        result[f'open-{l}'] = series_open.shift(l)

    # Add original 'close' and 'open' columns
    result['close'] = series_close[max(lag):]
    result['open'] = series_open[max(lag):]

    # Remove missing values (NaN)
    result = result.dropna()

    # Set index according to lag values
    result.index = series_close.index[max(lag):]

    return result

def predict_next_7_days(model, scaler, data):
    """Predict gold prices for the next 7 days"""
    # Normalize data
    normalized_df = normalize_data(data, scaler)
    
    # Create sliding window
    windowed_data = sliding_window(normalized_df, [1, 2, 3, 4, 5, 6, 7])
    windowed_data = windowed_data[['close', 'close-1', 'close-2', 'close-3', 'close-4', 'close-5', 'close-6', 'close-7', 
                                   'open', 'open-1', 'open-2', 'open-3', 'open-4', 'open-5', 'open-6', 'open-7']]
    
    # Initialize predictions list
    predictions = []
    
    # Get last row as initial input
    last_row = windowed_data.drop(columns=['close', 'open']).iloc[-1].values.reshape(1, -1)
    
    # Iterate for 7 days
    for _ in range(7):
        # Predict value for next day
        predicted_value_normalized = model.predict(last_row)
        predicted_value = scaler.inverse_transform(predicted_value_normalized.reshape(-1, 2))
        
        # Save prediction
        predictions.append(predicted_value[0])
        
        # Update input for next iteration
        new_row_normalized = np.hstack([last_row[0, 2:], predicted_value_normalized[0]])
        last_row = new_row_normalized.reshape(1, -1)
    
    # Transform predictions to DataFrame
    predictions_df = pd.DataFrame(
        predictions,
        columns=['close', 'open'],
        index=pd.date_range(start=normalized_df.index[-1] + pd.Timedelta(days=1), periods=7)
    )
    
    # Get last price
    last_price = scaler.inverse_transform(normalized_df[['close', 'open']].iloc[-1].values.reshape(-1, 2))
    
    # Calculate daily percentage changes
    predictions_df['close_change'] = predictions_df['close'].pct_change().fillna(0) * 100
    predictions_df['open_change'] = predictions_df['open'].pct_change().fillna(0) * 100
    
    # Calculate total change from today to day 7
    total_close_change = ((predictions_df['close'].iloc[-1] - last_price[0][0]) / last_price[0][0]) * 100
    total_open_change = ((predictions_df['open'].iloc[-1] - last_price[0][1]) / last_price[0][1]) * 100
    
    return predictions_df, last_price[0], total_close_change, total_open_change

def create_prediction_chart(data, predictions_df, last_price):
    """Create a chart showing historical and predicted prices"""
    plt.style.use('default')
    fig, ax = plt.subplots(figsize=(12, 8))
    
    # Plot last 30 days of historical data
    recent_data = data.tail(30)
    ax.plot(recent_data.index, recent_data['close'], 'b-', label='Historical Close Price', linewidth=2)
    ax.plot(recent_data.index, recent_data['open'], 'g-', label='Historical Open Price', linewidth=2)
    
    # Add current day point
    ax.plot(recent_data.index[-1], last_price[0], 'ro', markersize=8, label='Current Close Price')
    ax.plot(recent_data.index[-1], last_price[1], 'go', markersize=8, label='Current Open Price')
    
    # Plot predictions
    ax.plot(predictions_df.index, predictions_df['close'], 'r--', label='Predicted Close Price', linewidth=2, marker='o')
    ax.plot(predictions_df.index, predictions_df['open'], 'orange', linestyle='--', label='Predicted Open Price', linewidth=2, marker='s')
    
    ax.set_title('Gold Price Prediction - Next 7 Days', fontsize=16, fontweight='bold')
    ax.set_xlabel('Date', fontsize=12)
    ax.set_ylabel('Price (IDR)', fontsize=12)
    ax.legend()
    ax.grid(True, alpha=0.3)
    
    # Format y-axis to show prices in millions
    ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'{x/1000000:.1f}M'))
    
    plt.xticks(rotation=45)
    plt.tight_layout()
    
    # Convert plot to base64 string
    img_buffer = io.BytesIO()
    plt.savefig(img_buffer, format='png', dpi=150, bbox_inches='tight')
    img_buffer.seek(0)
    img_string = base64.b64encode(img_buffer.read()).decode()
    plt.close()
    
    return img_string

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/predict', methods=['POST'])
def predict():
    try:
        # Load model and scaler
        model, scaler = load_model_and_scaler()
        if model is None or scaler is None:
            return jsonify({'error': 'Model or scaler not found. Please train the model first.'}), 500
        
        # Load data
        data = load_data()
        if data is None:
            return jsonify({'error': 'Data file not found.'}), 500
        
        # Make predictions
        predictions_df, last_price, total_close_change, total_open_change = predict_next_7_days(model, scaler, data)
        
        # Create chart
        chart_img = create_prediction_chart(data, predictions_df, last_price)
        
        # Prepare response data
        predictions_list = []
        for date, row in predictions_df.iterrows():
            predictions_list.append({
                'date': date.strftime('%Y-%m-%d'),
                'close_price': round(row['close'], 2),
                'open_price': round(row['open'], 2),
                'close_change': round(row['close_change'], 2),
                'open_change': round(row['open_change'], 2)
            })
        
        response = {
            'success': True,
            'current_prices': {
                'close': round(last_price[0], 2),
                'open': round(last_price[1], 2)
            },
            'predictions': predictions_list,
            'total_changes': {
                'close': round(total_close_change, 2),
                'open': round(total_open_change, 2)
            },
            'chart': chart_img
        }
        
        return jsonify(response)
    
    except Exception as e:
        return jsonify({'error': f'An error occurred: {str(e)}'}), 500

@app.route('/data-analysis')
def data_analysis():
    """Show data analysis page"""
    try:
        data = load_data()
        if data is None:
            return render_template('error.html', error='Data file not found.')
        
        # Create historical price chart
        fig, ax = plt.subplots(figsize=(12, 6))
        ax.plot(data.index, data['close'], 'b-', label='Close Price', linewidth=1.5)
        ax.plot(data.index, data['open'], 'g-', label='Open Price', linewidth=1.5)
        ax.set_title('Historical Gold Prices', fontsize=16, fontweight='bold')
        ax.set_xlabel('Date', fontsize=12)
        ax.set_ylabel('Price (IDR)', fontsize=12)
        ax.legend()
        ax.grid(True, alpha=0.3)
        ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'{x/1000000:.1f}M'))
        plt.xticks(rotation=45)
        plt.tight_layout()
        
        img_buffer = io.BytesIO()
        plt.savefig(img_buffer, format='png', dpi=150, bbox_inches='tight')
        img_buffer.seek(0)
        historical_chart = base64.b64encode(img_buffer.read()).decode()
        plt.close()
        
        # Calculate statistics
        stats = {
            'total_records': len(data),
            'date_range': f"{data.index.min().strftime('%Y-%m-%d')} to {data.index.max().strftime('%Y-%m-%d')}",
            'avg_close': round(data['close'].mean(), 2),
            'avg_open': round(data['open'].mean(), 2),
            'min_close': round(data['close'].min(), 2),
            'max_close': round(data['close'].max(), 2),
            'current_close': round(data['close'].iloc[-1], 2),
            'current_open': round(data['open'].iloc[-1], 2)
        }
        
        return render_template('data_analysis.html', chart=historical_chart, stats=stats)
    
    except Exception as e:
        return render_template('error.html', error=f'An error occurred: {str(e)}')

if __name__ == '__main__':
    # For Hugging Face Spaces, use port 7860
    port = int(os.environ.get('PORT', 7860))
    app.run(host='0.0.0.0', port=port, debug=False)