środa, 31 stycznia 2024

Analiza sentymentów danych giełdowych - wstęp

Ta praca odzwierciedla zainteresowanie autora analizą finansową i wykorzystaniem narzędzi informatycznych w jej realizacji. Niniejszy blog nie ma na celu dostarczania porad finansowych, lecz stanowi konstruktywne podejście autora do dzielenia się wiedzą. Autor widzi w tym sposobność do rozwijania swoich umiejętności i zachęcania do wzajemnej dyskusji. Należy pamiętać, że wnioski wynikające z analiz prezentowanych na tym blogu nie powinny być bezpośrednio stosowane do podejmowania decyzji finansowych. Nawet jeśli analizy są dokładne, stanowią one tylko fragment potrzebnych informacji do podjęcia w pełni świadomej decyzji. Brak szerszego kontekstu, w którym znajduje się potencjalny inwestor lub decydent, może prowadzić do niepełnych wniosków.

Z tym wpisem zaczynam nowy projekt. Uznałem, że moim kolejnym krokiem będzie projekt dotyczący analizy sentymentów. Czym jest analiza sentymentów w kontekście analizy finansowej i Machine Learning'u?

Sentyment, w tym omawianym przez nas kontekście, to manifestowanie określonego nastawienia względem danej rzeczy, zdarzenia, zjawiska lub osoby; sentymentowi jasno możemy przypisać kategorie, np. pozytywny, neutralny, negatywny. Gdzie te wcześniej wyrażane sentymenty i następnie kwalifikowane, to po prostu teksty w języku naturalnym, na przykład komentarze na Facebook'u. 

Analiza sentymentów, to algorytmiczna kwalifikacja, bardzo często wsparta przez sztuczną inteligencję, polegająca na zakwalifikowaniu danej wypowiedzi do danej kategorii odzwierciedlającej jeden z sentymentów.

Ta powyższa definicja nie jest oczywiście ekspercką definicją, są to moje własne słowa i uważam, że w prosty sposób przekazują to co najważniejsze. Gdy ktoś jest zainteresowany tematem, to zawsze wskazane jest samodzielne szukanie i porównywanie ze sobą definicji i wyjaśnień różnych zagadnień, w tym jak i w każdym innym przypadku poruszanych przeze mnie tematów.

Tak jak to było w poprzedniej pracy na tym blogu i tak jak to zwykle wygląda w programowaniu, aby wykonać odpowiednią pracę, należy przywołać odpowiednie moduły, do których zasobów będziemy sięgać. 

Przed pokazaniem pierwszego kodu. Wspomnę tylko, że pracuję w Jupyterze używając Pythona.

import gensim
from gensim.parsing.preprocessing import STOPWORDS
from gensim.utils import simple_preprocess
import matplotlib.pyplot as plt
import nltk
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer, WordNetLemmatizer
from nltk.tokenize import word_tokenize, sent_tokenize
import numpy as np
import pandas as pd
import plotly.express as px
import re
import seaborn as sns
from wordcloud import WordCloud, STOPWORDS

Niektóre linijki w powyższym kodzie pominę, zrobię to w sytuacji, gdy już omawiałem jakąś bibliotekę/funkcję lub jest ona popularna i nie mam zamiaru jej opisywać w tej chwili.

"gensim" - dzięki temu popracujemy bezpośrednio na tekście...

"STOPWORDS" importowany z "gensim" - będzie filtrował niepotrzebne słowa. "Niepotrzebne", w kontekście tego co robimy, to słowa bez znaczenia semantycznego jak np. spójniki albo zaimki. Jak się domyślacie, przy badaniu sentymentów istotne będą słowa nacechowane emocjonalnie, albo specyficznie zwroty, które mają pozytywny lub negatywny przekaz.

"simple_preprocess" - jak sama nazwa wskazuje, dokonuje prostego i wstępnego przetworzenia tekstu. Wchodzi w to normalizacja i tokenizacja. Domyślnie ta funkcja jest dedykowana do pracy z językiem angielskim, natomiast żeby nie komplikować w tej chwili nie będę podawał sposobu na ominięcie problemu braku wrażliwości na polskie znaki diakrytyczne i podam już przetworzony tekst: ['simple_preprocess', 'jak', 'sama', 'nazwa', 'wskazuje', 'dokonuje', 'prostego', 'wstepnego', 'przetworzenia', 'tekstu', 'wchodzi', 'to', 'normalizacja', 'tokenizacja'].

"nltk" - rozwinięciem tego skrótu będzie Natural Language Toolkit; myślę, że to jest dość sugestywne i tak to zostawię :-)

"PorterStemmer" - pozwala na redukcję słów, aby ułatwiać analizę tekstu. Przykładu w języku angielskim: słowo "jumping" zostanie zredukowane do "jump", "easily" do "easili", "wolves" do "wolv." Jak widzimy te redukcje zmieniają te słowa, nie jest to perfekcyjne narzędzie, istnieją bardziej wyrafinowane narzędzia. Również trzeba wziąć pod uwagę, że jeśli będziecie kiedyś analizować polski tekst, to odpowiednią biblioteką będzie wykorzystanie klasy "PolishStemmer" (część modułu NLTK: "nltk.stem.snowball.PolishStemmer"). Ja w tym projekcie użyję jakichś dostępnych w internecie anglojęzycznych danych, do których łatwiej będzie mi się dostać.

"WordNetLemmatizer" - narzędzie służące do lematyzacji słów, czyli do sprowadzania ich do podstawowych form. Korzyść jaka z tego płynie to redukcja "szumu" wynikającego z różnych odmian danych słów i pozostawienie tylko ich podstawowego znaczenie. Przykłady: lematem słowa "byłoby" jest "być", a "biegliśmy" jest "bieg." W zwykłej międzyludzkiej komunikacji oczywiście rozszerzenie o właściwe formy jest bardziej estetyczne i zrozumiałe i przede wszystkim poprawne gramatycznie, natomiast w kontekście machine learningu, ta redukcja i uściślanie jest korzyścią, a nie ujmą.

"WordCloud" - dla odmiany, zamiast słów, przekażę obrazek z moich własnych zasobów prezentujący word cloud :-) To akurat artystyczny przykład, który interpretuję jako metaforyczne przedstawienie złożoności dziedziny jaką jest analiza danych. Najczęściej word cloud, to zestaw słów kluczowych (lub zwrotów) tworzących jedną przyjemną dla oka mozaikę. W analizie danych, to jeden ze sposobów na z wizualizowanie danych.

Na sam koniec tego wpisu dodam, że jeśli nie posiadacie któregoś z modułów, to z łatwością możecie go zainstalować w Jupyter przy użyciu np. "!pip install nltk". Istotne jest, to aby dodać "!" (wykrzyknik) na samym początku. Ta sama zasada obowiązuje dla innych pip install. Z tą różnicą, że jeśli używałbym tych komend, na przykład w PowerShell'u to robiłbym to bez wykrzyknika ("!"), a chcąc zrobić to samo w Jupyter muszę dodać "!" na początku.

niedziela, 28 stycznia 2024

Analiza SMA - wykres SMA

Ten post jest kontynuacją: https://kamilkondrat.blogspot.com/2024/01/analiza-sma-i-definiowanie-istotnej.html

Ta praca odzwierciedla zainteresowanie autora analizą finansową i wykorzystaniem narzędzi informatycznych w jej realizacji. Niniejszy blog nie ma na celu dostarczania porad finansowych, lecz stanowi konstruktywne podejście autora do dzielenia się wiedzą. Autor widzi w tym sposobność do rozwijania swoich umiejętności i zachęcania do wzajemnej dyskusji. Należy pamiętać, że wnioski wynikające z analiz prezentowanych na tym blogu nie powinny być bezpośrednio stosowane do podejmowania decyzji finansowych. Nawet jeśli analizy są dokładne, stanowią one tylko fragment potrzebnych informacji do podjęcia w pełni świadomej decyzji. Brak szerszego kontekstu, w którym znajduje się potencjalny inwestor lub decydent, może prowadzić do niepełnych wniosków.

W ostatnim wpisie zdefiniowaliśmy funkcję ("buy_sell_signal") wykonującą dla nas najważniejszą pracę jeśli chodzi o odnalezienie sygnałów kupna i sprzedaży przy użyciu SMA. 

Funkcja ta zwraca nam obiekt typu Series, który zawiera w sobie dwie listy "buy_signal" oraz "sell_signal." 

Dopiszemy sobie zawartość tych dwóch list do dwóch nowo tworzonych przez nas kolumn ("Buy Price Signal" i "Sell Price Signal"). Zrobimy to dzięki poniższej linijce kodu:

df['Buy Price Signal'], df['Sell Price Signal'] = buy_sell_signal(df)

Kolejnym krokiem będzie napisanie kodu potrzebnego do wizualizacji  danych:

fig, ax = plt.subplots(figsize=(15, 7))
ax.plot(df['Adj Close'], label='Ceny zamknięcia (Adj Close)', linewidth=1, 
	color='silver', alpha=1)
ax.plot(df['SMA_30'], label='SMA 30', alpha=1, color='violet')
ax.plot(df['SMA_120'], label='SMA 120', alpha=1, color='green')
ax.scatter(df.index, df['Buy Price Signal'], label='Kup', marker='^', 
	color='blue', alpha=1)
ax.scatter(df.index, df['Sell Price Signal'], label='Sprzedaj', marker='v', 
	color='red', alpha=1)
ax.set_title('Apple Inc.:' + " Historia cen z sygnałami kupna i sprzedaży", 
	fontsize=20)
ax.set_ylabel('Cena', fontsize=20)
legend = ax.legend()
ax.grid()
plt.show()

Jeśli chodzi o powyższy kod, to nie będę go ze szczegółami wyjaśniał. Można samemu wydedukować jaki element kodu czemu służy przez manipulowanie parametrami i obserwowanie zachodzących zmian, można też zajrzeć do Internetu. U mnie wygląda to tak:

Ceny akcji są w kolorze srebrnym, to kolor kojarzący mi się z Apple. Na wykresie znajdują się tylko dwa sygnały kupna i jeden sygnał sprzedaży. Ogólna tendencja cen akcji raczej rosnąca, jednak byłaby ona znacznie bardziej zauważalna, ta tendencja, gdybym wziął pod uwagę dane z większej ilości czasu niż rok, również ilość sygnałów kupna i sprzedaży byłaby większa. 

Wiem, bo takie dane też przetworzyłem przez skrypt, natomiast, uwierzcie mi :-) linie i oznaczenia sygnałów mogłyby się "zlewać" ze sobą i wymagać większego skupienia, aby odczytać wykres. Mnie jednak zależało, żeby przykład był bardziej demonstracyjny i na pierwszy rzut oka można by było rozróżnić wszystkie krzywe i z łatwością zauważyć sygnały kupna/sprzedaży.

To na razie wszystko, jeśli chodzi o SMA, przynajmniej na ten moment. Tym trzecim wpisem zamykam tę mini serię i otwieram ten blog: Hello World!

Kolejne wpisy będę dotyczyły już bardziej złożonych projektów, które mam w planach.

sobota, 27 stycznia 2024

Analiza SMA - definiowanie istotnej funkcji

W tym wpisie będziemy kontynuować analizę SMA: https://kamilkondrat.blogspot.com/2024/01/wprowadzenie-do-analizy-sma-przy-uzyciu.html

Ta praca odzwierciedla zainteresowanie autora analizą finansową i wykorzystaniem narzędzi informatycznych w jej realizacji. Niniejszy blog nie ma na celu dostarczania porad finansowych, lecz stanowi konstruktywne podejście autora do dzielenia się wiedzą. Autor widzi w tym sposobność do rozwijania swoich umiejętności i zachęcania do wzajemnej dyskusji. Należy pamiętać, że wnioski wynikające z analiz prezentowanych na tym blogu nie powinny być bezpośrednio stosowane do podejmowania decyzji finansowych. Nawet jeśli analizy są dokładne, stanowią one tylko fragment potrzebnych informacji do podjęcia w pełni świadomej decyzji. Brak szerszego kontekstu, w którym znajduje się potencjalny inwestor lub decydent, może prowadzić do niepełnych wniosków.

Po zaczytaniu odpowiednich bibliotek czas na właściwą pracę. Zaczniemy od pobrania danych finansowych i zdefiniowania odpowiednich struktur danych, którą pozwolą nam ruszyć z miejsca.

df = yf.download('AAPL', period = '1y')

df['SMA_30'] = ta.sma(df['Close'],30)
df['SMA_120'] = ta.sma(df['Close'],120)

"df" - definiujemy zmienną "df" inspirując się DataFrame, gdyż funkcja "download" zwraca obiekt tej właśnie klasy, czyli DataFrame. Będzie to przechowalnia naszych danych finansowych.

"AAPL" - to symboliczny zapis firmy Apple. Akurat kilka dni temu miałem okazje poznać i rozmawiać z osobą tam pracującą. :-) Stąd też postanowiłem, że przeprowadzę tę demonstrację z użyciem danych tej spółki.

"period = '1y'" - pobrane dane będą danymi zaczynając od tych (dostępnych) najnowszych kończąc na tych do 1 roku w tył. Dane z jednego roku nam wystarczą do policzenia SMA z 120 dni i mniej.

Następnie dodajemy dwie nowe kolumny do naszej ramki (tabeli) danych i jest to SMA_30 i SMA_120. Do policzenia SMA dla interesującego nas okresu 30 i 120 dni wykorzystujemy funkcję "sma()" z biblioteki "pandas_ta" oraz wartości z kolumny "Close" zawartej w naszych danych.

Ceny zamknięcia ("Close") są wartościami ostatniej transakcji kupna/sprzedaży danego aktywa w danym dniu handlowym.

Aby zapoznać się z matematycznym sposobem obliczania SMA można wejść tutaj: https://rankia.pl/analizy-gieldowe/prosta-wykladnicza-i-wazona-srednia-kroczaca-wzory-i-przyklady/ Ja nie będę rozwodził się nad matematycznymi wzorami, te obliczenia robi za nas komputer, a my mu "zlecamy" to przy pomocy Pythona.

Na moim urządzeniu pobranie danych przebiegło pomyślnie stąd też ten czerwony paseczek informujący o zakończeniu procesu. 

Po rozszerzeniu struktury danych o dwie kolumny dane prezentują się następująco:














Można zauważyć, że powyższe dane są z agregowane według daty.

Czas na esencję naszego skryptu. Funkcja "buy_sell_signal" porównuje ze sobą SMA oraz dopisuje sygnały o kupnie i sprzedaży:

def buy_sell_signal(df):
    
    buy_signal = []
    sell_signal = []
    position = False
    
    for i in range(len(df)):
        if df['SMA_30'][i] > df['SMA_120'][i]:
            if position == False:
                buy_signal.append(df['Adj Close'][i])
                sell_signal.append(np.nan)
                position = True
            else: 
                buy_signal.append(np.nan)
                sell_signal.append(np.nan)
        elif df['SMA_30'][i] < df['SMA_120'][i]:
            if position == True:
                buy_signal.append(np.nan)
                sell_signal.append(df['Adj Close'][i])
                position = False
            else:
                buy_signal.append(np.nan)
                sell_signal.append(np.nan)
        else:        
            buy_signal.append(np.nan)
            sell_signal.append(np.nan)            
                        
    return pd.Series([buy_signal, sell_signal])

"buy_signal" oraz "sell_signal" będą listami zbierającymi interesujące nas wartości.

"position" informuje nas o tym czy inwestor posiada aktywa czy nie. "True" będzie informować nas o tym, że inwestor, mówiąc kolokwialnie, "jest w grze." "False" oznacza, że inwestor jest "poza grą" i nie posiada aktywów.

Dzięki pętli "for" i funkcji "range" przechodzimy przez wszystkie wiersze w naszych danych. 

"[i]" - zawiera numer aktualnie porównywanych wartości dla danego wiersza. Porównywane są między sobą wartości z kolumn "SMA_30" i "SMA_120." Każdy wiersz z kolei to inny dzień i inny wyliczony na ten dzień współczynnik SMA. SMA przed tymi porównaniami było wyliczane w ten sposób, że każdy dzień był punktem zaczepienia i od tego dnia program cofał się 30 lub 120 dni licząc przy tym SMA i zrobiliśmy to dzięki funkcji "sma()" podanej na początku tego wpisu.

Kiedy krótkoterminowa SMA będzie większa niż długoterminowa, wtedy uznajemy to za sygnał kupna i dodajemy wartość z kolumny "Adj Close" przy użyciu funkcji "append()" do listy którą stworzyliśmy na początku: "buy_signal."

Kiedy krótkoterminowa SMA będzie mniejsza od długoterminowej, wtedy uznajemy ją za sygnał sprzedaży i dodajemy wartość z kolumny "Adj Close" przy użyciu "append()" do listy "sell_signal."

Ktoś z Was mógłby zapytać: "A dlaczego nie dodajemy do listy wartości z kolumny 'Close', która posłużyła nam do wyliczania SMA, a zamiast tego dodajemy wartości z kolumny 'Adj Close'?"

Bierze się to z tego, że do wyliczenia SMA jako tako potrzebujemy cen zamknięcia, czyli "Close", to jest istotą tego współczynnika. Natomiast idąc dalej kiedy zostawiamy same obliczenia SMA, która skupiają się wokół cen zamknięcia i pozwalają nam wstępnie wyłonić sygnały kupna i sprzedaży dochodzimy do punktu, w którym możemy analizować rezultat tego, czyli skupić się na sygnałach kupna i sprzedaży. Kiedy mamy już wyłonione wiersze i wartości, które przeszły już analizę SMA możemy zwrócić się do danych z kolumny "Adj Close", które będą bardziej stosowne, w późniejszych etapach podejmowania decyzji finansowej, niż "Close", gdyż będą uwzględniały dywidendy i podziały akcji.

Kiedy nie wyodrębniamy żadnego sygnału kupna lub sprzedaży polecamy komputerowi uzupełniać puste rekordy "wartościami" NaN (Not a Number) przy użyciu "np.nan", który jest obiektem tzw. specjalnym.

W ostateczności funkcja "buy_sell_signal" zwraca obiekt serii dzięki funkcji "Series()", który w naszym kodzie zawiera w sobie dwie listy, listę sygnałów kupna i listę sygnałów sprzedaży.

Cdn.

piątek, 26 stycznia 2024

Analiza SMA - Wprowadzenie

Ta praca odzwierciedla zainteresowanie autora analizą finansową i wykorzystaniem narzędzi informatycznych w jej realizacji. Niniejszy blog nie ma na celu dostarczania porad finansowych, lecz stanowi konstruktywne podejście autora do dzielenia się wiedzą. Autor widzi w tym sposobność do rozwijania swoich umiejętności i zachęcania do wzajemnej dyskusji. Należy pamiętać, że wnioski wynikające z analiz prezentowanych na tym blogu nie powinny być bezpośrednio stosowane do podejmowania decyzji finansowych. Nawet jeśli analizy są dokładne, stanowią one tylko fragment potrzebnych informacji do podjęcia w pełni świadomej decyzji. Brak szerszego kontekstu, w którym znajduje się potencjalny inwestor lub decydent, może prowadzić do niepełnych wniosków.

Zaczniemy od poznawania analizy danych finansowych korzystając z techniki SMA (Simple Moving Average) - w języku polskim istnieją dwa tłumaczenia dla "SMA" i jest to "Prosta Średnia Ruchoma" lub "Prosta Średnia Krocząca." Dla nas będzie to po prostu SMA, jednak uważam, że warto znać też polskie nazewnictwo, aby zawsze lepiej się orientować i nie być zaskoczonym jeśli usłyszy się chociażby o średniej kroczącej.

Najpierw dobrze by było powiedzieć czym jest szereg czasowy, gdyż jest on istotny z punktu widzenia SMA. Szereg czasowy, najprościej mówiąc, są to regularnie prowadzone obserwacje w czasie.

SMA została opracowana po to, aby wskazywać uwypuklające się trendy przez wygładzanie krzywej i eliminowanie drobnych zawirowań, na rzecz zaobserwowania długoterminowych tendencji. Możliwe jest to dzięki uporządkowaniu odczytów z danych finansowych, które zostały wykonane w określonym czasie (szeregi czasowe).

Aby skorzystać z SMA, w podejmowaniu decyzji finansowych można podjąć się krzyżowaniu SMA krótkiej i długoterminowej. W naszym przykładzie użyjemy 30 dniową krótką i 120 dniową długą. Będziemy je ze sobą krzyżować, co w praktyce oznacza porównywanie ich ze sobą pod względem wartości.

SMA krótkoterminowa > SMA długoterminowa

Jeśli SMA krótkoterminowa będzie wyższa od SMA długoterminowej to mamy do czynienia z sytuacją w której ostatnie zmiany cen były na tyle pozytywne, że sugerują one tendencję wzrostową. Taką sytuację nazywa się też "złotym krzyżem" (golden cross) i często interpretuje jako sygnał do zakupu. Innymi słowy, kupujemy gdyż spodziewamy się że akcje zaczną drożeć, dzięki czemu w przyszłości zarobimy.

SMA krótkoterminowa < SMA długoterminowa

Jeśli SMA krótkoterminowa będzie niższa od SMA długoterminowej wtedy mamy do czynienia z przeciwną sytuacją, nazywa się "krzyżem śmierci" (death cross) i często interpretuje jako sygnał do sprzedaży. Spodziewamy się, że ceny aktywów zaczną spadać, a my na tym stracimy, więc musimy się ich pozbyć.

Wnioski, które możemy wyciągnąć z zastosowania SMA oczywiście są tylko ułamkiem wiedzy potrzebnej do podjęcia odpowiedzialnej decyzji inwestycyjnej. Ten wpis nie jest poradą inwestycyjną, a przybliżeniem wiedzy technicznej potrzebnej do analizy technicznej do podejmowania świadomych decyzji w świecie finansów.

To jest ten czas, aby przejść do Python'a i Jupiter'a. Zakładam, że posiadacie podstawową wiedzę na temat programowania w Pythonie oraz macie jakieś pojęcie o tym czym jest Jupiter.

import pandas as pd
import numpy as np
import yfinance as yf
import pandas_ta as ta
import matplotlib.pyplot as plt

Zaczynamy od z importowania potrzebnych nam bibliotek:

"pandas" - dzięki niej będziemy mogli manipulować naszymi danymi.

"numpy" - dzięki numpy wykonamy odpowiednie obliczenia.

"yfinance" - pozwoli nam pobrać dane finansowe dostępne na Yahoo.

"pandas_ta" - jest rozszerzeniem pandas potrzebnym do zastosowania funkcji z obszaru analizy technicznej, to dzięki jej zasobom obliczymy m.in. tytułowe SMA.

"matplotlib.pyplot" - w przyszłości posłuży nam do wyświetlenia interesujących nas wykresów.

Analiza sentymentów - wpisy na mediach społecznościowych (podział danych)

Ten wpis zaczniemy od stworzenia DataFrame z danymi treningowymi train_df = pd.read_csv('train.csv', encoding='ISO-8859-1')....