28 maj 2017

Automat do pobierania kursów funduszy - 12 tydzień konkursu Daj Się Poznać

Witajcie! W tym tygodniu znowu publikuję tylko jednego posta i to dopiero w niedzielę. Na szczęście tym razem mogę napisać, że powstało sporo nowego kodu. Udało mi się dokończyć i uruchomić automatyczne pobieranie kursów funduszy Nationale-Nederlanden oraz ich zapis w bazie - oczywiście za pośrednictwem API. Zatem dziś krótko o tym rozwiązaniu.

Zalążek projektu DataDownloader powstał już ponad miesiąc temu, bo jeszcze w 7. tygodniu konkursu (zobacz wpis na blogu na ten temat). Wtedy udało się przy jego pomocy pobrać plik JSON z danymi dla przykładowego funduszu i zapisać go na dysku. W tym tygodniu dopisałem:
  1. parsowanie tego pliku do kolekcji obiektów klasy Rate. Pojedynczy obiekt tej klasy reprezentuje wartość funduszu w konkretnej dacie. Docelowo wszystkie wyceny funduszy będą parsowane do tego właśnie formatu, co pozwoli na użycie wspólnego zapisu do API dla wszystkich pobranych danych. Do parsowania JSON użyłem jedynej słusznej paczki Newtonsoft.Json
  2. wysłanie tak przygotowanych danych do API przy użyciu POST. Poza samym kursem funduszu wysyłany jest również sekretny token użytkownika z prawami administratora - inaczej każdy mógłby utworzyć nowe wartości kursu. Do obsługi RESTowych API w C# zawsze używam paczki RestSharp i tak samo zrobiłem również tutaj.
  3. obsługę wysłanego żądania po stronie API, czyli sprawdzenie czy token jest prawidłowy i ewentualne zapisanie pozostałych danych w bazie. Do zapisu do bazy musiałem przeiterować kolekcję par data wartość i utworzyć z nich zapytanie SQL. Następnie takie zapytanie jest wykonane przez bazę danych i API zwraca kod 201 z lokalizacją nowo zapisanych zasobów.
W trakcie prac pojawiły się 2 problemy z którymi musiałem sobie poradzić i o których chciałbym napisać coś więcej.

Pierwszy dziwny problem dotyczył dat w wygenerowanym przez RestSharp JSONie. Zserializowane daty były o godzinę lub dwie wcześniej niż oryginalna - np. data 28.05.2017 00:00:00 stawała się 27.05.2017 23:00:00. Podejrzewam, że chodzi o przesunięcie względem czasu UTC, ale nie mogłem znaleźć jak zmusić standardowy serializer w RestSharp do serializacji 1:1. Pomogła zmiana serializera na jedyny słuszny, czyli ponownie Newtonsoft.Json (konkretnie użyłem paczki RestSharp.Newtonsoft.Json)

Drugim problem było to, jak nie powielać już istniejących wartości kursów w bazie danych. Rozwiązaniem idealnym byłoby pobieranie ostatniej daty dla której mam wartość funduszu i wysyłanie do API jedynie późniejszych wartości. Na razie jednak takie rozwiązanie nie wchodziło w grę, bo same fundusze dla których pobieram wartość kursu są wpisane na sztywno w kod programu. Kiedy fundusze będą pobierane z API to będę mógł zwrócić również taką datę. Na razie jednak API za każdym razem dostaje pełny zestaw kursów dla każdego z funduszy, a o to żeby nie było powtórek w tabeli dba sama baza. Dzieje się tak dzięki indeksowi UNIQUE na kolumnach z datą i id funduszu (czyli dla każdego funduszu może być tylko jedna wartość w każdej z dat). Ostatnim i najważniejszym elementem tego rozwiązania jest samo zapytanie. Zamiast normalnego INSERT (które nie wykonuje się jeśli choć jeden z elementów zaburza unikalność) użyłem INSERT IGNORE - dzięki temu do bazy zostają dodane tylko takie elementy, których jeszcze nie ma w tabeli Rate.

Podsumowując prace nad tą częścią projektu wygląda na to, że automatyczne pobieranie kursów będzie największym i najbardziej skomplikowanym jego elementem. Moim zdaniem to dziwne, że tego typu dane nie są dostępne w postaci jakiegoś otwartego API dostarczonego przez każde TFI działające na rynku. W końcu są to dane publiczne, więc może kiedyś doczekam się prawa, które nakaże ich udostępnienie w postaci łatwej programistycznie. A jeśli nie to może sam takie zbuduję jak już będę miał parsery dla kilku towarzystw funduszy inwestycyjnych :)

0 komentarze:

Prześlij komentarz