Prognozowanie kursu złotego z użyciem LSTM (Long Short-Term Memory) to jedno z najbardziej praktycznych zastosowań deep learningu w finansach. Model LSTM radzi sobie z długoterminowymi zależnościami w szeregach czasowych znacznie lepiej niż klasyczne metody statystyczne, takie jak ARIMA. W tym artykule budujesz pełny pipeline: od pobrania danych walutowych, przez przygotowanie sekwencji, po trening sieci i ocenę jej dokładności. Wszystko w Pythonie, z kodem gotowym do uruchomienia.
Dlaczego LSTM, a nie klasyczne metody prognozowania kursów walut?
Kursy walut to szeregi czasowe z silnymi zależnościami od przeszłości, ale też z nagłymi skokami wywołanymi przez decyzje banków centralnych, dane makroekonomiczne czy zdarzenia geopolityczne. Klasyczne modele, takie jak ARIMA czy SARIMA, zakładają stacjonarność i liniowość, co w przypadku forex jest założeniem zbyt restrykcyjnym.
LSTM to rekurencyjna sieć neuronowa zaprojektowana przez Seppa Hochreitera i Jürgena Schmidhubera w 1997 roku właśnie po to, by uczyć się długookresowych zależności bez problemu zanikającego gradientu. W praktyce oznacza to, że sieć może "pamiętać", że kurs EUR/PLN był pod presją trzy tygodnie temu, i uwzględniać to w dzisiejszej predykcji.
Badania porównawcze, np. opublikowane w Applied Soft Computing (Fischer i Krauss, 2018), pokazują, że sieci LSTM osiągają wyższą dokładność kierunkową niż modele liniowe na danych forex, szczególnie na horyzontach 1-5 sesji.
Kiedy LSTM zawodzi?
LSTM nie jest srebną kulą. Model słabo radzi sobie z prognozowaniem konkretnych wartości dalej niż kilka sesji do przodu, a jego wyniki są wrażliwe na jakość danych wejściowych i dobór hiperparametrów. Traktuj go jako narzędzie wspomagające decyzję, nie jako wyrocznię.
Dane wejściowe: co i skąd pobierać
Do projektu potrzebujesz historycznych notowań kursowych. Najprostsze źródła:
- yfinance (biblioteka Pythona): darmowy dostęp do danych Yahoo Finance, kursy dzienne i tygodniowe
- Stooq (stooq.pl): dane dla par z PLN, w tym EUR/PLN, USD/PLN, CHF/PLN
- NBP API (api.nbp.pl): oficjalne kursy średnie Narodowego Banku Polskiego, format JSON
Dla pary EUR/PLN pobierasz dane tak:
Pythonimport yfinance as yf import pandas as pd df = yf.download("EURPLN=X", start="2018-01-01", end="2024-12-31") df = df[["Close"]].dropna() df.columns = ["EURPLN"] print(df.head())
Jakie cechy uwzględnić?
Sam kurs zamknięcia to punkt startowy, ale model uczy się lepiej, gdy dostarczysz mu dodatkowych sygnałów. Warto dodać:
- Wolumen (jeśli dostępny dla danej pary)
- Średnie kroczące (SMA 10, SMA 50): trend
- RSI (Relative Strength Index): momentum
- Zmienność historyczna (odchylenie standardowe z 20 sesji)
- Spread EUR/USD, jeśli modelujesz EUR/PLN, bo dolar silnie wpływa na PLN
Pythondf["SMA_10"] = df["EURPLN"].rolling(10).mean() df["SMA_50"] = df["EURPLN"].rolling(50).mean() df["Volatility"] = df["EURPLN"].rolling(20).std() df.dropna(inplace=True)
Przygotowanie danych dla sieci LSTM
LSTM przyjmuje dane w formacie (próbki, kroki czasowe, cechy). Musisz przekształcić szereg czasowy w sekwencje okien.
Normalizacja
Sieci neuronowe są wrażliwe na skalę danych. Użyj MinMaxScaler z biblioteki scikit-learn:
Pythonfrom sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() scaled = scaler.fit_transform(df)
Ważne: scaler dopasowujesz tylko na zbiorze treningowym, a zbior testowy transformujesz tym samym scalerem. Inaczej "przeciekają" informacje z przyszłości do modelu, co zawyża metryki.
Tworzenie sekwencji
Pythonimport numpy as np def create_sequences(data, seq_len=30): X, y = [], [] for i in range(len(data) - seq_len): X.append(data[i:i+seq_len]) y.append(data[i+seq_len, 0]) # kurs zamknięcia return np.array(X), np.array(y) SEQ_LEN = 30 # okno 30 sesji X, y = create_sequences(scaled, SEQ_LEN) split = int(len(X) * 0.8) X_train, X_test = X[:split], X[split:] y_train, y_test = y[:split], y[split:]
Okno 30 sesji (ok. 6 tygodni handlowych) to rozsądny punkt startowy dla EUR/PLN. Możesz eksperymentować z wartościami od 10 do 60.
Architektura modelu LSTM w Keras
Czas zbudować sieć. Użyjesz TensorFlow/Keras, które są standardem w deep learningu dla finansów.
Pythonimport tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout, BatchNormalization n_features = X_train.shape[2] model = Sequential([ LSTM(128, return_sequences=True, input_shape=(SEQ_LEN, n_features)), Dropout(0.2), BatchNormalization(), LSTM(64, return_sequences=False), Dropout(0.2), Dense(32, activation="relu"), Dense(1) ]) model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss="mean_squared_error", metrics=["mae"] ) model.summary()
Kluczowe decyzje architektoniczne
Dwie warstwy LSTM dają sieci możliwość uczenia się hierarchicznych wzorców. Pierwsza warstwa wychwytuje lokalne zależności, druga integruje szerszy kontekst.
Dropout 0.2 zapobiega przeuczeniu, które jest bardzo realne przy kilkuset sesjach danych.
BatchNormalization przyspiesza trening i stabilizuje gradienty, szczególnie gdy cechy mają różne skale (nawet po normalizacji).
Adam z lr=0.001 to bezpieczny punkt startowy. Jeśli strata oscyluje, zmniejsz do 0.0005.
Trening i ewaluacja modelu
Pythonfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau callbacks = [ EarlyStopping(patience=15, restore_best_weights=True), ReduceLROnPlateau(factor=0.5, patience=7, min_lr=1e-6) ] history = model.fit( X_train, y_train, epochs=100, batch_size=32, validation_split=0.1, callbacks=callbacks, verbose=1 )
EarlyStopping zatrzymuje trening, gdy model przestaje się poprawiać, i przywraca najlepsze wagi. Dzięki temu nie musisz ręcznie dobierać liczby epok.
Metryki oceny
Po treningu ewaluujesz model na zbiorze testowym:
Pythonfrom sklearn.metrics import mean_absolute_error, mean_squared_error y_pred = model.predict(X_test) # Odwrócenie normalizacji dla kursu zamknięcia y_pred_inv = scaler.inverse_transform( np.concatenate([y_pred, np.zeros((len(y_pred), n_features-1))], axis=1) )[:, 0] y_test_inv = scaler.inverse_transform( np.concatenate([y_test.reshape(-1,1), np.zeros((len(y_test), n_features-1))], axis=1) )[:, 0] mae = mean_absolute_error(y_test_inv, y_pred_inv) rmse = np.sqrt(mean_squared_error(y_test_inv, y_pred_inv)) print(f"MAE: {mae:.4f} PLN") print(f"RMSE: {rmse:.4f} PLN")
Dla EUR/PLN, gdzie kurs oscyluje między 4,20 a 4,80, MAE poniżej 0,03 PLN na horyzoncie jednodniowym to dobry wynik. Ważniejsza od absolutnych błędów jest jednak dokładność kierunkowa (czy model poprawnie przewiduje, czy kurs wzrośnie czy spadnie):
Pythondirection_actual = np.sign(np.diff(y_test_inv)) direction_pred = np.sign(np.diff(y_pred_inv)) directional_accuracy = np.mean(direction_actual == direction_pred) print(f"Dokładność kierunkowa: {directional_accuracy:.2%}")
W praktycznych projektach wynik powyżej 55% uznaje się za istotny sygnał, choć i tak nie zastępuje analizy fundamentalnej.
Najczęstsze błędy i jak ich unikać
Prognozowanie kursu złotego przez LSTM brzmi prosto, ale w praktyce czai się kilka pułapek:
1. Data leakage (przeciek danych) Najpoważniejszy błąd. Polega na tym, że informacje z przyszłości trafiają do zbioru treningowego. Scaler, jak wspomniano, dopasowuj wyłącznie na train. Wskaźniki techniczne (SMA, RSI) obliczaj przed podziałem, ale na pełnym szeregu, czyli też tylko na tym, co "historycznie widziałby" model.
2. Zbyt krótkie okno treningowe Neural networks forex potrzebują wystarczającej liczby próbek. Przy oknie 30 sesji i 1500 dniach danych masz ok. 1200 próbek treningowych, to minimum. Poniżej 500 próbek model będzie się przeuczać.
3. Brak walidacji walk-forward Klasyczny podział train/test nie oddaje realiów rynkowych. Użyj TimeSeriesSplit z scikit-learn lub własnej walidacji kroczącej, by sprawdzić, jak model radziłby sobie w różnych reżimach rynkowych.
4. Ignorowanie reżimów rynkowych Model trenowany w 2018-2022 może słabo działać w 2023, gdy NBP agresywnie zmieniał stopy. Rozważ retrenowanie modelu co kilka miesięcy lub użycie mechanizmu attention, który lepiej radzi sobie ze zmianami strukturalnymi.
5. Metryki bez kontekstu Niskie RMSE nie oznacza, że model zarabia. Zawsze sprawdzaj dokładność kierunkową i symuluj prostą strategię (kup jeśli model przewiduje wzrost), by ocenić praktyczną użyteczność.
Rozszerzenia: co warto dodać do modelu
Gdy masz działający baseline, możesz go rozbudować:
- Bidirectional LSTM: czyta sekwencję w obu kierunkach, pomocne przy danych z wyraźnymi cyklami
- Attention mechanism: pozwala sieci "skupić się" na najważniejszych momentach w oknie czasowym
- Dane makroekonomiczne: stopy NBP, inflacja CPI, saldo handlowe Polski i strefy euro jako dodatkowe cechy
- Sentyment z mediów: analiza NLP nagłówków finansowych (np. przez Hugging Face FinBERT) jako sygnał pomocniczy
- Ensemble z XGBoost: połączenie predykcji LSTM i gradientowego boostingu często obniża błąd o 5-10% względem samego LSTM
Jeśli chcesz przejść od teorii do praktyki z pełnym wsparciem kodu, gotowymi notebookami i zadaniami z oceną ryzyka portfela, sprawdź kurs AI w finansach: analiza danych, prognozowanie i ocena ryzyka w Pythonie na vita.edu.pl/courses/ai-finanse. Kurs obejmuje m.in. budowę modeli predykcyjnych, analizę szeregów czasowych i ocenę ryzyka w Pythonie, a wszystko to w ramach jednego abonamentu VITA.
Możesz zacząć już dziś: 3 dni pełnego dostępu za 0 zł do wszystkich kursów na platformie. Karta płatnicza jest wymagana, a po okresie próbnym abonament to 79 zł miesięcznie albo 590 zł rocznie. Anulujesz jednym kliknięciem przed upływem 3 dni, jeśli uznasz, że to nie dla Ciebie. Kurs posiada egzamin końcowy, a certyfikat ukończenia otrzymujesz po pierwszej płatności (możesz ją zrealizować od razu w panelu, nawet w trakcie okresu próbnego). Zacznij okres próbny na vita.edu.pl/abonament.
Podsumowanie: co zabrać z tego artykułu
Budowa modelu LSTM do prognozowania kursu złotego to projekt, który realnie można zrealizować w Pythonie w kilka godzin, o ile masz ustrukturyzowane podejście. Kluczowe punkty:
- Dane pobierasz z yfinance, Stooq lub API NBP; wzbogacasz je o wskaźniki techniczne
- Normalizujesz i przekształcasz w sekwencje okien (standardowo 20-60 sesji)
- Architektura: dwie warstwy LSTM z Dropout i BatchNormalization, optymalizator Adam
- Ewaluujesz przez MAE, RMSE i dokładność kierunkową na zbiorze testowym
- Unikasz data leakage i stosować walidację krocząca zamiast prostego podziału train/test
- Rozbudowujesz model o dane makroekonomiczne, sentyment i mechanizmy attention
Przewidywanie kursów walut przez AI nie zastępuje analizy fundamentalnej ani zarządzania ryzykiem, ale jako jeden z elementów procesu decyzyjnego, oparty na danych i testowalny empirycznie, jest narzędziem wartym opanowania.