Celem projektu jest refaktoryzacja oraz rozbudowa warstwy logicznej symulatora giełdowego. Głównym wyzwaniem jest implementacja mechanizmu rozliczania transakcji metodą FIFO (First-In, First-Out), zapewnienie optymalnych struktur danych dla portfela oraz wdrożenie modułu persystencji danych. Projekt kładzie nacisk na ręczne zarządzanie kolekcjami, algorytmikę finansową oraz rygorystyczną obsługę błędów.
Ze względu na wymogi audytowalności kodu i wydajności, projekt musi spełniać poniższe restrykcje: Zakaz użycia Stream API: Wszelkie operacje na kolekcjach (iteracja, filtrowanie, transformacja, sumowanie) muszą być realizowane przy użyciu klasycznych pętli (for, while) oraz instrukcji warunkowych.
Model danych: Zabrania się używania Java Records. Należy stosować klasyczne klasy (POJO) z prywatnymi polami i publicznymi metodami dostępowymi.
Typizacja:
Stałe wartości domenowe (typy aktywów, rodzaje transakcji) muszą być reprezentowane przez Enum.
Stosowanie literałów łańcuchowych (String) do sterowania logiką jest zabronione.
Biblioteki:
Projekt nie może korzystać z zewnętrznych frameworków (poza JUnit 5 do testów).
System nie może przechowywać stanu posiadania jako prostej pary ilość i średnia cena. Należy zaimplementować model oparty na Partiach Zakupowych. Każde aktywo w portfelu posiada historię transakcji kupna (Listę Partii).
Pojedyncza partia zawiera: datę zakupu, cenę jednostkową oraz liczbę sztuk.
System musi obsługiwać różne klasy aktywów (Akcje, Surowce, Waluty) w sposób polimorficzny – każdy typ posiada odrębną logikę wyliczania wartości (np. uwzględnianie spreadu dla walut).
Należy dobrać kolekcje zapewniające optymalną złożoność obliczeniową:
Wyszukanie aktywa po identyfikatorze (ticker) musi odbywać się w czasie stałym O(1).
Lista obserwowanych instrumentów ("Watchlist") musi gwarantować brak duplikatów na poziomie struktury danych.
Zlecenia kupna/sprzedaży oczekujące na realizację muszą być przechowywane w strukturze, która automatycznie priorytetyzuje je na podstawie ich atrakcyjności cenowej względem rynku.
Priorytetyzacja Zleceń Kontekst: Cena rynkowa akcji: 110 PLN. Złożone zlecenia (Kupno z Limitem):
Zlecenie A: Limit 100 PLN.
Zlecenie B: Limit 105 PLN.
Wymagane zachowanie: Zlecenie B jest "bliżej rynku" (bardziej atrakcyjne). W strukturze danych (np. PriorityQueue), Zlecenie B musi znajdować się na szczycie (HEAD), niezależnie od czasu wpłynięcia.
Kluczowy element systemu. Przy sprzedaży aktywów należy redukować stan posiadania, zaczynając od najstarszej partii zakupu.
Algorytm musi obsługiwać sprzedaż częściową (zmniejszenie liczby sztuk w partii).
Algorytm musi obsługiwać sprzedaż obejmującą wiele partii (iteracyjne "zamykanie" najstarszych partii aż do pokrycia wolumenu zlecenia).
System musi zwracać precyzyjny raport zysku/straty dla każdej transakcji sprzedaży.
Logika FIFO Stan początkowy portfela dla aktywa "XYZ":
Partia A (Data: 2023-01-01): 10 sztuk, cena zakupu: 100 PLN.
Partia B (Data: 2023-02-01): 10 sztuk, cena zakupu: 120 PLN.
Operacja: Sprzedaż 15 sztuk po cenie 150 PLN.
System zużywa całe 10 sztuk z Partii A. Zysk: 10×(150−100)=500 PLN.
System zużywa 5 sztuk z Partii B. Zysk: 5×(150−120)=150 PLN. (W Partii B zostaje 5 sztuk).
Wynik: Całkowity Zysk (P&L): 650 PLN.
Stan portfela (gotówka, posiadane aktywa, szczegółowa historia partii zakupowych) musi być zapisywany do pliku i odtwarzany przy starcie.
Należy zaprojektować autorski format pliku tekstowego (np. separatorowy).
Moduł wczytujący musi walidować spójność danych.
Wymagana obsługa błędów wejścia/wyjścia oraz bezpieczne zamykanie zasobów (try-with-resources).
Scenariusz Referencyjny: Format Pliku i Walidacja Przykładowa struktura pliku:
HEADER|CASH|10500.50
ASSET|SHARE|AAPL
LOT|2023-05-10|10|150.00
LOT|2023-06-12|5|155.00
Jeżeli suma ilości w liniach LOT (tutaj: 15) nie zgadza się ze stanem zadeklarowanym w nagłówku aktywa lub napotkano błąd formatowania, proces musi zostać przerwany wyjątkiem DataIntegrityException.
Należy zaimplementować generator raportów tekstowych.
Wymagane jest sortowanie aktywów według złożonego klucza:
Typ Aktywa -> Wartość Rynkowa (malejąco).
Ze względu na zakaz użycia Stream API, należy zaimplementować własny Comparator.
Kod musi zostać pokryty testami jednostkowymi. Autor rozwiązania musi samodzielnie zidentyfikować warunki brzegowe.
Testy muszą potwierdzać:
Poprawność obliczeń FIFO przy sprzedaży rozbitej na wiele partii.
Poprawność działania kolejki priorytetowej.
Odporność parsera plików na uszkodzone dane.
Prawidłowe rzucanie wyjątków biznesowych (np. InsufficientFundsException).
| Kryterium | Punkty | Opis |
|---|---|---|
| Ograniczenia techniczne | 4 | |
| Zakaz Stream API | 1 | Obliczenia powinny zostać wykonane za pomocą klasycznych pętli i instrukcji warunkowych |
| Zakaz Java Records | 1 | klasyczne klasy (POJO) z prywatnymi polami i publicznymi metodami dostępowymi |
| Typizacja | 1 | Stałe wartości domenowe muszą być reprezentowane przez Enum. |
| Biblioteki | 1 | Projekt nie może korzystać z zewnętrznych frameworków |
| Wymagania funkcjonalne | 5 | |
| Partie Zakupowe | 1 | aktywa jako historia zakupów, polimorfizm |
| Struktury danych | 1 | odczyt w czasie stałym, unikalność danych, kolejki zleceń |
| Kolejka FIFO | 1 | poprawna kolejność sprzedaży, sprzedaż częściowa, sprzedaż wielu partii |
| Raporty sprzedaży | 1 | Jasne informacje na temat tego jakie akcje zostały sprzedane |
| Persystencja | 1 | przechowywanie danych, generowanie raportów |
| Weryfikacja | 6 | |
| Testy kolejki FIFO | 1 | poprawność obliczeń przy sprzedaży rozbitej na wiele partii |
| Testy kolejki priorytetowej | 1 | poprawne wyznaczanie priorytetów zleceń |
| Testy parsera | 1 | walidacja plików, poprawny odczyt/zapis |
| Testy wyjątków | 1 | Wszystkie wyjątki powinny zostać sprawdzone |
| Testy warunków brzegowych | 1 | identyfikacja i sprawdzenie warunków brzegowych |
| Pełne pokrycie testami | 1 | każda operacja powinna mieć testy do wszyskich przypadków |
| Razem | 15 |