File size: 9,113 Bytes
4c1f943
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import gradio as gr
import pandas as pd
from datetime import datetime, timedelta
import numpy as np
import matplotlib.pyplot as plt

# Import các module của bạn
from SRC.utils.preprocessing import Preproces
from SRC.utils.CEEMDAN import CEEMDANWrapper
from SRC.utils.data_preparation import DataPreparation
from SRC.optimization.Optuna_opts import Optimizers
from SRC.utils.plot_figure import plot_figure
# from SRC.utils.insert_pd import insert_pd
from SRC.config.constant import const   # nếu bạn để trong SRC/config

# ====================== GRADIO APP ======================
with gr.Blocks(title="🚀 CEEMDAN-Hybrid Stock Predictor") as demo:  # ← xóa theme ở đây    gr.Markdown("# CEEMDAN + Hybrid ML Stock Forecast\nHugging Face Space")

    with gr.Tab("1. Cấu hình & Tải dữ liệu"):
        with gr.Row():
            ticker = gr.Textbox(value="AAPL", label="Ticker")
            start_date = gr.Textbox(value="2019-01-01", label="Ngày bắt đầu")
            end_date = gr.Textbox(value="2024-04-26", label="Ngày kết thúc")
            target_col = gr.Dropdown(["Close", "Adj Close"], value="Close", label="Cột dự đoán")
            window_size = gr.Slider(3, 20, value=5, step=1, label="Window size (lag)")
            split_ratio = gr.Slider(0.6, 0.95, value=0.80, step=0.05, label="Tỷ lệ train/test")
            ceemdan_trials = gr.Slider(10, 100, value=20, step=10, label="Số trials CEEMDAN (càng ít càng nhanh)")
            
        btn_load = gr.Button("📥 Tải & Chuẩn hóa dữ liệu", variant="primary")
        data_preview = gr.DataFrame(label="Dữ liệu giá")
        price_plot = gr.Plot(label="Biểu đồ giá lịch sử")

    with gr.Tab("2. Phân tích CEEMDAN"):
        btn_decompose = gr.Button("🔬 Chạy CEEMDAN Decomposition")
        ceemdan_overview = gr.DataFrame(label="Tổng quan các thành phần")
        ceemdan_plot = gr.Plot(label="Các IMF + Residue")

    with gr.Tab("3. Backtest (Đánh giá mô hình)"):
        models = gr.CheckboxGroup(["SVR", "DT", "RF", "KNN", "ANN"], value=["RF"], label="Chọn mô hình")
        n_trials = gr.Slider(5, 30, value=8, step=1, label="Số trial Optuna")
        btn_backtest = gr.Button("🚀 Chạy Backtest", variant="primary", size="large")
        
        metric_table = gr.DataFrame(label="Bảng so sánh Metric")
        tuning_table = gr.DataFrame(label="Best params & CV MSE")
        commentary = gr.Markdown()
        backtest_plot = gr.Plot()

    with gr.Tab("4. Dự đoán tương lai"):
        horizon = gr.Slider(1, 90, value=30, step=1, label="Số ngày dự đoán tương lai")
        models_future = gr.CheckboxGroup(["SVR", "DT", "RF", "KNN", "ANN"], value=["RF"], label="Mô hình dùng để dự báo")
        btn_future = gr.Button("🔮 Dự đoán tương lai", variant="primary", size="large")
        
        future_table = gr.DataFrame(label="Bảng dự đoán tương lai")
        future_plot = gr.Plot(label="Dự báo giá tương lai")

    # ====================== LOGIC ======================
    config = {}
    df_global = None
    components_global = None
    component_names_global = None

    def load_data(ticker_val, start, end, target, trials_val):
        global df_global, config
        config = const().__dict__  # load default
        config["ticker"] = ticker_val
        config["start_date"] = start
        config["end_date"] = end
        config["target_col"] = target
        config["window_size"] = window_size.value
        config["split_ratio"] = split_ratio.value
        config["ceemdan_trials"] = int(trials_val)
        
        raw = Preproces.download_price_data(config)
        df_global = Preproces.preprocess_price_data(raw, target)
        
        # Plot giá
        fig, ax = plt.subplots(figsize=(12, 5))
        ax.plot(df_global["Date"], df_global[target], label="Giá thực tế")
        ax.set_title(f"{ticker_val} - Giá lịch sử")
        ax.legend()
        return df_global.head(10), fig

    btn_load.click(
    load_data, 
    inputs=[ticker, start_date, end_date, target_col, ceemdan_trials], 
    outputs=[data_preview, price_plot]
)

    def run_ceemdan():
        global components_global, component_names_global
        if df_global is None:
            return None, None  # hoặc raise gr.Error("Vui lòng tải dữ liệu trước")
        
        signal = df_global[config["target_col"]].values
        components, names, comp_df, overview = CEEMDANWrapper.run_ceemdan(
            signal, config, df_global["Date"]
        )
        components_global = components
        component_names_global = names

        # Tạo plot
        fig, axes = plt.subplots(len(names), 1, figsize=(12, 2.5 * len(names)), sharex=True)
        if len(names) == 1:
            axes = [axes]
        for i, (ax, name) in enumerate(zip(axes, names)):
            ax.plot(df_global["Date"], components[i], label=name)
            ax.set_title(name)
            ax.legend()
        plt.tight_layout()

        return overview, fig

    btn_decompose.click(run_ceemdan, outputs=[ceemdan_overview, ceemdan_plot])   # bạn có thể mở rộng plot

    def run_backtest(selected_models):
        if df_global is None or components_global is None:
            raise gr.Error("Vui lòng tải dữ liệu và chạy CEEMDAN trước!")

        # Tính test_target_indices CHÍNH XÁC theo window_size
        N = len(df_global)
        ws = config["window_size"]
        X_len = N - ws
        split_idx = int(X_len * config["split_ratio"])
        test_start_idx = ws + split_idx                     # index gốc trong chuỗi
        test_target_indices = np.arange(test_start_idx, N)
        prepared_data = {}
        for i, name in enumerate(component_names_global):
            prepared_data[name] = DataPreparation.prepare_for_backtest(
                components_global[i], config["window_size"], config["split_ratio"]
            )

        results = []
        for model_key in selected_models:
            model_name = {"SVR": "SVR", "DT": "Decision Tree", "RF": "Random Forest",
                          "KNN": "KNN", "ANN": "Neural Network"}[model_key]
            exp = Optimizers.run_model_experiment(
                model_key, 
                model_name, 
                prepared_data, 
                df_global[config["target_col"]].values,
                df_global, 
                test_target_indices,          # ← THAY ĐỔI: dùng biến đã tính đúng
                config
            )
            results.append(exp)

            # Plot cho model đầu tiên làm ví dụ
            if len(results) == 1:
                fig = plot_figure.plot_prediction_report(exp["prediction_df"], model_name)  # sửa plot_figure để return fig nếu cần

        metric_df = pd.concat([r["metric_df"] for r in results], ignore_index=True)
        tuning_df = pd.concat([r["tuning_df"] for r in results], ignore_index=True)
        comment = f"""
            **Backtest hoàn tất với {len(selected_models)} mô hình**  
            - Số trial Optuna: {config.get('n_trials', 8)}  
            - CEEMDAN trials: {config.get('ceemdan_trials', 20)}  
            - Model tốt nhất hiện tại: {results[0]['model_name']}
            """

        return metric_df, tuning_df, comment, fig   # bạn có thể trả fig nếu muốn

    btn_backtest.click(run_backtest, inputs=models, outputs=[metric_table, tuning_table, commentary, backtest_plot])

    def run_future_forecast(selected_models, days):
        # Train trên toàn bộ dữ liệu
        prepared_data = {}
        for i, name in enumerate(component_names_global):
            prepared_data[name] = DataPreparation.prepare_for_forecast(components_global[i], config["window_size"])

        component_results, _ = Optimizers.train_hybrid_model(selected_models[0], prepared_data, config)  # chỉ lấy 1 model để demo, bạn có thể loop

        final_forecast, _ = Optimizers.forecast_hybrid_model(component_results, days, config)

        # Tạo bảng tương lai
        last_date = df_global["Date"].iloc[-1]
        future_dates = [last_date + timedelta(days=i+1) for i in range(days)]
        future_df = pd.DataFrame({
            "Date": future_dates,
            "Predicted_Price": final_forecast.round(2)
        })

        # Plot
        fig, ax = plt.subplots(figsize=(12, 6))
        ax.plot(df_global["Date"], df_global[config["target_col"]], label="Lịch sử")
        ax.plot(future_dates, final_forecast, label="Dự báo tương lai", linestyle="--", color="red")
        ax.legend()
        ax.set_title(f"Dự báo {days} ngày tới - {config['ticker']}")
        return future_df, fig

    btn_future.click(run_future_forecast, [models_future, horizon], [future_table, future_plot])

if __name__ == "__main__":
    demo.launch(
        theme=gr.themes.Soft(),           # ← chuyển theme vào đây
        server_port=7860,
        share=False,                      # set True nếu muốn public link
        debug=False
    )